在实际的微服务架构里,服务注册与发现这件事,rpcx默认是“装死”的——不配插件就不干活。很多新手踩的第一个坑就是:服务端直接 server.Serve() 启动后,客户端用 NewPeer2PeerDiscovery 死活连不上。原因很简单,服务端根本就没向注册中心上报过自己的地址。
必须手动加载注册中心插件,并配好地址。举个 etcd 的例子:
s := server.NewServer(
server.WithRegistry(&etcd.Registry{
Address: []string{"http://127.0.0.1:2379"},
Timeout: 5 * time.Second,
}),
)
- 注册中心类型(etcd、consul、zookeeper)需要对应导入
github.com/smallnest/rpcx/registry/xxx包,缺了它就报错。 server.RegisterName()的第三个参数是版本号,建议填"v1",默认为空,客户端匹配时可能会丢。- 如果非要用
Peer2PeerDiscovery这种点对点直连,服务端可以不用注册插件,但客户端得硬编码地址,生产环境就别指望这么干了。
客户端负载均衡策略选错会导致请求全部打到单个实例
NewXClient 的第三个参数是选择器(selector),千万别被 RandomSelect 这个名字骗了——它只在第一次请求时随机挑一个节点,后面所有请求都固定打在那一个上,除非节点挂了才换。这哪里是负载均衡,分明是“定点扫射”。
真正能持续分发请求的策略是 client.RoundRobin 或 client.SmoothWeightedRoundRobin:
xclient := client.NewXClient(
"Arith",
client.Failfast, // 超时立即失败,不重试
client.RoundRobin, // 每次请求轮询下一个节点
d,
client.DefaultOption,
)
Failtry会重试(默认3次),短时不可用时有用,但用不好会放大雪崩风险。- 权重策略需要服务端注册时带
Weight字段,否则退化为普通轮询。 - 想自己实现选择器?必须实现
Selector接口,直接传个函数不行。
传输协议切换要同步改客户端和服务端配置
rpcx 支持 TCP/KCP/QUIC/UTP,但两端协议不匹配时连接直接拒绝,错误信息一般是 read tcp: i/o timeout 或 connection refused,没有明确提示“协议不对”,排查起来很头大。
比如启用 KCP:
- 服务端启动用
s.Serve("kcp", ":8972"),并且编译时需加-tags kcp。 - 客户端发现器地址必须写成
kcp@localhost:8972,不能仍用tcp@...。 - KCP 默认开启快速重传和拥塞控制,但在内网稳定环境下效率反而不如 TCP,实测延迟高 10%~15%。
QUIC 同理,Go 版本需要 ≥1.18,编译加 -tags quic,客户端地址前缀为 quic@...。
TLS加密必须两端同时启用,且证书验证逻辑不同
rpcx 的 TLS 不是“开个开关就完事”。服务端配了 WithTLSConfig 后,客户端如果不配 TLSConfig,连接会被直接断开,报错 remote error: tls: bad certificate,而不是连接超时。
- 服务端
tls.Config中ClientAuth设为tls.RequireAndVerifyClientCert时,客户端必须提供有效证书,否则握手失败。 - 客户端如果跳过证书校验(
InsecureSkipVerify: true),服务端日志里不报错,但通信实际上是明文——rpcx 不强制校验,这个点容易被忽略。 - JWT 认证需要配合
serverplugin.AuthPlugin使用,单独配 TLS 不等于身份认证已启用。
协议层加密和业务层认证是两件事,缺一不可;很多团队只做了 TLS,就以为安全加固已完成,其实还差得远。
