PHP 怎么用gRPC-Web

wen PHP项目 2

本文目录导读:

PHP 怎么用gRPC-Web

  1. 使用 gRPC-Web 代理方案
  2. PHP 客户端实现
  3. 完整的现代 PHP 项目示例
  4. 配置和注意事项

在 PHP 中使用 gRPC-Web 主要有两种方式,我来详细说明:

使用 gRPC-Web 代理方案

这是最常用的方式,通过代理将 gRPC-Web 请求转换为标准 gRPC 请求。

1 搭建 gRPC 服务端

创建标准的 gRPC 服务:

// proto/helloworld.proto
syntax = "proto3";
package helloworld;
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
  string name = 1;
}
message HelloReply {
  string message = 1;
}

生成 PHP 代码:

protoc --php_out=generated --grpc_out=generated --plugin=protoc-gen-grpc=/path/to/grpc_php_plugin helloworld.proto

创建 gRPC 服务端:

// server.php
require 'vendor/autoload.php';
use Grpc\Server;
use Helloworld\GreeterInterface;
use Helloworld\HelloReply;
class GreeterService implements GreeterInterface {
    public function SayHello(\Helloworld\HelloRequest $request, \Grpc\ServerContext $context): \Helloworld\HelloReply {
        $reply = new HelloReply();
        $reply->setMessage("Hello " . $request->getName());
        return $reply;
    }
}
$server = new Server();
$server->addHttp2Port('0.0.0.0:50051');
$server->register(GreeterService::class, new GreeterService());
$server->run();

2 配置代理

使用 Envoy 或 grpc-web 代理:

Envoy 配置示例:

# envoy.yaml
static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address: { address: 0.0.0.0, port_value: 8080 }
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                codec_type: AUTO
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"]
                      routes:
                        - match: { prefix: "/" }
                          route:
                            cluster: greeter_service
                            timeout: 0s
                            max_stream_duration:
                              grpc_timeout_header_max: 0s
                  cors:
                    allow_origin_string_match:
                      - prefix: "*"
                    allow_methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
                    allow_headers: keep-alive, user-agent, cache-control, content-type, content-transfer-encoding, custom-header-1, x-accept-content-transfer-encoding, x-accept-response-streaming, x-user-agent, x-grpc-web
                    expose_headers: grpc-status, grpc-message
                http_filters:
                  - name: envoy.filters.http.grpc_web
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                http_protocol_options:
                  accept_http10: true
  clusters:
    - name: greeter_service
      connect_timeout: 0.25s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      typed_extension_protocol_options:
        envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
          "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
          explicit_http_config:
            http2_protocol_options: {}
      load_assignment:
        cluster_name: greeter_service
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address: { address: 127.0.0.1, port_value: 50051 }

PHP 客户端实现

1 使用 grpc-web-client 库

composer require grpc/grpc-php
composer require grpc-web/grpc-web-client
// client.php
require 'vendor/autoload.php';
use Helloworld\HelloRequest;
use Helloworld\GreeterClient;
use Grpc\ChannelCredentials;
use GrpcWeb\Client as GrpcWebClient;
// 创建 gRPC 客户端
$client = new GreeterClient('localhost:8080', [
    'credentials' => ChannelCredentials::createInsecure(),
]);
// 创建请求
$request = new HelloRequest();
$request->setName('World');
// 调用服务
$response = $client->SayHello($request);
// 处理响应
if ($response->getCode() === \Grpc\STATUS_OK) {
    echo $response->getMessage() . "\n";
} else {
    echo "Error: " . $response->getMessage() . "\n";
}

2 使用 cURL 或 HTTP 客户端

如果你需要更直接的控制,可以使用 cURL:

function callGrpcWeb($method, $body) {
    $url = 'http://localhost:8080/' . $method;
    $ch = curl_init($url);
    // 设置请求头
    $headers = [
        'Content-Type: application/grpc-web+proto',
        'X-Grpc-Web-Encoding: base64',
        'X-Grpc-Time-Out: 10000',
    ];
    // 创建 protobuf 消息
    $request = new HelloRequest();
    $request->setName('World');
    $binaryBody = $request->serializeToString();
    // 编码为 base64
    $encodedBody = base64_encode($binaryBody);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $encodedBody);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // 执行请求
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($httpCode === 200) {
        // 解码响应
        $decodedResponse = base64_decode($response);
        // 解析响应消息
        $reply = new HelloReply();
        $reply->mergeFromString($decodedResponse);
        return $reply->getMessage();
    }
    return null;
}
// 调用示例
echo callGrpcWeb('/helloworld.Greeter/SayHello', '');

完整的现代 PHP 项目示例

使用 PHP 8+ 和现代框架的完整示例:

// src/GrpcClient.php
namespace App\Services;
use Grpc\ChannelCredentials;
use Helloworld\GreeterClient;
use Helloworld\HelloRequest;
class GrpcClientService {
    private $client;
    public function __construct($endpoint = 'localhost:8080') {
        $this->client = new GreeterClient($endpoint, [
            'credentials' => ChannelCredentials::createInsecure(),
        ]);
    }
    public function sayHello($name) {
        $request = new HelloRequest();
        $request->setName($name);
        list($response, $status) = $this->client->SayHello($request)->wait();
        if ($status->code !== \Grpc\STATUS_OK) {
            throw new \Exception('gRPC call failed: ' . $status->details);
        }
        return $response->getMessage();
    }
}
// 使用示例
try {
    $grpcService = new GrpcClientService();
    $message = $grpcService->sayHello('PHP User');
    echo $message; // 输出: Hello PHP User
} catch (\Exception $e) {
    echo 'Error: ' . $e->getMessage();
}

配置和注意事项

前端集成示例:

// 前端 JavaScript 使用
const client = new GreeterClient('http://localhost:8080');
const request = new HelloRequest();
request.setName('Web User');
client.sayHello(request, {}, (err, response) => {
    if (response) {
        console.log('Response:', response.getMessage());
    }
});

常见问题处理:

  1. CORS 配置:确保代理配置了正确的 CORS 头
  2. 超时设置:处理长连接的 gRPC 流式调用
  3. 错误处理:统一处理 gRPC 状态码
  4. 性能优化:考虑连接池和重试机制

生产环境建议:

// 生产环境配置
$config = [
    'endpoint' => 'grpc-backend:50051',
    'credentials' => [
        'call_injector' => [
            'grpc.trace_flag',
            'grpc.service_name_dns' => 'my-service',
        ],
    ],
    'retry' => [
        'max_retries' => 3,
        'backoff' => 1000,
    ],
];
$client = new GreeterClient($config['endpoint'], $config);

这就是在 PHP 中使用 gRPC-Web 的完整方案,主要优势是客户端不需要安装特殊的 gRPC 扩展,只需要 HTTP/1.1 和 base64 编码即可,非常适合浏览器环境。

抱歉,评论功能暂时关闭!