Laravel 整合 gRPC (ft. Node.js)

tags: PHP Laravel RPC Node.js Protocol buffer
category: Back-End
description: Laravel 整合 gRPC (ft. Node.js)
created_at: 2021/08/28 19:00:00

cover image


前言

很久沒有發新的文章了,這次把最近做到花很多時間的東西紀錄一下,避免之後要做又要花很多時間重來。

雖然說文件很多,但也相對很散,官方上跟 Github 上不見得同步,需要東串西串,PHP+gRPC 網路上也滿多都是2019年左右的資料,不見得動的起來,Laravel的話資料更是少。

題外話: 花很多時間有很大的原因是 純Windows不友善

稍微描述一下: 官方github上有提供PHP範例原始碼,是已經經過 protoc 編譯過的,而php在compile的時候需要帶一個plugin才會產生client,而那個plugin只能在unix上build,windows雖然網路上有人提供.exe,但不是很敢使用。

所以這邊主要是 windows 上開發的解決方案,在 WSL 中只會安裝編譯需要的東西,並不會安裝PHP執行的套件(ex: extension)


事前準備


準備開始

Laravel 專案有他自己的目錄結構,但是 protobuf 你要丟哪邊都可以,反正最後要經過 protoc 編譯,再讓 composer 去幫你做 autoload,所以路徑對的到就沒問題。

假設我建立資料夾在根目錄 /protobuf

  • /protobuf
    • /protos -> 放 .proto
    • /compile -> 放編譯完的結果

建立 hello.proto

syntax = "proto3";

package hello;

// 定義 Request
message HelloRequest {
    string name = 1;
}

// 定義 Response
message HelloResponse {
    string message = 1;
}

// 定義 Server(Service) 提供的方法
service Hello {
    rpc Hello (HelloRequest) returns (HelloResponse) {
    }
}

進入到你的 WSL (ubuntu)

  1. 先更新套件來源

    $ sudo apt update
  2. 安裝PHP

    sudo add-apt-repository ppa:ondrej/php
    sudo apt -y install php8.0
  3. 安裝編譯相關套件

    $ sudo apt -y install php8.0-dev autoconf automake libtool make gcc libz-dev cmake
  4. clone官方github原始碼(假設這裡用1.39.0版)

    $ git clone --recurse-submodules -b v1.39.0 https://github.com/grpc/grpc
  5. 編譯grpc_php_plugin

    cd grpc
    mkdir -p cmake/build
    cd cmake/build
    cmake ../..
    make protoc grpc_php_plugin
  6. 安裝 protobuf-compiler

    $ sudo apt -y install protobuf-compiler
  7. 切到你的 Laravel 專案目錄

  8. 編譯 .proto

    $ protoc --proto_path=protobuf/protos \
    --php_out=protobuf/compile \
    --grpc_out=protobuf/compile \
    --plugin=protoc-gen-grpc={grpc_php_plugin path} \
    hello.proto

{grpc_php_plugin path},如果你是在家目錄執行,上面的所有動作,應該會長得像

/home/{username}/grpc/cmake/build/grpc_php_plugin

回到你熟悉的編輯器

Client端 (Laravel)

  1. 安裝 grpc 與 protobuf 套件

    $ composer require "grpc/grpc" "google/protobuf"
  2. 設定 composer.json 的 autoload

    {
     ...
     "autoload": {
         "psr-4": {
             "App\\": "app/",
             "Database\\Factories\\": "database/factories/",
             "Database\\Seeders\\": "database/seeders/",
             "Hello\\": "protobuf/compile/Hello",
             "GPBMetadata\\": "protobuf/compile/GPBMetadata"
         }
     },
     ...
    }
  3. 重新生成 autoload files

    $ composer dump-autoload
  4. 建立 Controller

    $ php artisan make:controller MainController
  5. 貼上

<?php

namespace App\Http\Controllers;

use Hello\HelloClient;
use Hello\HelloRequest;

class MainController extends Controller
{
    public function index() {
        $host = 'localhost:50051';
        $client = new HelloClient($host, [
            'credentials' => \Grpc\ChannelCredentials::createInsecure(),
        ]);
        $request = new HelloRequest();
        $request->setName('test');
        $call = $client->Hello($request);
        list($response, $status) = $call->wait();
        dd($response, $status);
    }
}
  1. 修改路由
    use App\Http\Controllers\MainController;
    Route::get('/', [MainController::class, 'index']);

Server端 (Node.js)

  1. 安裝 grpc_tools (假設上面的 protoc 已經裝好)

    $ npm install -g grpc-tools
  2. 編譯

    $ grpc_tools_node_protoc --proto_path=protobuf/protos --js_out=import_style=commonjs,binary:protobuf/compile --grpc_out=grpc_js:protobuf/compile hello.proto
  3. 安裝 @grpc/grpc-js 與 google-protobuf

    $ npm install @grpc/grpc-js google-protobuf
  4. 建立 hello_server.js

const grpc = require('@grpc/grpc-js');
const messages = require('./protobuf/compile/hello_pb');
const services = require('./protobuf/compile/hello_grpc_pb');


function hello(call, callback) {
    const reply = new messages.HelloResponse();
    reply.setMessage('Hello ' + call.request.getName());
    callback(null, reply);
}


const server = new grpc.Server();
server.addService(services.HelloService, { hello });
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
    server.start();
});

啟動 Laravel server

$ php artisan serv

之後到你的瀏覽器上面看,應該就會看到結果了!


總結

步驟真的是滿多的,而且第一次用找了一堆資料很零散真的花了很多時間,加上一開始我是打算全程用純windows就好,結果踩到一堆坑,後來只好加上WSL輔助。




最後更新時間: 2021年08月28日.