ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

rpcx 方法级服务注册:用 RegisterWithMethods 白名单只暴露你点名的 RPC 方法

rpcx 方法级服务注册:用 RegisterWithMethods 白名单只暴露你点名的 RPC 方法 后端RPC框架微服务【免费下载链接】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/smallnest/rpcx点击查看免费下载本篇基于 rpcx 仓库中 方法级注册设计文档配套需求见 PRD解读一个安全特性服务端注册 struct 时通过白名单精确圈定哪些方法暴露为 RPC 端点。读完你将理解整个 struct 全量注册这一默认行为的源码源头、白名单过滤与校验的实现位置、四条关键取舍单一路径、白名单而非黑名单、报错而非静默、空名单报错以及该特性在 server/service.go 与 server/service_test.go 中的完整落地证据。背景注册的粒度今天是整个 struct而真实需求是一个子集rpcx 服务端注册一个 struct就把它所有签名合适的导出方法全开成 RPC。这条路径的源头在 server/service.go 的内部register函数中// server/service.go all : suitableMethods(service.typ, true) // 全部合适方法无从挑选 ... service.method allsuitableMethods 会扫描typ的每个方法把导出且签名匹配的全部收进service.method这张 map。签名校验规则很明确方法必须是导出的、入参共 4 个receiver、context.Context、参数、指针回复、出参 1 个且为error、参数与回复类型必须导出。注册完成后这些方法就都能被远程调用。问题在于导出 ≠ 想开成 RPC。一个 service struct 上常有些导出方法是给同进程其他代码复用的——比如同一份业务逻辑既要被 rpcx 暴露又要被 HTTP handler 或 jsonrpc 调用。在方法级注册能力出现之前你没法说这个方法给本地用、别开成 RPC要么把它改成非导出同包代码也调不到了要么把 struct 拆开。对应 GitHub Issue #581 的诉求正是注册时指定具体注册哪些方法动机确认为避免暴露不该暴露的方法并归入 8.0 版本。设计白名单是 register 的一个可选参数旧入口传 nil设计的第一原则是不另起炉灶。现有register(rcvr, name, useName)已经是Register和RegisterName共用的核心给它加第四个参数methods []stringnil表示全要旧行为非 nil 表示只要名单里的。一条内部路径两种公开入口。四个公开入口两条行为路径当前仓库中四个入口都已落地见 server/service.go// 旧入口行为、签名都不动内部传 nil func (s *Server) Register(rcvr any, metadata string) error { sname, err : s.register(rcvr, , false, nil) // ← 多一个 nil if err ! nil { return err } return s.Plugins.DoRegister(sname, rcvr, metadata) } // 新入口白名单式 func (s *Server) RegisterWithMethods(rcvr any, methods []string, metadata string) error func (s *Server) RegisterNameWithMethods(name string, rcvr any, methods []string, metadata string) error新入口在公开层先做一道空名单拦截func (s *Server) RegisterWithMethods(rcvr any, methods []string, metadata string) error { if len(methods) 0 { return errors.New(rpcx.Register: empty methods whitelist; use Register to register all methods) } sname, err : s.register(rcvr, , false, methods) ... }注意nil与空切片在这两个层面的语义不同内部register收到nil才走旧的全量路径只有公开的Register/RegisterName这么传公开的RegisterWithMethods/RegisterNameWithMethods收到nil或空切片都按空名单直接报错。用法示例README 中的真实示例README.md// Arith 有 Mul、Add、Sub 三个合适方法但只允许 Mul/Add 被远程调用 s : server.NewServer() err : s.RegisterWithMethods(new(example.Arith), []string{Mul, Add}, ) // 或者指定服务名 // err : s.RegisterNameWithMethods(Arith, new(example.Arith), []string{Mul, Add}, ) s.Serve(tcp, addr)过滤 校验在 suitableMethods 之后做一次分流核心逻辑就在 register 内部suitableMethods先照常算出所有合适方法的 map然后按白名单过滤对名单里每个没命中的名字再分流出具体错误原因// server/service.go register 内部 all : suitableMethods(service.typ, true) // 既有逻辑全量 if methods nil { // 无白名单注册所有合适方法原始行为 service.method all } else { // 白名单只注册点名的方法。调用方 //RegisterWithMethods/RegisterNameWithMethods已保证非空。 picked : make(map[string]*methodType) for _, m : range methods { if mt, ok : all[m]; ok { picked[m] mt continue } // 不合适区分不存在这样的导出方法与 // 存在但签名不是合适的 RPC 方法两种情况。 if _, exists : service.typ.MethodByName(m); exists { errorStr : fmt.Sprintf(rpcx.Register: method %q of %s is not a suitable RPC method, m, sname) log.Error(errorStr) return sname, errors.New(errorStr) } errorStr : fmt.Sprintf(rpcx.Register: method %q not found on %s, m, sname) log.Error(errorStr) return sname, errors.New(errorStr) } service.method picked }两个边界值得注意过滤只决定哪些方法进service.method不碰方法本身的调用、编解码、selector——下游一律照旧。校验失败时的return发生在写入s.serviceMap之前写入语句在 server/service.go#L245所以不会留下半注册的 service。另外过滤后的service.method若最终为空还会落到既有的 no exported methods of suitable type含指针接收者提示分支统一报错。改造前 vs 改造后改造前Register(new(Calc)) → suitableMethods → {Add, Sub, Reset 中所有合适的} 全部开成 RPC 改造后RegisterWithMethods(new(Calc), []string{Add,Sub}) → suitableMethods 算全量 → 按 {Add,Sub} 过滤 → 只开 Add、Sub 名单写错名字 / 写了签名不符的方法 / 空名单 → 直接报错不注册理由与取舍四个关键决策为什么加参数走一条路径而不是另写一个 registerWithMethods最朴素的做法是新写一个registerWithMethods函数与register并存。设计文档明确没选理由是两者除了过滤那几行几乎完全一样——并存等于把 service 构造、指针接收者提示、错误处理、写 map 这一长串逻辑抄两份日后改一处要记得改两处。给register加一个methods参数、旧入口传nil让新旧共用同一条路径是改动最小、最不容易长歪的接法。代价是register的签名多了一个参数但它是非导出函数只需在包内调用点补nil对包外用户完全不可见。为什么用白名单而不是黑名单可以做成排除某些方法的黑名单。选白名单是因为这个特性的初衷是安全——避免暴露不该暴露的方法。白名单默认不暴露你新加一个方法除非显式列进名单否则它不会悄悄变成 RPC 端点黑名单则相反新方法默认暴露忘了加进黑名单就漏了。安全的默认应该是默认关所以白名单。为什么名单里有不存在/签名不符的方法名要报错而不是静默忽略静默忽略看着宽容实则危险。你把Add拼成add或把一个签名不符 RPC 形态的方法写进名单静默忽略的结果是这个接口没被注册、却没人告诉你——直到线上调用方收到方法不存在才发现。实现上选用service.typ.MethodByName(m)把根本不存在/非导出和存在但签名不符分成两条错误信息不存在rpcx.Register: method %q not found on %s存在但签名不符rpcx.Register: method %q of %s is not a suitable RPC method宁可注册时吵一句也不让接口静默缺失。为什么空名单报错而不是当成全部或全不要空名单nil 或len0有三种可能的语义全要、全不要、报错。全要会和Register重复且容易误用本想填名单却传了空结果全暴露正好踩中要避免的事全不要注册一个零方法的 service 毫无意义。所以让它报错并提示要全部就用Register/RegisterName——把模糊地带关掉逼调用方表达清楚意图。兼容性纯增量变更对现有用户零破坏Register/RegisterName的公开签名不变行为不变——它们内部给register传nil走的还是service.method all那条老路注册结果逐字节一致。新增的只有两个公开方法和一个非导出函数的参数。唯一代价内部register的签名多了一个参数。这是包内改动需同步更新包内所有调用点Register、RegisterName两处各加一个nil对包外用户完全不可见。没有迁移路径要写——老代码不用动想用新能力的人改调一个新方法即可。实现与验证单文件改动 六组单测改动集中在一个文件 server/service.go分三步、可独立验证改内部register签名加methods []string实现nil 走全量、非 nil 走过滤校验的分流更新Register/RegisterName两处调用传nil。加两个公开入口RegisterWithMethods/RegisterNameWithMethods各自先拦截空名单、再透传给register注册成功后照旧走Plugins.DoRegister。补单测白名单子集生效、名单含不存在方法报错、含签名不符方法报错两条错误信息不同、空名单报错且任一错误下 service 未进serviceMap。仓库中的测试 server/service_test.go 用一个专门的测试 struct 覆盖了全部四类分支// WhitelistArith exposes two suitable RPC methods (Add, Sub), one exported // method with an unsuitable signature (NotRPC), and one unexported method. type WhitelistArith int func (t *WhitelistArith) Add(ctx context.Context, args *Args, reply *Reply) error { ... } func (t *WhitelistArith) Sub(ctx context.Context, args *Args, reply *Reply) error { ... } // NotRPC is exported but is not a suitable RPC method (wrong signature). func (t *WhitelistArith) NotRPC() string { return not rpc }测试函数验证点TestRegisterWithMethods_subset只注册Add断言service.method长度为 1含Add不含Sub——直接印证key 集合等于名单集合的形状断言TestRegisterWithMethods_notFound名单含Nope报错含not found且serviceMap中无该 service无部分注册TestRegisterWithMethods_notSuitable名单含签名不符的NotRPC报错含not a suitableservice 未注册TestRegisterNameWithMethods_subset带服务名入口注册名Calc下含Add、Sub两个方法TestRegisterWithMethods_emptyWhitelistnil与[]string{}两种输入均报empty methods whitelist均不注册TestRegisterNameWithMethods_emptyWhitelist带服务名入口的空名单同样报错且不注册运行方式go test ./server/即可复现上述断言。运行时视角白名单外的方法会发生什么过滤只影响注册不影响调用链路。请求进来时handleRequest 先按服务名查serviceMap再查service.method[methodName]mtype : service.method[methodName] if mtype nil { if service.function[methodName] ! nil { // check raw functions return s.handleRequestForFunction(ctx, req) } err errors.New(rpcx: cant find method methodName) return s.handleError(res, err) }也就是说白名单外的那个导出方法仍然存在于 struct 上、可被同进程代码正常调用只是远程调用会收到rpcx: cant find method xxx错误——这正是给本地用、别开成 RPC的预期效果。并发安全沿用现有的serviceMapMu读写锁见 server/server.go没有引入新的并发模型。未决问题设计文档保留了三个开放项方法名匹配是否大小写不敏感当前定为精确匹配——Go 方法名本就大小写敏感跟随语言语义。是否提供辅助函数列出某 struct 上所有可注册方法名方便用户构造白名单倾向后续增强不进本版。RegisterFunction系列是否要类似能力当前定为不在范围——函数级注册本就是单个函数没有子集问题。参考文件设计文档tasks/design-method-level-registration.md需求文档tasks/prd-method-level-registration.md核心实现server/service.go四个入口 内部register、suitableMethods单元测试server/service_test.go请求分发未注册方法的运行时行为server/server_dispatch.go使用说明README.md赞分享后端RPC框架微服务【免费下载链接】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/smallnest/rpcx点击查看免费下载相关推荐rpcx 从零构建 Go 微服务服务注册、方法级暴露与客户端容错治理rpcx 从零构建 Go 微服务服务注册、方法级暴露与客户端容错治理 本篇基于 rpcx 仓库的主 README 与核心源码完整走通 rpcx 微服务框架的后端RPC框架微服务rpcx 代码简化实战上帝对象拆分与方法白名单注册规则的收敛rpcx 代码简化实战上帝对象拆分与方法白名单注册规则的收敛 本文围绕 rpcx 仓库中的 代码简化设计文档 https://link.gitcode.com后端RPC框架微服务qmd向量嵌入生成指南node-llama-cpp使用与性能调优qmd向量嵌入生成指南node llama cpp使用与性能调优 qmd是一款轻量级本地文档搜索引擎通过node llama cpp库实现高效的向量嵌入生成人工智能大模型RAG搜索引擎本地部署MCP 服务CLI上一篇突破语音格式壁垒silk-v3-decoder让全平台音频转换效率提升5倍下一篇TTGO-T-Display终极指南ESP32智能显示屏开发板快速入门创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表