c# 如何使用gRPC双向流处理高并发实时数据

来源:程序开发作者:南京GEO公司头衔:草根站长
导读:本期聚焦于小伙伴创作的《c# 如何使用gRPC双向流处理高并发实时数据》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《c# 如何使用gRPC双向流处理高并发实时数据》有用,将其分享出去将是对创作者最好的鼓励。

在实时数据类应用场景中,比如即时通讯、实时数据监控、多端状态同步等,系统往往需要同时处理大量客户端的并发请求,并且要求数据传输延迟低、吞吐量大。传统的HTTP请求响应模式每次交互都需要建立连接、等待响应,很难满足这类场景的需求。gRPC的双向流模式允许客户端和服务端在单个连接上同时发送多个消息,不需要等待对方响应,非常适合处理高并发实时数据场景,结合c#的高效开发能力,可以快速搭建稳定的实时数据服务。

c# 如何使用gRPC双向流处理高并发实时数据

gRPC双向流核心原理

gRPC基于HTTP/2协议实现,支持四种通信模式,其中双向流模式是客户端和服务端都可以在建立连接后,主动向对方发送多个消息,消息的发送和接收是异步的,不需要遵循请求-响应的顺序。这种模式可以减少连接建立的开销,单个连接就可以承载多个并发的数据流,非常适合高并发场景下的实时数据传输。

在双向流中,客户端调用服务方法后会得到一个流对象,既可以往流里写入消息,也可以从流里读取服务端返回的消息,两边的操作互不阻塞。服务端在接收到客户端的连接请求后,也会拿到对应的流对象,同样可以双向读写消息,实现全双工的实时通信。

c#中搭建gRPC双向流服务

1. 定义proto协议文件

首先需要定义proto文件,声明双向流的服务方法,这里以实时数据上报和推送场景为例,定义数据上报和实时通知的双向流接口。

syntax = "proto3";

option csharp_namespace = "GrpcRealTimeDemo";

package realtime;

// 定义实时数据消息
message RealTimeData {
  string client_id = 1;
  string data_content = 2;
  int64 timestamp = 3;
}

// 定义服务端推送的消息
message ServerPushMessage {
  string target_client_id = 1;
  string push_content = 2;
}

// 定义双向流服务
service RealTimeStreamService {
  // 双向流方法,客户端和服务端都可以发送多个消息
  rpc ProcessRealTimeData (stream RealTimeData) returns (stream ServerPushMessage);
}

2. 实现服务端逻辑

在c#项目中引用Grpc.AspNetCore包,然后实现proto中定义的服务接口,处理双向流的读写操作,同时加入简单的并发控制逻辑。

using Grpc.Core;
using GrpcRealTimeDemo;

namespace GrpcRealTimeDemo.Services;

public class RealTimeStreamServiceImpl : RealTimeStreamService.RealTimeStreamServiceBase
{
    // 控制并发处理的信号量,限制同时处理的数据流数量,避免资源耗尽
    private static readonly SemaphoreSlim _concurrencyLimiter = new SemaphoreSlim(100, 100);
    private readonly ILogger<RealTimeStreamServiceImpl> _logger;

    public RealTimeStreamServiceImpl(ILogger<RealTimeStreamServiceImpl> logger)
    {
        _logger = logger;
    }

    public override async Task ProcessRealTimeData(
        IAsyncStreamReader<RealTimeData> requestStream,
        IServerStreamWriter<ServerPushMessage> responseStream,
        ServerCallContext context)
    {
        // 等待获取并发处理许可
        await _concurrencyLimiter.WaitAsync(context.CancellationToken);
        try
        {
            // 启动两个异步任务,分别处理客户端发来的数据和向客户端推送数据
            var readClientDataTask = ReadClientDataAsync(requestStream, context);
            var pushServerDataTask = PushServerDataAsync(responseStream, context);

            // 等待两个任务完成,任意一个取消就整体取消
            await Task.WhenAny(readClientDataTask, pushServerDataTask);
        }
        finally
        {
            // 释放并发许可
            _concurrencyLimiter.Release();
        }
    }

