PHP搞微服务杂谈(二) 如何实现GRPC服务端流式响应

拉点家常

大家好,我是Crayxn。今天跟大家聊下如何用PHP实现GRPC服务端流模式响应,这也是我这一周的研究课题。原本第二章是计划跟大家分享如何实现 同GO一样的GRPC服务反射的,但想着趁热打铁就先跟大家分享今天的这一课题。

研究的起因

前面系列博文有讲过我们团队采用go+php并行开发,go使用的是 kratos框架,当调用服务会先检查服务健康,然后就是调php这边的服务时不时报 … but health check failed … 。是的让人难受,检查了下 GRPC Health Check 是没问题的,那么问题就应该是出在 Watch了。是的,Watch没有实现定义的 Server Streaming 模式(关于GRPC的四种模式,不了解的自行谷歌哈),也就是客户端只能接收到一次响应并被动关闭。正确应该是能收到多个响应,并一直保持连接到主动关闭或者数据流结束。

开始研究

好的知道问题后,就翻阅了Hyperf(实现GRPC使用的是Hyperf)文档、源码,并没有。暂时只能找到的只要支持流模式的客户端(哭泣.jpg)。Swoole开发群里也问了,没有大佬回复,只有铭昕大佬丢过来个hyper/grpc-client仓库地址,很感谢回复但无用。后面又翻阅Swoole的各种,没错也没找到(大声哭泣.jpg)。但发现了这个 wiki.swoole.com/#/http_server?id=d...

这就意味着 我们可以自己向客户端发送报文。难点在于GRPC使用的是HTTP/2,并非像文档中给到的HTTP/1.1可以明文发送。因此要做的事情就是将 Response 打包再发送。

开搞

首先实现Message打包,将 Protobuf Message 对象转 String 再打包数据

public static function pack(string $data): string
{
    return pack('CN', 0, strlen($data)) . $data;
}

再实现把数据 转换为HTTP/2二进制帧和数据(我爱GPT)

private function frame(string $data, int $type, int $flags, int $stream = 0): string
{
    return (substr(pack("NccN", strlen($data), $type, $flags, $stream), 1) . $data);
}

最后发送

...
$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) {

    $response = new HealthCheckResponse();

    while (1) {
        if(!$http->send($response->fd, frame(pack($rsponse->serializeToString()), 0x00, 0x00, $request->streamId))){
            break;
        }
        sleep(2);
    }
});
...

好的 到这一步 已经成功了一半,客户端能正常接收到数据了。到Postman 试一下,不出意外Responses窗口没有打印想得到的数据,这是因为没有头部帧,根本不认识你这流是个啥。这里说明下HTTP/2数据流的结构,如下图(可能是这样):

好的,那就实现它。

...
// 第一步 将头部压缩,感谢 amphp/hpack
$compressedHeaders = (new HPack())->encode($headers);
// 第二步 打包
$headFrame = frame($compressedHeaders, 0x1, 0x04, $request->streamId)
// 第三步 发送
$http->send($response->fd, $headFrame)

总结

最后,再实现到Hyperf框架中使用。上述只是将关键代码 贴出,具体如何实现大家可以看下插件github.com/crayxn/hyperf-grpc ,具体位置在 github.com/crayxn/hyperf-grpc/blob...

如果对您有帮助,还请给个星星哦,这是我前进的动力,谢谢。

最后贴出已经实现 /grpc.health.v1.Health/Watch 流模式的截图,正常是可以收到服务端的多次 Response 并且主动关闭 Cancel 才结束。

本作品采用《CC 协议》,转载必须注明作者和本文链接
from crayxn github.com/crayxn
本帖由系统于 9个月前 自动加精
讨论数量: 7
CodingHePing

大佬公司现在什么技术栈啊

9个月前 评论
CodingHePing (作者) 9个月前
Crayxn (楼主) 9个月前
CodingHePing (作者) 9个月前
Crayxn (楼主) 9个月前
梦想星辰大海

大佬你这个包好使不,我打算用到生产?

8个月前 评论
Crayxn (楼主) 8个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!