rurpc

发布时间:2026/11/3 10:06:54

rurpc rurpc 工程讲解src/rurpc是 ruwebframe 框架的RPC 模块基于字节跳动开源的 Volo-gRPC 实现远程过程调用。它同时内置了服务端、客户端、服务发现注册三块能力。代码生成依赖独立 cratevolo-gen后者在build.rs中依据idl/*.proto编译期生成桩代码rurpc通过volo_gen::volo_gen::...路径引用。目录结构src/rurpc/ ├── mod.rs # 模块声明入口 ├── main.rs # 独立演示 bin 入口装配并启动 RPC 及其它插件 ├── rpcplugin/ # RPC 生命周期插件真正拉起 gRPC 服务 │ ├── rpc_plugin.rs # RpcPlugin实现 Plugin traitstart 时注册并启动服务 │ ├── rpc_plugin_init.rs # #[ctor] 自动注册单例 Bean │ └── rpc_plugin_test.rs ├── rpcreg/ # RPC 服务注册etcd 服务发现参考 webreg │ ├── rpc_etcd_reg.rs # RpcEtcdReg / RpcRegValue │ ├── rpcreg_plugin.rs # RpcregPluginstart 注册、stop 注销 │ └── rpcreg_plugin_init.rs ├── rpcrouter/ # 注册汇总入口插件层调用的唯一入口 │ ├── mod.rs │ └── register_rpc.rs # register_rpc()Once 幂等转调 rpcservice::register_rpc() ├── rpcservice/ # 业务服务实现 │ ├── mod.rs # 子模块声明 │ ├── register_rpc.rs # 依次触发三个服务自注册 │ └── voloservice/ # 每个服务一个文件 │ ├── item_service.rs # 商品服务codec impl ItemService 自注册 │ ├── hello_service.rs # 问候服务codec impl HelloService 自注册 │ └── mail_service.rs # 邮件服务codec impl MailService 自注册 ├── voloclient/ # Volo-gRPC 客户端 │ ├── rpc_client.rs # RpcClientC 通用客户端 RpcClients 业务门面 │ ├── rpc_client_init.rs │ └── rpc_client_test.rs └── voloserver/ # Volo-gRPC 服务端 ├── mod.rs # register_rpc! 宏 模块声明 re-export ├── rpc_server.rs # RpcServer启动/绑定/注入路由 ├── rpc_container.rs # RpcContainer注册函数表 serve ├── volo_routes.rs # VoloRoutes 路由容器 RpcRouterLayer register_service ├── rpc_server_init.rs # RpcServer 的 DI Bean 注册 ├── rpc_container_init.rs # RpcContainer 的 DI Bean 注册 └── rpc_server_test.rs一、代码生成地基接口定义在idl/*.protoproto 文件package逻辑服务名volo_example.protovolo.examplevolo.example.ItemServicevolo_hello.protohellohello.HelloServicevolo_mail.protomailmail.MailServicevolo-gen/build.rs编译这些 proto 生成volo_gen.rs为每个服务生成*Servicetrait服务端需实现、*ServiceServer*RequestRecv/*ResponseSend*ServiceClientBuilder、*ServiceClient二、服务端 voloserver2.1 启动骨架RpcServerRpcServer是单例 Bean负责启动与注入不点名任何具体服务启动RpcServer::start()开独立线程 自建 tokio 运行时避免占用主线程。绑定serve()读取RpcConfigenable / serverName / serverPort绑定0.0.0.0:port并用local_addr()打印实际端口。注入路由build_server()把路由容器整体作为内层Layer注入Serverpubfnbuild_serverIL,OL(self,server:ServerIL,OL,)-ServerStackRpcRouterLayer,IL,OL{server.layer(RpcRouterLayer(VoloRoutes::global().router()))}2.2 路由容器VoloRoutesvolo_routes.rs对齐 web 的RouteContainer各服务自行登记 codec容器统一持有启动时一次性注入。内部RwLockRouterRouter是 volo 公开类型内部用BoxCloneService装箱因此类型各异的 codec 能收敛进同一容器。global()提供惰性单例Once OnceLockregister(codec)写入router()取出供注入。RpcRouterLayer把取出的Router直接作为Server的内层ServiceLayer::layer忽略 inner。同文件导出自由函数register_service(codec)作为各服务的自注册入口。pubstructVoloRoutes{router:RwLockRouter,}implVoloRoutes{pubfnglobal()-staticVoloRoutes{/* Once OnceLock */}pubfnregisterC(self,codec:C){/* take add_service put back */}pubfnrouter(self)-Router{/* read().clone() */}}pubfnregister_serviceC(codec:C){VoloRoutes::global().register(codec);}2.3 服务容器RpcContainerrpc_container.rs单例 Bean持有注册函数表RwLockVecfn()并提供运行入口register(registrar)把一个fn()型注册函数压入表。register_all_services()Once保护下遍历执行表内注册函数幂等避免同路径重复挂载导致 voloRouterpanic。serve(incoming, signal)先register_all_services()再RpcServer::build_server(Server::new())最后run_with_shutdown。两条注册路径说明重要路径 A当前默认生效RpcPlugin::register→rpcrouter::register_rpc()→rpcservice::register_rpc()→ 各xxx_service::register_rpc()→voloserver::register_service(codec)→ 直接写入VoloRoutes。不经过RpcContainer的函数表。路径 B预留自由函数rpc_server::register_rpc(fn())把注册函数压入RpcContainer再由register_all_services()统一触发。当前只有路径 A 被接入因此RpcContainer.registrars实际为空register_all_services()是无副作用的空遍历路由仍能正常注入因为build_server读取的是VoloRoutes。两条路径都受Once幂等保护。2.4 业务实现rpcservice/voloservice每个服务一个文件codec 构建、业务实现、自注册三件事都在同一文件里。统一由宏register_rpc!#[macro_export]定义在voloserver/mod.rs生成 codec 函数与自注册函数// voloserver/mod.rsmacro_rules!register_rpc{($name:ident,$srv:ident,$recv:ty,$send:ty,$doc:expr){#[doc $doc]fn$name()-implService...NamedServiceCloneSendSyncstatic{ServiceBuilder::new($srv::new(RpcServer::new())).build::$recv,$send()}/// 登记本服务到全局路由容器。pubfnregister_rpc(){crate::rurpc::voloserver::register_service($name());}};}服务文件里只需一行宏调用 一段impl// rpcservice/voloservice/hello_service.rsusecrate::{register_rpc,rulog};register_rpc!(codec,HelloServiceServer,HelloServiceRequestRecv,HelloServiceResponseSend,构建问候服务 HelloService 的编解码器。);implHelloServiceforRpcServer{/* say_hello ... */}业务实现示例impl MailServiceRpc for RpcServer把请求转成SendMailDto再代理给业务 BeanMailService::send。2.5 汇总入口rpcrouterrpcrouter::register_rpc::register_rpc()是插件层调用的唯一注册入口Once保证幂等内部转调rpcservice::register_rpc()// rpcrouter/register_rpc.rspubfnregister_rpc(){staticONCE:std::sync::Oncestd::sync::Once::new();ONCE.call_once(||{rpcservice::register_rpc();});}// rpcservice/register_rpc.rspubfnregister_rpc(){item_service::register_rpc();hello_service::register_rpc();mail_service::register_rpc();}2.6 新增一个 gRPC 服务在rpcservice/voloservice/新建xxx_service.rs写一行register_rpc!(...)再写impl XxxService for RpcServer。在rpcservice/voloservice/mod.rs加pub mod xxx_service;。在rpcservice/register_rpc.rs加一行xxx_service::register_rpc();。无需改动rpc_server.rs——服务端骨架与注册机制完全与服务数量解耦。命名提示voloserver/mod.rs的宏名为register_rpc!它生成的函数也叫register_rpc()voloserver::rpc_server另有自由函数register_rpc(fn())rpcrouter::register_rpc与rpcservice::register_rpc各有一个同名函数。它们处于不同模块作用域调用时需写全路径以免混淆。三、客户端 voloclientRpcClientC是通用客户端C为某服务生成的 client 类型一套逻辑服务所有服务pubstructRpcClientC{service_name:staticstr,// 逻辑服务名build:fn(staticstr,SocketAddr)-C,// client 构建函数cell:OnceLockC,// 懒加载缓存}implC:CloneRpcClientC{pubfnnew(service_name:staticstr,build:fn(staticstr,SocketAddr)-C)-Self;pubfnclient(self)-ResultC,String;// 取缓存或按配置构建}地址解析收敛为一个自由函数resolve_target()读全局配置serverName/serverPort非法地址回退127.0.0.1OnceLock缓存每个服务只建一次新增服务只需RpcClient::new(服务名, |n, a| XxxServiceClientBuilder::new(n).address(a).build())RpcClients是业务门面单例 Bean为每个服务持有一个RpcClient并暴露业务友好方法字段通用客户端业务方法itemRpcClientItemServiceClientget_item(item_id)helloRpcClientHelloServiceClientsay_hello(name)mailRpcClientMailServiceClientsend_mail(SendMailDto)业务方法内部完成 DTO ↔ proto 消息互转并统一把构建失败转成Status::Unavailable。四、生命周期插件 rpcpluginRpcPlugin实现框架Plugintraitstart()依次执行init()→plugin_default_init()register()→rpcrouter::register_rpc::register_rpc()登记各服务RpcServer::new().start()拉起 gRPC 服务由#[ctor]自动注册为单例在src/main.rs通过ctx.add(get_bean_unwrap!(RpcPlugin))纳入统一启动流程。五、服务发现 rpcregRpcregPlugin把当前 RPC 服务注册到etcd复用 webreg 的EtcdReg底层key/ruwebframe/rpc/{serverName}valueRpcRegValue含urlgrpc://host:port、serviceTyperpc等start_register()异步注册并持续续期stop_register()注销如此服务端启动后在 etcd 注册客户端/网关可据此做服务发现与路由而不必写死地址。模块职责小结模块角色关键类型rpcplugin生命周期驱动RpcPluginrpcreg注册中心RpcregPlugin/RpcEtcdRegrpcrouter注册汇总入口register_rpc()rpcservice业务实现每服务一文件register_rpc!生成的服务实现_c voloclient调用方RpcClientC/RpcClientsvoloserver提供方骨架 容器RpcServer/RpcContainer/VoloRoutesmain.rs演示入口装配各插件注册机制对比与 web 路由对齐环节webrurpc容器RouteContainerVoloRoutes登记项路由配置服务 codec自注册各 controller 调用reg_route各服务文件register_rpc!生成的register_rpc()汇总入口apply_routes(conf)rpcrouter::register_rpc()注入装配到Appserver.layer(RpcRouterLayer(router))单例OnceLockOnce OnceLock/ DI Bean一图流调用链[main.rs] ctx.add(RpcregPlugin → 注册到 etcd) ctx.add(RpcPlugin) └─ start ├─ register → rpcrouter::register_rpc()Once │ └─ rpcservice::register_rpc() │ ├─ item_service::register_rpc() → voloserver::register_service(codec) │ ├─ hello_service::register_rpc() → voloserver::register_service(codec) │ └─ mail_service::register_rpc() → voloserver::register_service(codec) │ └→ VoloRoutes::register(codec) └─ RpcServer::start → serve ├─ RpcContainer::register_all_services()当前表为空 ├─ RpcServer::build_server → layer(RpcRouterLayer(VoloRoutes::global().router())) └─ run_with_shutdown └─ Router 分发 → impl ItemService / HelloService / MailService └─ MailService::send ctx.add(WebPlugin) 客户端侧RpcClients { get_item / say_hello / send_mail } → RpcClientC::client()OnceLock 缓存 resolve_target → volo_gen client → 对端 RpcServer
延伸阅读

更多相关文章

2026/11/3 10:06:54

(二十三)基于RO的数字签名——BLS Scheme

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 10:06:54

政务审批系统的状态机与权限设计:以证件借用归还流程为例

政务审批系统看起来是表单和流程,真正难的是状态、权限和审计。本文以证件借用归还流程为例,讨论如何把线下规则拆成可执行的状态机、审批路由和日志模型。一、先定义状态,不要先画页面证件借用归还流程至少包含这些状态:草稿、待…

2026/11/3 10:06:54

(二十四)在RO模型下的BB方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 11:17:37

35岁测试工程师转型指南:技术升级与职业突破

1. 35岁软件测试工程师的职场困境解析 35岁对于软件测试从业者而言,往往是一个关键的分水岭。在这个阶段,我们通常会面临三个维度的职业挑战: 首先是技术迭代带来的压力。随着DevOps和持续测试的普及,传统的功能测试岗位需求正在…

2026/11/3 11:17:37

SQL高级语言实战:窗口函数与CTE深度解析

1. 为什么SQL高级语言值得深入学习 作为一名与数据库打了十年交道的开发者,我见过太多人止步于基础的SELECT和INSERT语句。SQL高级语言就像一把瑞士军刀,能让你在数据处理的世界里游刃有余。记得去年有个电商项目,团队里有个小伙子用几十行Ja…

