rurpc

发布时间:2026/11/3 10:06:34
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

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询