gRPC 的 HTTP 代理网关
目录
介绍
imi v2.1.22 新加入的 gRPC HTTP 代理网关,作用是可以用 HTTP + JSON 的方式请求接口,而不用 Protobuf + HTTP2 来直接调用 gRPC。
其实这里可以画一个很厉害的架构图,但对使用没有什么帮助,就不放了。
服务端
proto 文件生成 PHP 代码
这一步和 gRPC 服务开发一样,就不再赘述。
配置 gRPC 连接池
因为我们服务端是代理网关,其实就是作为客户端去连服务端,所以要配置连接池。
可参考 gRPC 服务开发。
绑定 gRPC 接口(非必须)
如果你的 gRPC 接口是跟代理同一个服务,那么 imi 会帮你做好绑定,不需要做这一步。
如果你是代理外部服务的 gRPC 接口,此步骤是必须的。
在 @app.beans
中配置:
[
'GrpcInterfaceManager' => [
// 绑定的服务接口
'binds' => [
\Grpc\AuthServiceInterface::class, // 这里换成你自己的
],
],
]
编写代理接口
就是写一个 HTTP 接口,没什么多说的,直接上代码:
<?php
declare(strict_types=1);
namespace GrpcApp\GrpcServer\Controller;
use Imi\Aop\Annotation\Inject;
use Imi\Controller\HttpController;
use Imi\Grpc\Proxy\Http\GrpcHttpProxy;
use Imi\Server\Http\Route\Annotation\Action;
use Imi\Server\Http\Route\Annotation\Controller;
use Imi\Server\Http\Route\Annotation\Route;
/**
* @Controller("/proxy/")
*/
class ProxyController extends HttpController
{
/**
* @Inject("GrpcHttpProxy")
*/
protected GrpcHttpProxy $grpcHttpProxy;
/**
* @Action
* @Route("grpc/{service}/{method}")
*
* @return mixed
*/
public function proxy(string $service, string $method)
{
// 这里的 grpc 是连接池名称,换成你自己的
return $this->grpcHttpProxy->proxy('grpc', $this->request, $this->response, $service, $method);
}
}
@Controller("/proxy/")
和@Route("grpc/{service}/{method}")
的路由都是可以自己定义的,这里仅仅作为演示用。
客户端
测试
这里拿 gRPC 服务开发中的示例来测试:
curl --location --request POST -X POST "http://127.0.0.1:8080/proxy/grpc/grpc.AuthService/login" \
--header 'Content-Type: application/json' \
--data '{
"phone": "12345678901",
"password": "123456"
}'
返回:
{
"success": true,
"error": ""
}
使用 Protobuf 的 gRPC HTTP 网关客户端
类名:\Imi\Grpc\Proxy\Http\GrpcHttpClient
使用:
use Imi\Grpc\Proxy\Http\GrpcHttpClient;
$client = new GrpcHttpClient('http://127.0.0.1:8080/proxy/grpc');
$request = new TestRequest();
$request->setInt(123);
$responseMessage = $client->request('grpc.TestService', 'test', $request, TestRequest::class, ['grpc-test' => 'abc'], $responseMetadata, $response);
var_dump($responseMessage); // 响应对象
var_dump($responseMetadata); // metadata
$response; // YurunHttp 响应对象,一般无用
注意事项
- 如何传请求参数
请求参数可以是 GET、POST、JSON 数据,只要 imi 能正常接收就可以。
推荐使用 JSON,可以支持复杂数据结构。
- 请求参数格式
下面给个示例,可以参考,几乎覆盖了绝大部分常见类型
proto:
syntax = "proto3";
package grpc;
import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/field_mask.proto";
option php_generic_services = true;
service AuthService {
rpc Login (LoginRequest) returns (LoginResponse);
}
message LoginRequest {
string phone = 1; // 手机号
string password = 2; // 密码
}
message LoginResponse {
bool success = 1; // 是否成功
string error = 2; // 错误信息
}
service TestService {
rpc Test (TestRequest) returns (TestRequest);
}
enum Test {
A = 0;
B = 2;
}
message TestRequest {
int32 int = 1;
string string = 2;
LoginRequest message = 3;
repeated LoginRequest messages = 4;
google.protobuf.Any any = 5;
map<int32, string> map = 6;
map<string, LoginRequest> map2 = 7;
repeated google.protobuf.Any anys = 8;
Test enum = 9;
bool bool = 10;
google.protobuf.Timestamp timestamp = 11;
google.protobuf.Duration duration = 12;
google.protobuf.Struct struct = 13;
google.protobuf.FieldMask fieldMask = 14;
repeated string strings = 15;
}
JSON 请求参数:
{
"int": 1,
"string": "abc",
"strings": [
"a",
"b"
],
"message": {
"phone": "114514",
"password": "123456"
},
"messages": [
{
"phone": "1",
"password": "11"
},
{
"phone": "2",
"password": "22"
}
],
"any": {
"@type": "type.googleapis.com\/grpc.LoginRequest",
"phone": "114514",
"password": "123"
},
"map": {
"11": "aa",
"22": "bb"
},
"map2": {
"a": {
"phone": "1",
"password": "11"
},
"b": {
"phone": "2",
"password": "22"
}
},
"anys": [
{
"@type": "type.googleapis.com\/grpc.LoginRequest",
"phone": "114514",
"password": "123"
}
],
"enum": 2,
"bool": true,
"timestamp": "2018-06-21T04:00:00Z",
"duration": "1s",
"struct": {
"null": null,
"number": 3.14,
"string": "abc",
"bool": true,
"struct": {
"id": 1,
"name": "imi"
},
"list1": [
1,
2,
3
],
"list2": [
{
"id": 1,
"name": "imi"
}
]
},
"fieldMask": "abc.def"
}
- Metadata
以 grpc-
开头的请求头,都被当做 metadata 传给 gRPC 服务端。
响应头同理。