当前位置:首页>php>高性能通信利器!在PHP Hyperf 框架中实现 gRPC 客户端与服务端进行通信

高性能通信利器!在PHP Hyperf 框架中实现 gRPC 客户端与服务端进行通信

  • 2026-10-21 13:26:29
高性能通信利器!在PHP Hyperf 框架中实现 gRPC 客户端与服务端进行通信

前言:在微服务架构中,高性能的 RPC 通信是核心。gRPC 凭借其基于 HTTP/2 的传输协议和 Protobuf 的高效序列化,成为了跨语言服务的首选。这边文章是基于 Hyperf 协程框架,演示如何快速接入 gRPC 客户端,并实现与服务端的无缝通信。这边文章做了一个最简单的demo, 就是PHP作为客户端调用gRPC服务端, 传什么数据过去就返回什么数据


一、 定义服务契约 (Proto)

gRPC 是“契约先行”的。我们先创建一个 proto 文件来定义通信协议。

在对应的项目目录新建 proto/echo.proto文件:

syntax = "proto3";package echo;// 请求参数message EchoRequest {  string message = 1;  map<string, string> metadata = 2;}// 响应参数message EchoResponse {  string message = 1;  map<string, string> metadata = 2;  int64 timestamp = 3;}// 定义服务service Echo {  rpc Echo(EchoRequest) returns (EchoResponse);}

服务端部分

一、Rust 实现的服务端

1.1 初始化项目

首先创建 Rust 项目并添加依赖。核心依赖如下:

# Cargo.toml[package]name = "grpc-echo-server"version = "0.1.0"edition = "2021"[dependencies]# gRPC 框架tonic = "0.9"prost = "0.11"# 异步运行时tokio = { version = "1", features = ["macros", "rt-multi-thread"] }# 时间处理chrono = "0.4"[build-dependencies]# Proto 编译器tonic-build = "0.9"

1.2 配置 Proto 编译

在项目目录创建 build.rs,告诉 Cargo 在编译前自动处理 Proto 文件:

// build.rsfnmain() -> Result<(), Box<dynstd::error::Error>> {    // 编译 proto 文件,生成的代码会放在 OUT_DIR    tonic_build::compile_protos("proto/echo.proto")?;    Ok(())}

这样每次 cargo build 时,Proto 文件会自动编译成 Rust 代码,不需要手动操作。

1.3 实现 Echo 服务

现在来写核心逻辑。创建 src/services/echo.rs:

use tonic::{Request, Response, Status};use chrono::Utc;// 引入由 tonic-build 生成的代码pub mod echo {    tonic::include_proto!("echo");}use echo::echo_server::{Echo, EchoServer};use echo::{EchoRequest, EchoResponse};/// Echo服务实现 - 传什么返回什么#[derive(Debug, Default)]pub struct EchoService;impl EchoService {    pub fn new() -> Self {        Self    }    /// 创建 gRPC 服务    pub fn into_service(self) -> EchoServer<Self> {        EchoServer::new(self)    }}#[tonic::async_trait]impl Echo for EchoService {    async fn echo(        &self,        request: Request<EchoRequest>,    ) -> Result<Response<EchoResponse>, Status> {        let req = request.into_inner();        // 获取当前时间戳(毫秒)        let timestamp = Utc::now().timestamp_millis();        // 原样返回请求内容        let response = EchoResponse {            message: req.message,            metadata: req.metadata,            timestamp,        };        Ok(Response::new(response))    }}

在创建 src/services/mod.rs:

pub mod echo;pub use echo::EchoService;

主要用于声明和管理模块结构

1.4 启动服务

最后,在 src/main.rs 中把服务跑起来:

