资讯动态

Hyperf JSON-RPC 服务实战:基于注解的微服务提供者与消费者完整指南

发布时间:2026/10/9 20:21:18 来源:尧图企业网站定制
后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载JSON-RPC 是一种基于 JSON 格式的轻量级 RPC 协议标准易于使用和阅读。在 Hyperf 中由hyperf/json-rpc组件实现可自定义基于 HTTP 协议传输或直接基于 TCP 协议传输。本篇指南将带你完整掌握在 Hyperf 中定义服务提供者ServiceProvider、服务消费者ServiceConsumer、发布服务到服务中心Consul / Nacos、返回 PHP 对象以及使用连接池化 Transporter 的完整流程并深入到源码层面理解协议注册、路由生成与错误码体系使你能快速搭建可运行的微服务 RPC 链路。JSON-RPC 在 Hyperf 中的定位Hyperf 的 RPC 体系由多个组件协同组成hyperf/json-rpc负责 JSON-RPC 协议的解析与封装提供Packer数据打包器、DataFormatter数据格式化器、Transporter数据传输器等协议处理核心hyperf/rpc-server服务端组件负责将#[RpcService]注解类注册为可调用的路由hyperf/rpc-client客户端组件提供AbstractServiceClient基类与动态代理机制让调用远程服务如同调用本地方法。三个组件的职责划分清晰json-rpc解决协议怎么讲rpc-server解决服务怎么被暴露rpc-client解决客户端怎么消费。安装安装 JSON-RPC 协议处理组件composer require hyperf/json-rpc该组件仅是 JSON-RPC 的协议处理组件通常还需要配合rpc-server或rpc-client来满足服务端和客户端场景如同时使用则都需要安装。要使用 JSON-RPC 服务端composer require hyperf/rpc-server要使用 JSON-RPC 客户端composer require hyperf/rpc-client若需要将服务发布到服务中心Consul / Nacos还需安装对应的服务治理组件见下文发布到服务中心一节。角色与服务契约服务有两种角色服务提供者ServiceProvider为其它服务提供服务的服务服务消费者ServiceConsumer依赖其它服务的服务。一个服务既可能是提供者同时又是消费者。两者之间通过服务契约来定义和约束接口的调用。在 Hyperf 中服务契约可直接理解为一个接口类Interface通常这个接口类会同时出现在提供者和消费者的代码中。定义服务提供者目前仅支持通过注解形式定义服务提供者后续迭代会增加配置形式。可以通过#[RpcService]注解对一个类进行定义即可发布这个服务?php namespace App\JsonRpc; use Hyperf\RpcServer\Annotation\RpcService; /** * 注意如希望通过服务中心来管理服务需在注解内增加 publishTo 属性 */ #[RpcService(name: CalculatorService, protocol: jsonrpc-http, server: jsonrpc-http)] class CalculatorService implements CalculatorServiceInterface { // 实现一个加法方法这里简单的认为参数都是 int 类型 public function add(int $a, int $b): int { // 这里是服务方法的具体实现 return $a $b; } }使用#[RpcService]注解需use Hyperf\RpcServer\Annotation\RpcService;命名空间。#[RpcService]注解的四个参数从源码 src/rpc-server/src/Annotation/RpcService.php 可以看到注解类定义了四个属性参数说明默认值name定义该服务的名称全局唯一即可Hyperf 会根据该属性生成对应的 ID 注册到服务中心protocol定义该服务暴露的协议目前仅支持jsonrpc-http、jsonrpc、jsonrpc-tcp-length-check分别对应于 HTTP 协议和 TCP 协议下的两种形态jsonrpc-httpserver绑定该服务类发布所要承载的Server对应config/autoload/server.php文件内servers下所对应的name意味着需要定义一个对应的Serverjsonrpc-httppublishTo定义该服务所要发布的服务中心目前仅支持consul、nacos或为空。为空时代表不发布到服务中心需手动处理服务发现问题关于protocol参数这里的值对应在Hyperf\Rpc\ProtocolManager中注册的协议的key它们本质上都是 JSON-RPC 协议区别在于数据格式化、数据打包、数据传输器等不同。server参数则要求配置文件config/autoload/server.php中存在同名 Server否则启动会报错——TcpServer 在初始化时会遍历server.servers查找匹配的配置项找不到则抛出InvalidArgumentException见 src/json-rpc/src/TcpServer.php。路由如何生成从源码 src/rpc-server/src/Router/DispatcherFactory.php 可以看到注解路由的注册过程为遍历注解收集器找到带有#[RpcService]的类通过反射获取该类所有公共方法跳过__开头的方法用PathGenerator基于服务名与方法名生成路径并注册到对应server的路由收集器中同时触发AfterPathRegister事件——这正是服务注册监听器RegisterServiceListener的挂载点。也就是说服务类中的每个公共方法都会自动成为一个可被远程调用的 RPC 方法。定义 JSON RPC ServerHTTP Server适配jsonrpc-http协议在config/autoload/server.php中配置?php use Hyperf\Server\Server; use Hyperf\Server\Event; return [ // 这里省略了该文件的其它配置 servers [ [ name jsonrpc-http, type Server::SERVER_HTTP, host 0.0.0.0, port 9504, sock_type SWOOLE_SOCK_TCP, callbacks [ Event::ON_REQUEST [\Hyperf\JsonRpc\HttpServer::class, onRequest], ], ], ], ];HttpServer继承自 Hyperf 的 HTTP Server 基类构造时从ProtocolManager中取出jsonrpc-http协议对应的Packer与DataFormatter见 src/json-rpc/src/HttpServer.php。它还会检查content-type是否为application/json并校验请求体中是否包含jsonrpc、method、params三个关键字段不满足时直接返回 JSON-RPC 标准错误响应。值得注意的是它还内置了对 Consul 健康检查的兼容当user-agent为Consul Health Check时直接放行不解析协议体见 src/json-rpc/src/HttpServer.php。TCP Server适配jsonrpc协议?php use Hyperf\Server\Server; use Hyperf\Server\Event; return [ // 这里省略了该文件的其它配置 servers [ [ name jsonrpc, type Server::SERVER_BASE, host 0.0.0.0, port 9503, sock_type SWOOLE_SOCK_TCP, callbacks [ Event::ON_RECEIVE [\Hyperf\JsonRpc\TcpServer::class, onReceive], ], settings [ open_eof_split true, package_eof \r\n, package_max_length 1024 * 1024 * 2, ], ], ], ];该配置采用EOF 分隔方式界定数据包边界以\r\n作为 JSON 消息的结束标记package_max_length限制单包最大 2MB防止异常数据撑爆内存。TCP Server适配jsonrpc-tcp-length-check协议jsonrpc-tcp-length-check是jsonrpc的扩展协议采用长度字段方式界定数据包边界只需修改对应settings即可切换?php use Hyperf\Server\Server; use Hyperf\Server\Event; return [ // 这里省略了该文件的其它配置 servers [ [ name jsonrpc, type Server::SERVER_BASE, host 0.0.0.0, port 9503, sock_type SWOOLE_SOCK_TCP, callbacks [ Event::ON_RECEIVE [\Hyperf\JsonRpc\TcpServer::class, onReceive], ], settings [ open_length_check true, package_length_type N, package_length_offset 0, package_body_offset 4, package_max_length 1024 * 1024 * 2, ], ], ], ];配置项含义open_length_check开启长度校验package_length_type为N表示长度字段为 4 字节无符号大端整数对应pack(N, ...)package_length_offset为 0表示长度字段位于包首package_body_offset为 4表示消息体从第 4 字节开始。有意思的是服务端并不需要为两种 TCP 协议分别配置不同的 Server从 src/json-rpc/src/TcpServer.php 可以看到TcpServer在初始化协议时会检查settings.open_length_check为真则自动选用jsonrpc-tcp-length-check协议及其对应的JsonLengthPacker否则使用jsonrpc协议。对应地JsonLengthPacker的pack()方法会先用pack($this-type, strlen($data))写入 4 字节长度头再拼接 JSON 正文见 src/json-rpc/src/Packer/JsonLengthPacker.php。发布到服务中心目前仅支持发布服务到consul、nacos后续会增加其它服务中心。发布服务到 Consul通过composer require hyperf/service-governance-consul引用组件如果已安装则可忽略该步骤然后在config/autoload/services.php配置文件内配置drivers.consul配置即可。发布服务到 Nacos 类似通过composer require hyperf/service-governance-nacos引用组件然后在config/autoload/services.php配置文件内配置drivers.nacos配置示例如下?php return [ enable [ discovery true, register true, ], consumers [], providers [], drivers [ consul [ uri http://127.0.0.1:8500, token , ], nacos [ // nacos server url like https://nacos.hyperf.io, Priority is higher than host:port // url , // The nacos host info host 127.0.0.1, port 8848, // The nacos account info username null, password null, guzzle [ config null, ], group_name api, namespace_id namespace_id, heartbeat 5, ], ], ];配置完成后在启动服务时Hyperf 会自动地将#[RpcService]定义了publishTo属性为consul或nacos的服务注册到对应的服务中心去。其实现机制是RegisterServiceListener监听AfterPathRegister事件当路由注册完成后自动把服务信息写入ServiceManager见 src/json-rpc/src/Listener/RegisterServiceListener.php由服务治理驱动如service-governance-consul完成实际注册。目前仅支持jsonrpc和jsonrpc-http协议发布到服务中心去其它协议尚未实现服务注册。定义服务消费者一个服务消费者可以理解为就是一个客户端类但在 Hyperf 中无需处理连接和请求相关的事情只需要进行一些鉴定配置即可。自动创建代理消费者类可通过在config/autoload/services.php配置文件内进行一些简单配置即可通过动态代理自动创建消费者类?php return [ // 此处省略了其它同层级的配置 consumers [ [ // name 需与服务提供者的 name 属性相同 name CalculatorService, // 服务接口名可选默认值等于 name 配置的值如果 name 直接定义为接口类则可忽略此行配置如 name 为字符串则需要配置 service 对应到接口类 service \App\JsonRpc\CalculatorServiceInterface::class, // 对应容器对象 ID可选默认值等于 service 配置的值用来定义依赖注入的 key id \App\JsonRpc\CalculatorServiceInterface::class, // 服务提供者的服务协议可选默认值为 jsonrpc-http // 可选 jsonrpc-http jsonrpc jsonrpc-tcp-length-check protocol jsonrpc-http, // 负载均衡算法可选默认值为 random load_balancer random, // 这个消费者要从哪个服务中心获取节点信息如不配置则不会从服务中心获取节点信息 registry [ protocol consul, address http://127.0.0.1:8500, ], // 如果没有指定上面的 registry 配置即为直接对指定的节点进行消费通过下面的 nodes 参数来配置服务提供者的节点信息 nodes [ [host 127.0.0.1, port 9504], ], // 配置项会影响到 Packer 和 Transporter options [ connect_timeout 5.0, recv_timeout 5.0, settings [ // 根据协议不同区分配置 open_eof_split true, package_eof \r\n, // open_length_check true, // package_length_type N, // package_length_offset 0, // package_body_offset 4, ], // 重试次数默认值为 2收包超时不进行重试。暂只支持 JsonRpcPoolTransporter retry_count 2, // 重试间隔毫秒 retry_interval 100, // 使用多路复用 RPC 时的心跳间隔null 为不触发心跳 heartbeat 30, // 当使用 JsonRpcPoolTransporter 时会用到以下配置 pool [ min_connections 1, max_connections 32, connect_timeout 10.0, wait_timeout 3.0, heartbeat -1, max_idle_time 60.0, ], ], ] ], ];关键配置项说明name需与服务提供者的name属性相同service/id均为可选service默认值等于nameid默认值等于service。当服务提供者以接口类名作为服务名发布时消费端只需设置name为接口类名即可无需再设置id和serviceregistry与nodes二选一配置registry则从服务中心动态获取节点不配置则直接对nodes中指定的节点进行消费options中的connect_timeout、recv_timeout会直接影响Transporter的行为。从 src/json-rpc/src/JsonRpcTransporter.php 源码可见两个超时默认值均为5.0秒settings会原样透传给底层 Socket 创建src/json-rpc/src/JsonRpcTransporter.php因此 EOF 与长度校验相关配置必须与服务端保持一致。在应用启动时会自动创建客户端类的代理对象并在容器中使用配置项id的值如果未设置会使用配置项service值代替来添加绑定关系这样就和手工编写的客户端类一样通过注入CalculatorServiceInterface接口来直接使用客户端。当服务提供者使用接口类名发布服务名在服务消费端只需要设置配置项name值为接口类名不需要重复设置配置项id和service。手动创建消费者类如对消费者类有更多的需求可通过手动创建一个消费者类来实现只需定义一个类及相关属性即可?php namespace App\JsonRpc; use Hyperf\RpcClient\AbstractServiceClient; class CalculatorServiceConsumer extends AbstractServiceClient implements CalculatorServiceInterface { /** * 定义对应服务提供者的服务名称 */ protected string $serviceName CalculatorService; /** * 定义对应服务提供者的服务协议 */ protected string $protocol jsonrpc-http; public function add(int $a, int $b): int { return $this-__request(__FUNCTION__, compact(a, b)); } }然后还需要在配置文件定义一个配置标记要从何服务中心获取节点信息位于config/autoload/services.php如不存在可自行创建?php return [ // 此处省略了其它同层级的配置 consumers [ [ // 对应消费者类的 $serviceName name CalculatorService, // 这个消费者要从哪个服务中心获取节点信息如不配置则不会从服务中心获取节点信息 registry [ protocol consul, address http://127.0.0.1:8500, ], // 如果没有指定上面的 registry 配置即为直接对指定的节点进行消费通过下面的 nodes 参数来配置服务提供者的节点信息 nodes [ [host 127.0.0.1, port 9504], ], ] ], ];这样便可以通过CalculatorService类来实现对服务的消费了。为了让这里的关系逻辑更加合理还应该在config/autoload/dependencies.php内定义CalculatorServiceInterface和CalculatorServiceConsumer的关系示例如下return [ App\JsonRpc\CalculatorServiceInterface::class App\JsonRpc\CalculatorServiceConsumer::class, ];这样便可以通过注入CalculatorServiceInterface接口来使用客户端了。从源码看手动创建的消费者类继承自 src/rpc-client/src/AbstractServiceClient.php基类默认定义了serviceName、protocol默认jsonrpc-http、loadBalancer默认random等属性构造时会从容器中取出Protocol、LoadBalancerManager用createNodes()生成节点列表并创建负载均衡器最终组装出Client内含Packer与Transporter。registry配置正是驱动createNodes()从服务中心如 Consul、Nacos拉取节点的来源这一点在测试用例 src/rpc-client/tests/AbstractServiceClientTest.php 中有明确验证模拟注册中心返回多个节点后createNodes()会生成对应数量的Node对象。配置复用通常来说一个服务消费者会同时消费多个服务提供者当通过服务中心来发现服务提供者时config/autoload/services.php配置文件内就可能会重复配置很多次registry配置。由于服务中心通常是统一的可以通过PHP 变量或循环等 PHP 代码来实现配置文件的生成。通过 PHP 变量生成配置?php $registry [ protocol consul, address http://127.0.0.1:8500, ]; return [ // 下面的 FooService 和 BarService 仅示例多服务并不是在文档示例中真实存在的 consumers [ [ name FooService, registry $registry, ], [ name BarService, registry $registry, ] ], ];通过循环生成配置?php return [ // 此处省略了其它同层级的配置 consumers value(function () { $consumers []; // 这里示例自动创建代理消费者类的配置形式顾存在 name 和 service 两个配置项这里的做法不是唯一的仅说明可以通过 PHP 代码来生成配置 // 下面的 FooServiceInterface 和 BarServiceInterface 仅示例多服务并不是在文档示例中真实存在的 $services [ FooService App\JsonRpc\FooServiceInterface::class, BarService App\JsonRpc\BarServiceInterface::class, ]; foreach ($services as $name $interface) { $consumers[] [ name $name, service $interface, registry [ protocol consul, address http://127.0.0.1:8500, ] ]; } return $consumers; }), ];由于services.php本身是 PHP 文件Hyperf 会以require方式加载因此可以在配置中直接使用变量、循环乃至value()闭包来动态生成消费者列表避免大量重复粘贴。返回 PHP 对象当框架导入symfony/serializer (^5.0)和symfony/property-access (^5.0)后并在dependencies.php中配置一下映射关系use Hyperf\Serializer\SerializerFactory; use Hyperf\Serializer\Serializer; return [ Hyperf\Contract\NormalizerInterface::class new SerializerFactory(Serializer::class), ];NormalizerInterface就会支持对象的序列化和反序列化。暂时不支持MathValue[]这种对象数组。定义返回对象?php declare(strict_types1); namespace App\JsonRpc; class MathValue { public $value; public function __construct($value) { $this-value $value; } }改写接口文件?php declare(strict_types1); namespace App\JsonRpc; interface CalculatorServiceInterface { public function sum(MathValue $v1, MathValue $v2): MathValue; }控制器中调用?php use Hyperf\Context\ApplicationContext; use App\JsonRpc\CalculatorServiceInterface; use App\JsonRpc\MathValue; $client ApplicationContext::getContainer()-get(CalculatorServiceInterface::class); /** var MathValue $result */ $result $client-sum(new MathValue(1), new MathValue(2)); var_dump($result-value);从源码角度看json-rpc组件提供了JsonRpcNormalizersrc/json-rpc/src/JsonRpcNormalizer.php作为NormalizerInterface的默认实现它负责在请求发出前把参数对象序列化、在响应回来后把数据反序列化为接口中声明的对象类型。测试目录中的IntegerValue存根类src/json-rpc/tests/Stub/IntegerValue.php即用于验证这类对象参数/返回值的传输链路。使用 JsonRpcPoolTransporter框架提供了基于连接池的Transporter可以有效避免高并发时建立过多连接的问题。可以通过替换JsonRpcTransporter的方式使用JsonRpcPoolTransporter。修改dependencies.php文件?php declare(strict_types1); use Hyperf\JsonRpc\JsonRpcPoolTransporter; use Hyperf\JsonRpc\JsonRpcTransporter; return [ JsonRpcTransporter::class JsonRpcPoolTransporter::class, ];两种 Transporter 的差异从源码中可以看得非常清楚JsonRpcTransportersrc/json-rpc/src/JsonRpcTransporter.php每次请求通过协程上下文缓存连接Context::has/Context::set实现协程内连接复用但高并发时仍可能为每个协程建立新连接JsonRpcPoolTransportersrc/json-rpc/src/JsonRpcPoolTransporter.php通过PoolFactory获取连接池连接用完通过defer()归还并内置了retry_count默认 2 次与retry_interval默认 100 毫秒的重试逻辑。特别地当异常码为SOCKET_ETIMEDOUT收包超时时不进行重试避免超时场景下的重复请求放大见 src/json-rpc/src/JsonRpcPoolTransporter.php。池化连接的相关参数min_connections、max_connections、connect_timeout、wait_timeout、heartbeat、max_idle_time在消费者配置的options.pool中定义其默认值与源码中$config属性一致src/json-rpc/src/JsonRpcPoolTransporter.php。对应地src/json-rpc/tests/JsonRpcPoolTransporterTest.php 中通过RpcPoolStub等存根验证了池化连接的获取与释放流程。附JSON-RPC 标准错误码在协议层面Hyperf 的ResponseBuilder严格遵循 JSON-RPC 2.0 规范定义了标准错误码见 src/json-rpc/src/ResponseBuilder.php错误码含义默认消息-32700解析错误Parse errorParse error.-32600无效请求Invalid requestInvalid request.-32601方法不存在Method not foundMethod not found.-32602无效参数Invalid paramsInvalid params.-32603内部错误Internal errorInternal error.-32000服务端错误Server error取异常消息服务端在请求体缺少jsonrpc、method、params字段、请求头content-type非application/json等场景下会自动构造对应错误响应错误码映射逻辑见 src/json-rpc/src/ResponseBuilder.php。理解这些错误码有助于在客户端侧快速定位调用失败的原因。总结至此你已经掌握了 Hyperf JSON-RPC 服务的完整链路通过#[RpcService]注解暴露服务并提供者通过 HTTP / TCPEOF 分隔或长度校验两种传输形态承载协议可选发布到 Consul / Nacos 服务中心实现服务注册与发现通过配置文件动态代理或继承AbstractServiceClient实现消费者配合 Symfony Serializer 实现 PHP 对象级参数传递再按需切换连接池化 Transporter 应对高并发场景。结合本文给出的源码路径你可以进一步深入阅读 src/json-rpc、src/rpc-server、src/rpc-client 中的实现与测试用例构建出属于自己的健壮微服务通信体系。赞分享后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载相关推荐Hyperf JSON-RPC 组件实战从服务提供者到消费者的完整微服务通信指南Hyperf JSON RPC 组件实战从服务提供者到消费者的完整微服务通信指南 导读 本文以 Hyperf 官方文档 docs/zh hk/json rpc后端微服务Hyperf JSON RPC 服务实战指南服务提供者、服务消费者与连接池传输的完整实现Hyperf JSON RPC 服务实战指南服务提供者、服务消费者与连接池传输的完整实现 JSON RPC 是一种基于 JSON 格式的轻量级 RPC 协议标后端Web框架微服务RPC框架异步编程Hyperf JSON RPC 服务实战指南服务提供者、服务消费者与多协议传输详解Hyperf JSON RPC 服务实战指南服务提供者、服务消费者与多协议传输详解 本指南以 docs/zh hk/json rpc.md https://l后端Web框架微服务RPC框架异步编程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