本文目录导读:

在 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());
}
});
常见问题处理:
- CORS 配置:确保代理配置了正确的 CORS 头
- 超时设置:处理长连接的 gRPC 流式调用
- 错误处理:统一处理 gRPC 状态码
- 性能优化:考虑连接池和重试机制
生产环境建议:
// 生产环境配置
$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 编码即可,非常适合浏览器环境。