Jump to Navigation

go实现gRPC的服务端的PHP扩展

go实现gRPC的服务端的PHP扩展

一种支持PHP语言编写gRPC服务端的方案

在有这个方案的想法时,并不确定技术上是否可行,只是觉得可行性程度挺高。

为了确认这可行性,先行做了些测试,所以先把技术点的可行性放在了文章的开头部分。

方案的设计将在总结可行性的基础上做详细说明,从本文的第二部分开始。

可行性验证

需要验证的主要有,

  • 从PHP调用go函数
  • 从go调用PHP函数
  • 把go代码编译为库
  • 把go库链接进PHP扩展
PHP调用go函数

上篇已经验证并实现。

用go实现PHP扩展

上篇已经验证并实现。

go调用PHP函数

如果需要go调用PHP函数,其实先要确定的是用C能够调用PHP函数。

好在我们有zend_API.h中的两个函数:

  • call_user_function
  • call_user_function_ex

而且比较成熟,不再多说明了。

接下来,再用go调用C函数,这是在cgo中支持的,也非常成熟,并且测试可用。

这么说,只需要实现go版本的call_user_function(_ex)即可。

if (call_user_function(CG(function_table), cobj , &function_name,
                       &retval_ptr, argc, NULL TSRMLS_CC) == SUCCESS) {
}

这是主要代码,给定适当的参数,就可直接调用了。而实现实现时可能会有调整,比较封装一个参数更少的call_user_function函数,并且参数的类型为go类型的的函数。

完整源代码,

https://git.mydomain.com/liuguangzhao/atapi/blob/master/src/shrpc/zend.go

https://git.mydomain.com/liuguangzhao/atapi/blob/master/src/shrpc/szend.c

把go代码编译为库

在go 1.5中,go命令行工具引入了-buildmode参数,让go代码能够编译为库的形式,提供给其他的go代码使用,或者是其他语言使用。

如果没有这个参数的支持,这个验证工作难度非常大,并且有可能不可行。

所以,我们需要go 1.5+版本编译相关的代码。

-buildmode的值有4个,分别是c-archive, c-shared, archive, shared。

其中带c-前缀的两个参数值,编译的结果可以为C语言使用,即兼容C生成的共享库.so或者静态库.a。

不带前缀的两个参数值,编译生成的结果只可用于其他的go代码调用。

另外,还有一个参数-linkshared,用于确定是否是可重定位的符号,有一点像C中的-fPIC参数。

一行示例,

go build  -linkshared -buildmode=c-archive -o lib/libgoshrpc.a shrpc

这样生成的libgoshrpc.a可以链接进C代码的程序中了。

设计方案

主要使用go语言,以及现有gRPC-go代码,转变为PHP扩展,供PHP层次调用,解决官方gRPC-PHP实现不完善的问题。

这个方案,优点是完全有希望支持gRPC-go的大多数功能,至少是能够编写gRPC服务端的功能的。

  • 在方案结果上,该方案会产出一个PHP扩展.so,或者外加几个封装用的.php类。
  • 在部署上,与部署其他的PHP扩展完全一致,无需额外处理。
  • 在开发上,PHP开发工程师与使用普通的PHP扩展函数一样。
  • 对于实现,相比直接采用gRPC的C API封装,工作量小很多。相比在官方php-grpc扩展基础上查找bug,完善功能,应该工作量也会小一点。

从这几点看,最终效果应该还是非常好的,因为系统涉及到的多方工程师都可以保持现有的使用方式,无需额外处理。

系统设计V1

用go实现一个通道gRPC服务,php2rpc,承载通信信道的功能。

这个服务将编译进PHP扩展中,并且一旦稳定,不需要任何改动。避免频繁发布.so扩展,重启服务。

用PHP实现一个客户端封装,把服务打包进php2rpc,传递到服务端。

用PHP实现一个服务端封装,从php2rpc服务接收参数,解析并分发到不同的真正的服务函数上。

用PHP实现一个服务端封装,负责模拟注册过程,生成分发需要的元信息。

本设计只支持gRPC中的非stream类型服务,但是可以选择使用长连接或者短连接。

本设计只支持PHP封装的客户端,如果要用go调用,则还需要对go做一个封装。

注:封装部分还需要看看是用PHP还是用go呢?

系统设计V2

支持从.proto生成部分代码,简化使用PHP实现gRPC服务,简化使用PHP调用gRPC服务。

在API层次上,实现与官方提供的PHP版本更兼容的API。或者与现在官方提供的PHP层次整合。

在开发流程上,实现与官方go版本类似的流程,即流程合并兼容。

支持PHP与go两者开发的服务端与客户相互兼容调用。

试验支持客户端异步调用。

实现计划

初步计划,传递过程中参数用json字符串,服务函数或者方法接收一个关联数组参数,从json解码出来的。

在服务端注册的时候,类似这么一行,register(string $api_name, string $api_version, callable $api_svc_func);

注意,$api_svc_func是一个callable变量即可,比如,可以是普通的PHP函数,

可以是一个PHP匿名函数,可以是一个对象方法。

并且,服务端注册时,并不需要指定参数,因为默认情况下只一个关系数组参数。

客户端调用时,类似这么一行,call(string $api_name, string $api_version, array $args);

call方法是同步执行,阻塞并等待服务端的响应。

备注

虽说主要是用的go和PHP,但仍还需要使用一点C语言,C语言的作用是不同语言之间的桥梁。

为了尽量避免引入大问题,尽量保持C代码功能简洁,不提供功能性处理逻辑。

这种实现方式还涉及到编译相关的高级参数,而不是go build命令就能成的,编译稍微复杂。

由于gRPC-PHP官方支持不够完善,并且为适应PHP快速开发服务的目的,提出这么一种涉及流程长,涉及技术点比较多的方案,可能有很多不完善,希望多提意见帮助完善。

本方案是暂时取代官方的gRPC-PHP实现的,代码级上完全不兼容,但使用模式将保持与官方尽量一致。

评论

good

添加新评论

Plain text

  • 不允许HTML标记。
  • 自动将网址与电子邮件地址转变为链接。
  • 自动断行和分段。
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.


Main menu 2

Story | by Dr. Radut