hetao29/php-grpc-server-protobuf

使用 protobuf 的 php grpc 服务器框架,不使用任何第三方库。

1.7.5 2024-04-24 04:04 UTC

This package is auto-updated.

Last update: 2024-09-24 04:56:02 UTC


README

使用 protobuf 的 php grpc 服务器框架,不使用任何第三方库或使用 Swoole。支持 protobuf 和 json 请求。

架构

与 nginx & php-fpm 一起使用的方式(仅支持 php 客户端和 http json 请求)

  1. gRPC 客户端 > nginx > php-fpm > 此框架 > 自定义服务 > protobuf 二进制响应
  2. http/https json 请求(content-type:application/json) > nginx > php-fpm > 此框架 > 自定义服务 > json 响应

与 swoole 一起使用的方式(所有 gRPC 客户端和 http json 请求)

  1. gRPC 客户端 > Swoole > 此框架 > 自定义服务 > protobuf 二进制响应
  2. http/https json 请求(content-type:application/json) > Swoole > 此框架 > 自定义服务 > json 响应

使用方法

  1. 使用 composer 安装
composer require "hetao29/php-grpc-server-protobuf:dev-main"
  1. 在 php 文件中使用,如 samples/www/index.php(php-fpm 模式)
<?php
define("ROOT",				dirname(__FILE__)."/../");
define("ROOT_LIBS",			ROOT."/libs");
define("ROOT_APP",			ROOT."/app");
define("ROOT_PROTO_GENERATED",		ROOT."/proto_generated");
require_once(ROOT_LIBS."/vendor/autoload.php");
spl_autoload_register(function($class){
	$root = ROOT_PROTO_GENERATED."/".str_replace("\\","/",$class).".php";
	if(is_file($root)){
		require_once($root);
	}
});
spl_autoload_register(function($class){
	$root = ROOT_APP."/".str_replace("\\","/",$class).".php";
	if(is_file($root)){
		require_once($root);
	}
});

try{
	$content_type = (isset($_SERVER['HTTP_CONTENT_TYPE']) && $_SERVER['HTTP_CONTENT_TYPE']=='application/json') ? 'json' : null; //json | null (default)
	if(($r=GRpcServer::run(null,null,$content_type))!==false){
		echo($r);
	}
}catch(Exception $e){
	print_r($e);
}
  1. 或 swoole 服务器
<?php
require __DIR__ . '/../libs/vendor/autoload.php';

define("ROOT",				__DIR__."/../");
define("ROOT_APP",			__DIR__."/../app");
define("ROOT_PROTO_GENERATED",		__DIR__."/../proto_generated/");
spl_autoload_register(function($class){
	$root = ROOT_PROTO_GENERATED."/".str_replace("\\","/",$class).".php";
	if(is_file($root)){
		require_once($root);
	}
});
spl_autoload_register(function($class){
	$root = ROOT_APP."/".str_replace("\\","/",$class).".php";
	if(is_file($root)){
		require_once($root);
	}
});


$http = new Swoole\Http\Server('0.0.0.0', 50000, SWOOLE_BASE);

$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($http) {
	$content_type = (isset($request->header['content-type']) && $request->header['content-type']=='application/json') ? 'json' : null; //json | null (default)
	if($content_type=="json"){
		$response->header('content-type', 'application/json');
	}else{
		$response->header('content-type', 'application/grpc');
	}
	try{
		if(($r=GRpcServer::run($request->server['request_uri'], $request->rawContent(), $content_type))!==false){
			//echo($r);
			$response->header('trailer', 'grpc-status, grpc-message');
			$trailer = [
				"grpc-status" => "0",
				"grpc-message" => ""
			];
			foreach ($trailer as $trailer_name => $trailer_value) {
				$response->trailer($trailer_name, $trailer_value);
			}
			$response->end($r);
	}
	}catch(Exception $e){
		$response->header('trailer', 'grpc-status, grpc-message');
		$trailer = [
			"grpc-status" => $e->getCode(),
			"grpc-message" => $e->getMessage(),
		];
		foreach ($trailer as $trailer_name => $trailer_value) {
			$response->trailer($trailer_name, $trailer_value);
		}
		$response->end();
	}
});
$http->start();

编写应用程序服务

  1. 将 proto 和 genproto 转换为 php 文件
cd proto && make
  1. 在 services 目录中编写 gRPC 服务器,例如 helloworld
<?php
namespace Test\Helloworld;
class Greeter implements GreeterInterface{
	/**
	 */
	public function SayHello(HelloRequest $request) : HelloReply{
		$reply = new HelloReply();
		$reply->setMessage("Hello2, ".$request->getName()."!");
		return $reply;
	}
}
  1. 配置 nginx 和 php-fpm
server {
	listen 50010 http2; #with http2 is grpc protocol
	#listen 50010; #without http2 is json protocol
	root /data/server/;
	location / {
		if (!-e $request_filename){
			rewrite ^/(.+?)$ /index.php last;
		}
	}
	location ~ \.php$ {
		fastcgi_param REMOTE_ADDR $http_x_real_ip;
		fastcgi_pass   127.0.0.1:9000;
		fastcgi_index  index.php;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		include        fastcgi_params;
	}
}
  1. 测试
# cli mode
php client/hello.php

# curl command (json request)
# swoole (50000 port)
curl -d '{"name":"xx"}' -v http://127.0.0.1:50000//Test.Helloworld.Greeter/SayHello -H "content-type:application/json"
# nginx php-fpm (50010 port)
curl -d '{"name":"xx"}' -v http://127.0.0.1:50010//Test.Helloworld.Greeter/SayHello -H "content-type:application/json"

# or web browser