    // 读取客户端发送的实时数据
    private async Task ReadClientDataAsync(IAsyncStreamReader<RealTimeData> requestStream, ServerCallContext context)
    {
        while (await requestStream.MoveNext(context.CancellationToken))
        {
            var clientData = requestStream.Current;
            _logger.LogInformation($"收到客户端{clientData.ClientId}的数据:{clientData.DataContent}");
            // 这里可以加入数据持久化、转发给其他客户端等业务逻辑
        }
    }

    // 向客户端推送服务端消息
    private async Task PushServerDataAsync(IServerStreamWriter<ServerPushMessage> responseStream, ServerCallContext context)
    {
        // 模拟定期向客户端推送消息,实际场景中可以结合消息队列、事件总线触发推送
        while (!context.CancellationToken.IsCancellationRequested)
        {
            await responseStream.WriteAsync(new ServerPushMessage
            {
                TargetClientId = "all",
                PushContent = $"服务端推送消息,时间:{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}"
            });
            await Task.Delay(1000, context.CancellationToken);
        }
    }
}

3. 客户端调用示例

客户端同样需要引用对应的proto生成的代码,然后建立连接,启动双向流的读写操作,实现实时数据的发送和接收。

using Grpc.Net.Client;
using GrpcRealTimeDemo;

namespace GrpcClientDemo;

class Program
{
    static async Task Main(string[] args)
    {
        // 创建gRPC客户端通道,地址根据实际服务端配置调整
        using var channel = GrpcChannel.ForAddress("http://127.0.0.1:5001");
        var client = new RealTimeStreamService.RealTimeStreamServiceClient(channel);

        // 调用双向流方法,获取流对象
        using var call = client.ProcessRealTimeData();

        // 启动异步任务接收服务端推送的消息
        var readServerPushTask = Task.Run(async () =>
        {
            while (await call.ResponseStream.MoveNext(CancellationToken.None))
            {
                var serverMessage = call.ResponseStream.Current;
                Console.WriteLine($"收到服务端推送:{serverMessage.PushContent}");
            }
        });

        // 模拟客户端持续发送实时数据
        var random = new Random();
        for (int i = 0; i < 10; i++)
        {
            await call.RequestStream.WriteAsync(new RealTimeData
            {
                ClientId = "client_001",
                DataContent = $"实时数据_{random.Next(1000)}",
                Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
            });
            await Task.Delay(500);
        }

        // 发送完数据后标记请求流结束
        await call.RequestStream.CompleteAsync();
        // 等待服务端推送任务完成
        await readServerPushTask;
    }
}

高并发场景优化方案

在处理高并发实时数据时,除了基础的双向流实现,还需要做对应的优化来提升服务的稳定性和吞吐量:

  • 使用信号量或者连接数限制,控制同时处理的双向流数量,避免大量连接同时建立导致服务端资源耗尽
  • 对于需要转发、持久化的数据,采用异步处理的方式,不要阻塞双向流的读写线程,避免影响实时性
  • 结合消息队列解耦数据生产和消费,服务端收到客户端数据后写入消息队列,由消费者异步处理,减少流处理的耗时
  • 合理设置HTTP/2的连接参数,比如调整帧大小、流控窗口,提升单个连接的传输效率
  • 对客户端连接做心跳检测,及时关闭无效连接,释放资源

注意事项

使用gRPC双向流处理高并发实时数据时,需要注意流的生命周期管理,避免流没有正确关闭导致内存泄漏。同时要做好异常处理,当客户端断开连接时,服务端的流操作要能正确捕获取消信号,释放相关资源。另外,双向流适合持续的数据交互场景,如果是单次请求的场景,使用普通的请求响应模式会更合适。

gRPC双向流C#高并发实时数据修改时间:2026-07-11 11:48:40

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。