2026/11/3 11:17:37

两轮电动车智能化转型:物联网与边缘计算实践

1. 合作背景与行业现状 两轮电动车行业正经历从传统代步工具向智能化终端转型的关键阶段。根据中国自行车协会数据显示,2022年全国电动自行车保有量已达3.5亿辆,年产量超过4000万辆。在这个万亿级市场中,智能化渗透率不足15%,存在…

2026/11/3 11:17:37

校园反诈骗系统开发:Spring Boot与Android双端架构实践

1. 项目背景与核心需求 校园诈骗案件近年来呈现高发态势,据某高校保卫处统计,2022年该校电信诈骗案件同比上升37%,单笔最高损失达8万元。这种背景下,开发校园反诈骗系统具有显著现实意义。本项目采用Spring BootAndroid双端架构&a…

2026/11/3 11:17:37

测试部门转型:从成本中心到利润中心的实践路径

1. 测试部门从成本中心到利润中心的转型契机 在传统IT组织架构中,测试部门往往被视为纯粹的成本中心——每年消耗大量人力物力进行重复性验证工作,却难以直接量化其商业价值。这种定位导致测试团队在资源分配和话语权上长期处于弱势地位。但近年来&#…

2026/11/3 11:12:37

哈希表原理与应用:从基础实现到性能优化

1. 哈希表是什么?从生活场景理解核心概念第一次听说"哈希表"这个词时,我以为是某种新型家具。直到在调试一个超时查询时,老工程师扔给我一句"用哈希表重构吧",才真正见识到这个数据结构的威力。简单来说&…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/2 6:39:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 1:04:48

STM32实战:基于MAX14431数字隔离器的SPI隔离通信方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 1:04:48

工业SPI隔离通信设计:MAX14431BAEE+与STM32L4A6RG实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 1:04:48

STM32+NRF24L01无线抢答器:硬件设计、通信协议与低功耗实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/2 6:39:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