结论:net/rpc仅适合快速验证,生产环境应避免高并发场景;高性能首选grpc或kitex,次选hprose-golang——因它们解决了net/rpc的单连接阻塞、gob序列化慢、无服务治理等硬伤。

直接说结论:用 net/rpc 能快速跑通,但生产环境别用它扛高并发;真要高性能,优先选 gRPC 或 KiteX,其次考虑 Hprose-Golang —— 它们不是“更高级”,而是解决了 net/rpc 的硬伤:单连接阻塞、Gob序列化慢、无内置服务治理。
为什么 net/rpc 在压测时 CPU 突增还超时?
net/rpc 默认走 HTTP + Gob,每个请求都新建/关闭 TCP 连接(除非手动配 http.Transport),且 Gob 编解码在高 QPS 下 CPU 占用飙升。更麻烦的是它不支持连接复用、超时粒度粗(只能设全局 http.Client.Timeout)、没法做熔断或重试。
- 现象:QPS 超 500 后延迟陡增,
pprof显示大量时间耗在gob.decode和net/http.(*conn).read - 修复路径:必须替换序列化协议(如用
jsonrpc2或自定义ServerCodec接入 MessagePack)+ 复用http.Transport+ 改用长连接监听(rpc.ServeConn配合net.Conn池) - 但注意:
net/rpc的方法签名强约束(必须两个参数+error返回)和仅支持 Go-to-Go 通信,让它天然不适合跨语言或灰度发布场景
gRPC 里 proto 文件改了,客户端不重新生成会怎样?
字段增删或类型变更后,旧客户端调用新服务端大概率 panic,错误通常是 proto: can't skip unknown field 或 unmarshaling error: invalid wire format —— 因为 gRPC 底层用 Protocol Buffers 二进制编码,字段编号错位直接导致解析失败。
- 兼容性底线:新增字段必须设
optional或带默认值;删除字段不能回收编号;修改字段类型属于不兼容变更 - 实操建议:用
protoc-gen-go的--go-grpc_opt=paths=source_relative保证生成路径一致;CI 中加入protoc --check_data_loss校验变更安全 - 别依赖 “看起来能跑”:哪怕 JSON over HTTP 网关(grpc-gateway)返回 200,内部 proto 解析失败时 gRPC 层已返回
codes.Internal
KiteX 的 TTHeader 协议和 Thrift Framed 有什么区别?
两者都是二进制传输协议,但 TTHeader 是 Kitex 自研的头部增强协议,专为微服务治理设计;Thrift Framed 是 Apache Thrift 的标准封装,侧重跨语言兼容。
-
TTHeader在帧头嵌入元信息(traceid、timeout、baggage),服务发现/限流/链路跟踪直接读取,不用解析完整 body -
Thrift Framed仅保证消息边界(4 字节长度前缀),所有治理逻辑得靠中间件从 payload 解析,性能损耗明显 - 实际选型:内部全 Go 服务用
TTHeader + Kitex Protobuf;对接 Python/Java 服务才切回Thrift Framed,且需确认对方 thrift 版本兼容(如 0.13+ 才支持 optional 字段)
Hprose-Golang 的 AddInstanceMethods 为啥注册不了私有方法?
因为 AddInstanceMethods 内部用 reflect.Value.MethodByName 查找方法,而 Go 的反射只能访问导出(首字母大写)方法 —— 这不是 Hprose 的限制,是 Go 语言本身规则。
- 现象:调用返回
method not found,日志里看不到该方法名,reflect.TypeOf(&T{}).NumMethod()也查不到 - 绕过方案:把方法名改成大写(如
add→Add),同时确保接收者是导出结构体(type Calculator struct{}合法,type calculator struct{}不行) - 注意副作用:Hprose 默认把方法名转小写传给客户端(如
Add变成add),如果客户端是 JS/Python,得确认它们是否接受小写方法名,否则要手动覆写MethodName钩子
真正卡住性能的往往不是框架选型,而是序列化协议和连接管理——比如用 gRPC 却没开 WithTransportCredentials(insecure.NewCredentials()) 在内网跳过 TLS 握手,或者 KiteX 用了 TTHeader 却忘了在 client 端配 WithTimeout 导致超时全部堆积在服务端。这些细节不写死在启动代码里,光靠文档根本没人记得住。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











