Go原生net/rpc功能受限,需定制化改造以支持多语言互通、上下文传递、中间件、自定义协议等;推荐替换为gRPC或HTTP框架,或通过自定义Codec/Conn/Client实现扩展。

Go 语言原生的 net/rpc 包提供了基于 HTTP 或 TCP 的简单 RPC 框架,但默认只支持 Gob 编码、要求方法签名严格匹配、不支持上下文传递、无内置超时与重试,也难以扩展中间件或适配自定义协议。若需满足特定通信需求(如兼容旧系统、对接非 Go 服务、要求 JSON-RPC 兼容、添加鉴权头、支持双向流、或使用 Protobuf 序列化),必须进行定制化改造。
替换序列化器:从 Gob 到 JSON/Protobuf
原生 RPC 默认用 Gob,但 Gob 不跨语言。要实现多语言互通,需替换编码器。
- 使用
jsonrpc包替代标准rpc.Server:导入"net/rpc/jsonrpc",服务端监听时用jsonrpc.ServeConn;客户端连接后用jsonrpc.NewClient。 - 若需 Protobuf,推荐放弃原生
net/rpc,改用 gRPC(底层即基于 HTTP/2 + Protobuf),或手动封装:定义.proto文件 → 生成 Go 代码 → 在 handler 中解析请求体为proto.Message→ 调用业务逻辑 → 序列化响应返回。 - 自定义编码器需实现
rpc.ServerCodec接口(ReadRequestHeader/ReadRequestBody/WriteResponse等),再通过rpc.ServeCodec启动服务。
增强服务端能力:注入上下文与中间件
标准 net/rpc 方法无法接收 context.Context,也不支持拦截请求。可通过包装 handler 实现增强。
- 在 TCP 连接建立后、调用
server.ServeConn前,用自定义net.Conn包装器读取前置元数据(如 token、trace-id),存入context.WithValue,再透传至业务方法(需修改方法签名并配合反射调用)。 - 更实用的方式是弃用
net/rpc,改用轻量 HTTP 框架(如gin或chi)暴露 RPC 风格接口:POST/rpc,请求体为{"method":"User.Get","params":[123]},统一做日志、鉴权、限流、超时控制,再分发到对应函数。 - 若坚持用原生 RPC,可在注册服务前用装饰器模式包装方法:将原始
func(*Args, *Reply) error改为闭包,内部注入 context 并调用实际逻辑。
适配自定义传输层:TCP+自定义帧格式
当需要与嵌入式设备、IoT 网关等低开销场景通信时,可能要求精简报文、固定长度头、或二进制帧协议。
立即学习“go语言免费学习笔记(深入)”;
- 实现自定义
net.Conn,在Read中按协议解析帧(如 4 字节长度头 + JSON body),在Write中先写长度再写内容。 - 基于该 Conn 构建
rpc.ServerCodec:在ReadRequestHeader中解析帧头获取 method 名和序列号;ReadRequestBody解析后续 payload;WriteResponse按同样格式打包返回。 - 注意处理粘包与半包:自定义 Codec 必须在读取时循环调用底层
conn.Read直到收满指定字节数,不可假设单次读完。
客户端增强:超时、重试与负载均衡
原生 rpc.Client 不支持超时,Call 是阻塞同步调用,且无重试机制。
- 用
context.WithTimeout控制调用时限:启动 goroutine 执行client.Call,主协程 select 等待结果或超时通道。 - 封装可重试 Client:对幂等方法(如 GET 类),在失败时自动重连并重发;需捕获
io.EOF、net.OpError等临时错误,配合指数退避。 - 若需多节点负载均衡,可维护服务发现列表(如 etcd/ZooKeeper),每次调用前用轮询或一致性哈希选取一个 endpoint,再新建
rpc.Client连接——注意复用连接避免频繁建链,建议用连接池管理。
不复杂但容易忽略:所有自定义都要确保服务端与客户端使用完全一致的编解码规则、错误约定和超时策略。建议将协议规范文档化,并用一组端到端测试验证边界情况(如大 payload、空参数、非法 method 名)。










