You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 06:31:04