为什么直接手写高性能RPC框架不现实
除非你有多年网络协议栈、序列化引擎和线程调度优化经验,否则不建议从零实现一个“高性能”RPC框架。.NET 生态已有成熟方案:gRPC(基于 HTTP/2 + Protocol Buffers)、Microsoft.Extensions.DependencyInjection 配合
System.Net.Http.HttpClient做轻量调用、或开源的
DotNetty+
MessagePack组合。自己造轮子容易在以下环节翻车:
• 连接复用没做对 → TIME_WAIT 爆满<br>• 序列化未跳过反射 → <code>JsonSerializer.Serialize</code> 默认走反射,比 <code>System.Text.Json.SourceGeneration</code> 慢 3–5 倍<br>• 异步上下文丢失 → <code>async void</code> 或未用 <code>ConfigureAwait(false)</code> 导致线程节流<br>• 超时控制只靠 <code>CancellationToken</code> → 网络层未设 socket-level timeout,请求卡死数分钟
用 gRPC 替代自研是最快落地的“高性能”路径
gRPC 是 .NET 官方推荐、默认启用 HTTP/2 多路复用、支持流式调用、天然压缩、服务发现友好。关键不是“能不能写”,而是“有没有必要绕开它”。
• 定义接口只需写 .proto 文件,<code>dotnet-grpc</code> 工具自动生成 client/server stub<br>• 服务端用 <code>MapGrpcService<GreeterService>()</code> 注册,无需手动监听 socket<br>• 客户端用 <code>GrpcChannel.ForAddress("https://api.example.com")</code>,复用连接池自动管理<br>• 启用 <code>AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true)</code> 可跑在 HTTP/2 over TCP(非 TLS)环境性能瓶颈通常不在框架层,而在业务逻辑 IO 或序列化字段冗余。
若必须自定义通信层,优先改造而非重写
真正需要定制的场景极少,比如对接遗留二进制协议、硬件设备直连、或超低延迟要求(微秒级)。此时应基于
System.IO.Pipelines构建传输层,而非
TcpClient+
Stream:
• 用 <code>PipeReader</code>/<code>PipeWriter</code> 避免内存拷贝,减少 GC 压力<br>• 协议头固定 12 字节(含 magic number + length + msgid),用 <code>SequenceReader<byte></code> 零分配解析<br>• 序列化选 <code>MessagePackSerializer.Serialize<T>(buffer, value, options)</code>,禁用 JIT 生成(<code>MessagePackSerializerOptions.Standard.WithSecuritySafeMode(true)</code>)<br>• 回调不走 <code>Task.ContinueWith</code>,改用 <code>ValueTask</code> + <code>IValueTaskSource</code> 手动状态机(仅限极端场景)99% 的“高性能需求”实际是数据库慢查询、未索引、或同步阻塞日志写入导致的。
压测前必须关掉的三个默认开关
很多团队压出 500 QPS 就喊“性能差”,结果发现只是没调对配置:
• 关闭 Kestrel 的 <code>AllowSynchronousIO = true</code>(默认 false,但某些中间件误开会导致线程池饿死)<br>• 禁用 ASP.NET Core 的 <code>HttpResponse.BodyWriter</code> 自动 flush(设 <code>HttpResponse.BodyWriter.FlushAsync()</code> 显式调用,避免每字节都 syscall)<br>• 客户端 <code>HttpClient</code> 必须单例复用,且设置 <code>MaxConnectionsPerServer = int.MaxValue</code>(默认 2)没有真实流量模型就谈“高性能”,就像没画电路图就焊芯片。