mod services;use tonic::transport::Server;use std::net::SocketAddr;use services::EchoService;#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> {    // 监听所有网卡,方便 Docker 部署    let addr: SocketAddr = "0.0.0.0:50051".parse()?;    println!("🚀 gRPC 服务启动: {}", addr);    println!("📡 可用服务: echo.Echo");    // 创建 Echo 服务    let echo_service = EchoService::new();    // 启动 gRPC 服务    Server::builder()        .add_service(echo_service.into_service())        .serve(addr)        .await?;    Ok(())}

运行 cargo run,服务就跑起来了!

$ cargo run🚀 gRPC 服务启动: 0.0.0.0:50051📡 可用服务: echo.Echo

客户端部分

一、 环境准备

在开始之前,我们需要安装必要的扩展和依赖。由于 Protobuf 需要编译支持,我们以 Docker 环境(Alpine 镜像)为例:

1. 安装系统依赖

首先,确保系统安装了 protobuf 编译器:

apk add --no-cache protobuf protobuf-dev php81-pecl-protobuf

2. 安装组件库

在 Hyperf 项目根目录下运行:

# 安装 gRPC 客户端组件composer require hyperf/grpc-client# 安装 Google Protobuf 核心库composer require google/protobuf

验证安装是否成功:

protoc --version

二、 生成 PHP 代码与自动加载

1. 生成代码

使用 protoc 工具将 .proto 文件转换为 PHP 类文件。执行以下命令:

mkdir -p ./app/Grpcprotoc --proto_path=./proto --php_out=./app/Grpc ./proto/echo.proto

2. 配置自动加载

生成的代码通常带有命名空间(如 PBEcho),我们需要在 composer.json 中注册它:

"autoload": {    "psr-4": {        "App\\": "app/",        "PBEcho\\": "app/Grpc/PBEcho/"    }}

注意:路径和命名空间需根据你生成的实际目录调整。

执行命令使配置生效:

composer dump-autoload

三、 依赖注入配置 (DI)

在 Hyperf 中,我们通过 dependencies.php 来管理客户端实例。这里有一个关键细节:不要在闭包内使用 make(),否则会引起无限递归。

修改 config/autoload/dependencies.php:

use App\Grpc\EchoClient;return [    EchoClient::class => function () {        // gRPC 服务端地址        $host = 'x.x.x.x';        $port = 'xxxx';        // 直接实例化,避免使用 make() 造成递归调用        return new EchoClient("{$host}:{$port}");    },];

四、 业务逻辑实现

现在,我们可以在 Controller 中轻松调用 gRPC 服务了。Hyperf 的 gRPC 客户端返回的是一个数组:[$reply, $status, $rawResponse]。

/** * Echo 服务测试接口 */public function echo(): array{    $message = $this->request->input('message', 'Hello from Hyperf!');    try {        // 1. 创建 gRPC 请求对象        $request = new \PBEcho\EchoRequest();        $request->setMessage($message);        $request->setMetadata([            'client' => 'hyperf',             'time' => date('Y-m-d H:i:s')        ]);        // 2. 调用服务        // _simpleRequest 是基类提供的方法,返回 [响应对象, 状态码, 原始响应]        [$reply, $status, $rawResponse] = $this->echoClient->echo($request);        // 3. 检查状态 (0 表示成功)        if($status !== 0) {            return [                'success' => false,                'error' => is_string($reply) ? $reply : 'gRPC 调用失败',                'status' => $status,            ];        }        /** @var \PBEcho\EchoResponse $reply */        $metadata = $reply->getMetadata();        return [            'success' => true,            'data' => [                'message' => $reply->getMessage(),                'metadata' => $metadata ? iterator_to_array($metadata) : [],                'timestamp' => $reply->getTimestamp(),            ],        ];    } catch(\Throwable $e) {        return [            'success' => false,            'error' => $e->getMessage(),            'status' => -1,        ];    }}

结语:通过以上步骤,我们成功在 Hyperf 中打通了 gRPC 的通信链路。gRPC 不仅带来了更强的类型约束,更在性能上为 PHP 赋能。有兴趣的话快去你的项目中试试吧!

最新文章

随机文章