gRPC服务端到客户端通知推送的实现方案咨询
核心结论
gRPC完全适配你说的跨系统双向通信、服务端主动推送交易通知的场景,你查到的靠锁/无限循环保活的变通方案是用错了gRPC通信模式导致的,不是gRPC本身的能力缺陷。
你当前调研的方案存在的明显问题
- 靠服务端锁、无限循环强行维持调用存活的实现额外开销高,断连清理、异常重试的逻辑很容易写漏,在交易系统这类对稳定性、消息准确性要求极高的场景下隐患很大
- 客户端为每个通知事件处理器单独开循环调用
ResponseStream.MoveNext()属于重复造轮子,既会造成不必要的线程占用,还容易出现消息乱序、漏收的问题
C# 生态下的标准实现方式
你要的双向通信能力直接用gRPC原生的双向流(Duplex Streaming) 模式即可,不需要做任何变通改造:
- 首先在proto文件里声明双向流RPC,语法非常简单:
service TradeTransmitService { // 入参、返回值都加stream关键字,就是原生双向流通道 rpc OpenChannel (stream ClientRequest) returns (stream ServerNotify); }
连接建立后,客户端、服务端都可以随时向通道写入消息,不需要等待对方发起请求,和你现在基于Socket实现的双向通信能力完全对齐。
- 服务端实现要点:
- 客户端调用
OpenChannel建立连接后,不需要写无限循环占住工作线程,只要把当前连接对应的IServerStreamWriter<ServerNotify>实例按客户端唯一标识存入连接池即可;同时绑定HttpContext.RequestAborted的取消令牌,连接断开时自动从连接池清理失效的流实例,不需要自己加锁维持调用存活,gRPC底层会自动管理长连接生命周期 - 当有交易事件、状态变更需要推送给指定客户端时,直接从连接池取出对应流实例,调用
WriteAsync()方法推送消息即可,gRPC内部已经做了线程安全处理,不需要额外加锁控制并发写入
- 客户端调用
- 客户端实现要点:
- 调用
OpenChannelAsync拿到双向流实例后,只需要开一个单例后台任务循环读取ResponseStream.MoveNext()即可,不需要为每个事件处理器单独开读循环;读到消息后,通过进程内的事件委托、本地消息分发器把消息丢给对应业务处理器处理就行,循环捕获到连接断开异常时自动触发重连逻辑 - 客户端需要向服务端发送请求时,直接往双向流的
RequestStream写入对应消息即可,和现有Socket的发送逻辑没有本质区别
- 调用
交易场景适配的注意事项
- 创建
GrpcChannel时记得配置合理的KeepAlive参数,设置KeepAliveInterval、KeepAliveTimeout值定期发送心跳包,避免中间交换机、防火墙因为连接空闲切断长连接,作用和你现在Socket层的心跳保活逻辑一致 - gRPC只保证传输层的消息可靠投递,如果业务要求强一致的通知送达,需要自己在业务层加消息序号、客户端ACK确认、离线消息补推逻辑,这部分和你自研Socket方案的可靠性保障逻辑是通用的
- 不要继续用客户端流+手动保活的非标准实现,双向流是gRPC原生支持的标准通信模式,官方库已经做了完整的流控、异常捕获、资源回收处理,稳定性远高于自定义的保活逻辑
内容的提问来源于stack exchange,提问作者Amit Langer
相关产品推荐
相关产品推荐

