CoreWCF客户端断开后仍写入流致服务器高CPU占用问题
CoreWCF流式服务CPU占用过高问题解决方法
问题根源
你的RandomStream实现了一个无限流:每次Read调用都会返回请求的全部字节数,没有终止条件。当客户端仅读取4字节就断开连接后,CoreWCF运行时仍会持续尝试从这个无限流中读取数据并发送,导致服务器线程一直处于高负载状态,CPU占用飙升。
解决方案
方案1:让流响应客户端断开信号
通过注入请求取消令牌,在客户端断开时终止流的读取操作,避免服务器无限生成数据。
1.1 修改RandomStream,支持取消令牌
namespace RandomNumberCore; public class CancellableRandomStream : Stream { private readonly Random _random; private int _sequence; private readonly CancellationToken _cancellationToken; private bool _isDisposed; public CancellableRandomStream(Random random, CancellationToken cancellationToken) { _random = random; _cancellationToken = cancellationToken; // 客户端断开时自动释放流 _cancellationToken.Register(() => Dispose()); } public override bool CanRead => !_isDisposed; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length => throw new NotSupportedException(); public override long Position { get => _sequence; set => throw new NotSupportedException(); } public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) { if (_isDisposed || _cancellationToken.IsCancellationRequested) return 0; // 返回0表示流结束 var internalBuffer = new Span<byte>(buffer, offset, count); _random.NextBytes(internalBuffer); _sequence += count; return count; } public override int Read(Span<byte> buffer) { if (_isDisposed || _cancellationToken.IsCancellationRequested) return 0; _random.NextBytes(buffer); _sequence += buffer.Length; return buffer.Length; } protected override void Dispose(bool disposing) { if (!_isDisposed) { _isDisposed = true; base.Dispose(disposing); } } public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotSupportedException(); public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); }
1.2 修改服务实现,注入取消令牌
namespace RandomNumberCore; [ServiceContract] public interface IStreamingService { [OperationContract] Stream GetRandomStream(); } public class StreamingService : IStreamingService { private readonly IHttpContextAccessor _httpContextAccessor; // 注入HttpContextAccessor获取请求取消令牌 public StreamingService(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public Stream GetRandomStream() { var cancellationToken = _httpContextAccessor.HttpContext?.RequestAborted ?? CancellationToken.None; return new CancellableRandomStream(Random.Shared, cancellationToken); } }
1.3 注册依赖服务
在服务启动代码中添加IHttpContextAccessor的注册:
var builder = WebApplication.CreateBuilder(); builder.Services.AddHttpContextAccessor(); // 添加这一行 builder.Services.AddServiceModelServices(); builder.Services.AddServiceModelMetadata(); // ...其余代码保持不变
方案2:限制流的输出长度
如果业务不需要无限流,可以直接限制流的最大输出长度,避免服务器持续生成数据。
修改RandomStream:
namespace RandomNumberCore; public class RandomStream : Stream { private readonly Random _random; private int _sequence; private readonly long _maxLength; private bool _isDisposed; // 默认输出1MB随机数据 public RandomStream(Random random, long maxLength = 1024 * 1024) { _random = random; _maxLength = maxLength; } public override bool CanRead => !_isDisposed; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length => throw new NotSupportedException(); public override long Position { get => _sequence; set => throw new NotSupportedException(); } public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) { if (_isDisposed) return 0; var remaining = (int)(_maxLength - _sequence); if (remaining <= 0) return 0; var readCount = Math.Min(count, remaining); var internalBuffer = new Span<byte>(buffer, offset, readCount); _random.NextBytes(internalBuffer); _sequence += readCount; return readCount; } public override int Read(Span<byte> buffer) { if (_isDisposed) return 0; var remaining = (int)(_maxLength - _sequence); if (remaining <= 0) return 0; var readCount = Math.Min(buffer.Length, remaining); var slice = buffer.Slice(0, readCount); _random.NextBytes(slice); _sequence += readCount; return readCount; } protected override void Dispose(bool disposing) { if (!_isDisposed) { _isDisposed = true; base.Dispose(disposing); } } public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotSupportedException(); public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); }
效果说明
两种方案都能解决CPU占用过高的问题:
- 方案1会在客户端断开连接时立即终止流的读取,服务器停止生成数据
- 方案2会在输出指定长度的数据后自动结束流,即使客户端提前断开,流也不会无限运行
内容的提问来源于stack exchange,提问作者Eterm
相关产品推荐
相关产品推荐

