后端微服务【免费下载链接】rpcxBest microservices framework in Go, like alibaba Dubbo, but with more features, Scale easily. Try it. Test it. If you feel its better, use it! 有, 有! build for cloud!项目地址https://gitcode.com/gh_mirrors/rp/rpcx点击查看免费下载rpcx 通过一个 network 字符串tcp、http、kcp、quic、unix、memu、iouring 等选择底层传输其中实验性的rdma传输藏在 Go 构建标签rdma之后。本篇围绕 tasks/prd-rdma-conn-transport.md 这份 PRD 展开讲清楚一次典型的传输层替换是如何设计的把 RDMA 数据路径从第三方rsocket包切换到github.com/smallnest/gordma/rdmanet的Conn并只用一层薄适配器补齐net.Conn接口缺口。读完后你能掌握 rpcx 传输注册机制的工作原理、rdmanet.Conn与net.Conn的适配细节以及一套默认构建逐字节不变的兼容性验收方法。背景rpcx 用两张注册表选择传输rpcx 的传输选择建立在两张 map 之上客户端client.ConnFactories[network]工厂函数返回net.Conn服务端server.makeListeners[network]工厂函数返回net.Listener。tcp、http、kcp、quic、unix、memu、iouring 都按同一契约接入client/connection.go 中的init()注册了 http/kcp/quic/unix/memu/iouring 各工厂client/connection_rdma.go 和 server/listener_rdma.go 则以//go:build rdma标签注册rdma键。Client.Connect的默认分支会查ConnFactories查不到再回退newDirectConn// client/connection.go节选 default: fn : ConnFactories[network] if fn ! nil { conn, err fn(client, network, address) } else { conn, err newDirectConn(client, network, address) }改造前rdma这一格绑死在第三方github.com/smallnest/rsocket上——rsocket 直接把net.Conn/net.Listener都实现好了接入只要两行// 改造前client/connection_rdma.go return rsocket.DialTCP(address) // rsocket 直接给一个 net.Conn // 改造前server/listener_rdma.go return rsocket.NewTCPListener(host, p, blog) // rsocket 直接给一个 net.Listener代价是整条 RDMA 数据路径锁死在一个演进不受控的第三方库上。PRD 的目标很直接用rdmanet.Conn/rdmanet.Listener接管这两处让 rpcx 跑在自家维护的 RDMA 栈gordma上。接口差距rdmanet.Conn 只差接口形状与底层裸端点rdmanet.RawConn不同rdmanet.Conn已经内置了消息分帧、基于信用的流控和托管缓冲自带Read/Write/Close——本质上是一条 RDMA 上的io.ReadWriteCloser。它距离 Go 的net.Conn接口只缺两样东西// rdmanet.Conn 已有的能力已经够用 func (c *Conn) Read(p []byte) (int, error) func (c *Conn) Write(p []byte) (int, error) func (c *Conn) Close() error func (c *Conn) LocalAddr() string // 注意string不是 net.Addr func (c *Conn) RemoteAddr() string // 注意string不是 net.Addr // 没有 SetDeadline / SetReadDeadline / SetWriteDeadline // rdmanet.Listener.Accept() 返回 *rdmanet.Conn不是 net.ConnAddr() 返回 string。所以适配层的全部工作就是把类型对齐string → net.Addr、补三个 no-op 的 deadline 方法、把Accept()的返回值再包一层。Read/Write/Close原样透传数据路径一行都不碰。适配器实现一个文件封住全部 RDMA 特异性PRD 的 US-001 和 US-004 两个故事合并落地在 share/rdma_conn.go整个文件在//go:build rdma标签之后。net.Conn 适配器// share/rdma_conn.go节选 type RDMAConn struct { *rdmanet.Conn // 内嵌Read/Write/Close 自动透传 } var _ net.Conn (*RDMAConn)(nil) func (c *RDMAConn) LocalAddr() net.Addr { return RDMAAddr(c.Conn.LocalAddr()) } func (c *RDMAConn) RemoteAddr() net.Addr { return RDMAAddr(c.Conn.RemoteAddr()) } // deadline 三件套rdmanet.Conn 没有原生 deadline统一做成 no-op。 func (c *RDMAConn) SetDeadline(t time.Time) error { return nil } func (c *RDMAConn) SetReadDeadline(t time.Time) error { return nil } func (c *RDMAConn) SetWriteDeadline(t time.Time) error { return nil }几个实现要点内嵌而非包装结构体内嵌*rdmanet.ConnGo 的方法提升机制让Read/Write/Close零成本透传适配器不重新分帧、不重新缓冲。这是它和裸端点适配方案的本质区别——后者要在适配器里手写长度前缀分帧和残留缓冲这边一行都不写。RDMAAddr类型把 rdmanet 的字符串地址包成net.AddrNetwork()恒返回rdmaString()返回原始字符串type RDMAAddr string func (a RDMAAddr) Network() string { return rdma } func (a RDMAAddr) String() string { return string(a) }编译期接口断言var _ net.Conn (*RDMAConn)(nil)让形状对齐在编译期就有保证——这是薄适配器最强的正确性证明。net.Listener 适配器// share/rdma_conn.go节选 type RDMAListener struct { *rdmanet.Listener } var _ net.Listener (*RDMAListener)(nil) func (l *RDMAListener) Accept() (net.Conn, error) { c, err : l.Listener.Accept() // 返回 *rdmanet.Conn if err ! nil { return nil, err } return NewRDMAConn(c), nil } func (l *RDMAListener) Addr() net.Addr { return RDMAAddr(l.Listener.Addr()) }Accept()调用底层rdmanet.Listener.Accept()拿到*rdmanet.Conn包成RDMAConn返回Addr()完成string → net.Addr转换Close()直接依赖内嵌的*rdmanet.Listener提升。客户端与服务端接线工厂函数各改几行客户端工厂rdmanet.DialTimeoutclient/connection_rdma.go 对应 PRD 的 US-002完整实现只有二十几行//go:build rdma // build rdma package client import ( errors net github.com/smallnest/gordma/rdmanet github.com/smallnest/rpcx/share ) func init() { ConnFactories[rdma] newRDMAConn } func newRDMAConn(c *Client, network, address string) (net.Conn, error) { if network ! rdma { return nil, errors.New(network is not rdma) } conn, err : rdmanet.DialTimeout(address, c.option.ConnectTimeout) if err ! nil { return nil, err } return share.NewRDMAConn(conn), nil }要点使用rdmanet.DialTimeout(address, c.option.ConnectTimeout)而非无超时版本直接复用 rpcx 客户端已有的ConnectTimeout配置项语义与 tcp 等其他传输保持一致返回前先经share.NewRDMAConn包成net.Conn对上层Client完全透明network ! rdma的防御性检查原样保留文件保持//go:build rdma标签。服务端工厂rdmanet.Listen 与 host:portserver/listener_rdma.go 对应 PRD 的 US-003func rdmaMakeListener(s *Server, address string) (ln net.Listener, err error) { // Validate and normalize the host:port form rdmanet uses for its TCP // out-of-band handshake address. rdmanet.Listen exposes no backlog knob, // so the former RDMA_BACKLOG env var no longer applies. host, port, err : net.SplitHostPort(address) if err ! nil { return nil, err } l, err : rdmanet.Listen(net.JoinHostPort(host, port)) if err ! nil { return nil, err } return share.NewRDMAListener(l), nil }地址沿用host:port形式——这是rdmanet.Conn做 TCP 带外握手out-of-band handshake所用的地址net.SplitHostPortnet.JoinHostPort完成校验与归一化保证与现状相比无回归。值得注意的是源码注释明确说明rdmanet.Listen不再暴露 backlog 参数因此旧版RDMA_BACKLOG环境变量已失效——PRD 中保留或映射 RDMA_BACKLOG这条验收项最终以不再适用收口。init()中的makeListeners[rdma] rdmaMakeListener注册模式原样不动接入点没变只换了返回值的来源。依赖治理把 rsocket 连根拔掉PRD 的 US-005 要求依赖图彻底干净验收标准是grep -r rsocket --include*.go .零命中。从 go.mod 可以看到最终状态github.com/smallnest/rsocket已不在依赖列表中取而代之的是github.com/smallnest/gordma v0.3.0子包rdmanet。CHANGELOG.md 的 1.9.4 条目也印证了这次切换switch the experimentalrdmatransport from rsocket to gordmas rdmanet.Conn (build tagrdma)选择彻底删除而非并存的理由很实际留着 rsocket 当备选等于让模块多扛一个不维护的依赖、多一条没人走的代码路径既然rdmanet.Conn全面接管了 RDMA 路径就从go.mod/go.sum连根拔掉。关键设计取舍PRD 与配套设计文档tasks/design-rdma-conn-transport.md对四个核心取舍给出了明确论证。为什么用 rdmanet.Conn 而不是 RawConn这是本次设计最关键的岔路维度rdmanet.Conn本方案rdmanet.RawConn裸端点分帧/流控内置信用流控 托管缓冲需自己在适配器中手写长度前缀分帧、残留缓冲、post/poll 驱动适配器复杂度几十行封顶只做类型对齐显著更高正确性需自行论证吞吐上限托管层把极限优化空间提前焊死保留 batch/pipeline/单边 RDMA Write/Read 的吞吐天花板PRD 的 Non-Goals 明确了边界不使用RawConn、不在 rpcx 侧做手工分帧/批处理/流控、不在 RPC 数据路径中使用单边 RDMA Write/Read 动词。取舍逻辑是本次目标是干净地替掉 rsocket、让 rpcx 跑在自家 RDMA 栈上正确和薄比榨干线速更重要。若未来要打满线速再回到 RawConn 路线。为什么是适配器而不是改传输抽象最朴素的做法是让 rpcx 协议层直接认识rdmanet.Conn但这会侵入核心读写循环、破坏net.Conn这层统一抽象只为一个实验性传输买单。适配器把 RDMA 特异性关在一个文件里share/rdma_conn.go被 client 和 server 共用上层零改动——直接服务于默认构建不变的硬承诺。PRD 的 Open Questions 曾权衡单文件共享 vs 各自一份最终落地为单个共享文件由NewRDMAConn/NewRDMAListener两个构造函数对外提供。为什么 deadline 做成静默 no-op 而不是报错rdmanet.Conn没有原生 deadline 支持选项有三个报not supported错、给读写加尽力而为的超时、或静默 no-op。rpcx 的调用路径会主动SetDeadline——例如 client/connection.go 中IdleTimeout逻辑会执行conn.SetDeadline(...)若 RDMA 连接返回错误会直接打断正常 RPC 流程。硬塞超时又是在 v1 引入一套未经真机验证的半成品语义。最终选择返回 nil、不生效、注释写清是已知 no-op。宁可诚实地暂不支持也不假装支持——FR-4 把它写成了硬性需求源码中三个方法的注释也都如实标注了这一点。构建隔离rdma 标签就是总开关全部 RDMA 代码待在//go:build rdma之后这是 PRD 的 FR-9也是整个方案的硬约束不开rdma标签时默认构建产物与 rpcx 公开 API 一个字节都不变gordma 的 Linux 实现路径走 cgo libibverbsstub 构建返回gordma.ErrNotSupported所以非 RDMA 主机上跑默认构建与 CI 完全不受影响只有真正开启-tags rdma才需要在装了 libibverbs 的 Linux 主机上编译。兼容性谁会被影响怎么迁移对默认用户这不是破坏性变更。全部改动在 build tag 后面不开标签的人无需任何动作。对已经在用-tags rdma rsocket 的用户这是破坏性变更代价有三线缆不兼容rdmanet.Conn的握手与分帧和 rsocket 不互通两端必须同时升级不能混部行为变化deadline 从由 rsocket/TCP 实现变成 no-op依赖 RDMA 连接读写超时的代码需知晓依赖变化go.mod移除 rsocket、新增 gordma下游执行go mod tidy会看到依赖图变动。迁移路径很直接rdma标签本身就是特性开关在 RDMA 主机上重新构建、两端同时切换即可。由于这是藏在 build tag 后的实验性传输破坏面有限不提供线缆层兼容垫片。验收标准与验证方式PRD 的 Success Metrics 给出了一套可机械执行的验收闸门grep -r rsocket --include*.go .零命中go build ./...默认标签通过go build -tags rdma ./...在 libibverbs Linux 主机上编译通过rpcx 客户端能通过rdmanetwork 调用 rpcx 服务端且 RPC 语义与tcp一致。由于适配器不碰数据路径逻辑层几乎没有可单独单测的东西——编译期接口断言var _ net.Conn (*RDMAConn)(nil)承担形状正确性真正的功能验证落在真机端到端rpcx client overrdma调通 server依赖 libibverbs 的 Linux 主机。遗留问题PRD 结尾的 Open Questions 也值得记录它们定义了 v1 之后的演化方向真机-tags rdma端到端验证能否在现有 CI 环境完成还是只能在独立 RDMA Linux 主机上做是否把WithBufferSize/WithQueueDepth等rdmanet选项经环境变量类似旧RDMA_BACKLOG透出PRD 倾向 v1 全用默认值因为Conn自管缓冲——最终实现也遵循了这一倾向rdmanet.Listen未暴露 backlog 旋钮与姊妹方案RawConn 路线的关系两版设计文档并存于 tasks/ 目录未来若要极限吞吐再评估切换。小结这次重构的价值在于示范了一种低风险的传输替换范式接口差多少就只写多少适配代码。rdmanet.Conn距离net.Conn只差net.Addr类型和 deadline 方法族于是全部新增代码就是一个约百行的 share/rdma_conn.go 加两个各二十几行的工厂函数协议、编解码、字节流零改动默认构建逐字节不变依赖图干净到grep可验证。对于任何想把 rpcx或同架构框架接到自定义底层网络栈上的开发者这套注册表 build tag 薄适配器的组合都值得直接参照。赞分享后端微服务【免费下载链接】rpcxBest microservices framework in Go, like alibaba Dubbo, but with more features, Scale easily. Try it. Test it. If you feel its better, use it! 有, 有! build for cloud!项目地址https://gitcode.com/gh_mirrors/rp/rpcx点击查看免费下载相关推荐rpcx RDMA 传输替换 rsocket基于 rdmanet.Conn 的薄 net.Conn 适配器设计rpcx RDMA 传输替换 rsocket基于 rdmanet.Conn 的薄 net.Conn 适配器设计 rpcx 的实验性 rdma 传输已从第三方后端RPC框架微服务rpcx RDMA 传输重构用 rdmanet.Conn 薄适配器替换 rsocket一次干净的依赖切换rpcx RDMA 传输重构用 rdmanet.Conn 薄适配器替换 rsocket一次干净的依赖切换 本文围绕 rpcx 仓库中的产品需求文档 task后端RPC框架微服务rpcx RDMA 传输改造一层薄适配器把 rdmanet.Conn 接入 net.Conn 体系rpcx RDMA 传输改造一层薄适配器把 rdmanet.Conn 接入 net.Conn 体系 本文基于 rpcx 的设计文档 design rdma c后端微服务上一篇79万真实医患对话中文医疗对话数据集的完整指南下一篇NoFences终极指南免费开源的Windows桌面分区管理工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
