eiz/manageproto

Proto Files Manage

v1.0 2025-04-25 09:26 UTC

This package is auto-updated.

Last update: 2025-04-25 09:26:53 UTC


README

1. php 安装grpc & protobuf 扩展

以下按照 Alpine 容器为例

apk update

apk add --no-cache php-dev autoconf libtool make g++ grpc
# 安装 gRPC 扩展
pecl install grpc
# 安装 protobuf 扩展
pecl install protobuf

echo "extension=grpc.so" > /etc/php7/conf.d//grpc.ini
echo "extension=protobuf.so" > /etc/php7/conf.d/protobuf.ini
# 随后重启容器

# 查看扩展是否安装成
php -m

2.安装Consul服务

#去官网找需要下载的版本 直接替换 CONSUL_VERSION
wget https://releases.hashicorp.com/consul/${CONSUL_VERSION}/consul_${CONSUL_VERSION}_linux_amd64.zip
unzip consul_${CONSUL_VERSION}_linux_amd64.zip

3.服务项目中准备

composer require grpc/grpc --ignore-platform-reqs
composer require google/protobuf --ignore-platform-reqs

#eiz/manageproto 这个是所有服务的proto 以及 客户端服务端的统一管理
composer require eiz/manageproto --ignore-platform-reqs
#说明一下 咱们项目由于php版本偏低所以需要追加忽略版本的参数,否则代码运行会有检测版本的验证
CONSUL_URL=?        //配置consul服务地址和端口 默认:127.0.0.1:8500
SERVICE_NAME=?      //服务名称
SERVICE_ADDRESS=?   //服务地址 默认:127.0.0.1
SERVICE_PORT=?      //服务端口

4.新增grpc启动文件


<?php

require 'vendor/autoload.php';

use App\Grpc\FulfillmentGrpcService;
use App\Grpc\HealthGrpcService;
use Grpc\RpcServer;

$app = require __DIR__.'/../bootstrap/app.php';

$app->run();

$server = new RpcServer();

$server->addHttp2Port('0.0.0.0:10051');
$server->handle(new FulfillmentGrpcService()); //如果有多个服务可以增加 多个 handle
$server->handle(new HealthGrpcService());

echo "gRPC server running at 0.0.0.0:10051\n";

$server->run();

//在项目的public文件夹下创建启动文件 order_server.php

5.生成GRPC客户端&服务端代码

# --proto_path  指定了 proto 文件的查找路径,也就是当前目录(. 表示当前路径)。
# --php_out     这个参数指定了生成的 PHP 代码的输出目录。
# --grpc_out    这表示生成的 gRPC 代码(PHP 版)也会输出到指定目录中
# --plugin      用于指定 gRPC 插件的路径,生成 gRPC 代码时需要这个插件。
protoc --proto_path=. --php_out=../src --grpc_out=../src --plugin=protoc-gen-grpc=/d/works/demo_php_grpc/grpc_for_windows-master/grpc_for_windows-master/x64/grpc_php_plugin.exe ./Warehouse.proto

示例代码

//在bootstrap下的app.php新增一行代码
$app->register(Providers\ConsulServiceProvider::class);

//服务项目中创建 grpc.php 配置文件,配置该服务需要发现的客户端配置
// key-value 形式,key 指定的是 启动grpc服务注册到 consul中的服务,value指的是生成的grpc客户端类
return [
    "services" => [
        'catalog-service' => CatalogServiceClient::class,
        'order-service' => OrderServiceClient::class,
        'warehouse-service' => WarehouseServiceClient::class
    ]
];




//发现服务
    /**
     * 从 consul 中发现服务
     *
     * @param string $serviceName
     * @return array|null
     * @throws GuzzleException
     */

    public function discoverService(string $serviceName): ?array
    {
        try {
            $consulClient = new Client([
                'base_uri' => env('CONSUL_HOST', 'http://127.0.0.1:8500'),
            ]);
            $response = $consulClient->get("/v1/catalog/service/{$serviceName}");

            $services = json_decode($response->getBody(), true);

            if (count($services) > 0) {
                // 返回第一个可用的服务实例
                return $services[0];
            } else {
                writeLog('err', ['err' => "No services found for {$serviceName}"], 'etcd-err-log');
            }
        } catch (Exception $e) {
            writeLog('err', ['err' => "Failed to discover service: " . $e->getMessage()], 'etcd-err-log');
        }


        return null;
    }
    
    public function getServices(): stdClass
    {
        $services = config('grpc.services');
        $clients = new stdClass();
        foreach ($services as $serviceKey => $serviceClient) {
            $serviceConfig = $this->discoverService($serviceKey);
            if ($serviceConfig) {
                $serviceAddress = $serviceConfig['ServiceAddress'];
                $servicePort = $serviceConfig['ServicePort'];
                $serviceClientName = $this->ucFirst($serviceKey);
                $clients->$serviceClientName = new $serviceClient("{$serviceAddress}:{$servicePort}", [
                    'credentials' => ChannelCredentials::createInsecure(),
                ]);
            }
        }

        return $clients;
    }


    private function ucFirst($serviceName) {
        $serviceName = str_replace('-', ' ', $serviceName);
        $serviceName = ucwords($serviceName);
        return str_replace(' ', 'Grpc', $serviceName);
    }
    
    
    
    

调用示例:

$warehouseRequest = new \Warehouse\Request();
    $_reqData = [
        'warehouseId' => $fulfillment->warehouse_id,
        'productMap' => array_column($shippingProducts, 'stock', 'product_id'),
        'products' => $products,
        'type' => !empty($adjustStock) ? Constant::DECREMENT_STOCK_LOCK : Constant::DECREMENT,
        'extra' => [
            'reduceStock' => $reduceStock,
            'messageType' => Constant::DISPATCH_FULFILLMENT,
            'fulfillmentId' => $fulfillmentId,
            'salesRecordNumber' => $fulfillment->shipTo_ref ?? ''
        ]
    ];
    $warehouseRequest->setJsonData(json_encode($reqData));
    list($response, $status) = $this->clients->WarehouseGrpcService->reduceStock($warehouseRequest, $this->metadata)->wait();
    $logRes = $this->baseService->grpcResponse($response, $status);