• PHP(客户端)与 Golang(服务端)使用grpc+protobuf 通信


      从零开始讲解,PHP(客户端)与 Golang(服务端)使用grpc+protobuf 通信。因为我本地环境都是配置好的,避免我落下步骤操作,所以我在docker环境下开发,拉取一个基于Alpine的镜像。Alpine操作系统是一个面向安全的轻型 Linux 发行版。

    搭建环境

    1.项目中,我会用到composer以及PHP相关的扩展,于是我拉取一个基于PHP7.2的docker镜像。命令中的参数,可以看我另一篇文章,Docker 使用笔记-常用基础命令

    docker container run -it --name grpc-ubuntu -v /Users/wuerzhilv-lq/examples:/home/examples lorisleiva/laravel-docker:7.2 /bin/bash

    2.安装相关的包

    apk add autoconf automake libtool linux-headers  

    3.这个docker没有golang语言环境,我们这里安装下。这里采用编译安装的方式,为什么不用apk包管理器安装,因为有坑,详情链接

    Go工具链是用Go编写的,要构建它,需要安装Go编译器;由于我们没有GO编译器,同时1.4以后的GO语言版本没有直接支持GCC,支持GO编译器GCCGO,因此我们需要先现在1.4版本的GO,利用GCC编译好GO编译器后,再利用1.4版本的GO编译器编译最新版本的GO(比如以下的1.14)。

      

    ①下载go1.4版本,源码从官方下载
    wget https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz
    tar -zxf go1.4-bootstrap-20171003.tar.gz
    mv go /usr/local/go1.4
    
    ②下载go1.14版本 wget https://dl.google.com/go/go1.14.2.src.tar.gz tar -zxf go1.14.2.src.tar.gz mv go /usr/local/go
    ③环境变量设置(GOROOT_BOOTSTRAP是编译器环境设定需要) echo 'export GOROOT_BOOTSTRAP=/usr/local/go1.4' >> ~/.bash_profile echo 'export GOROOT=/usr/local/go' >> ~/.bash_profile echo 'export PATH=${GOROOT}/bin:${PATH}' >> ~/.bash_profile //重载环境配置变量(主要是重载GOROOT_BOOTSTRAP)
    source ~/.bash_profile
    ④ 编译go1.4的版本 cd /usr/local/go1.4/src && ./make.bash
    ⑤利用go1.4的go编译器,编译go1.14 cd /usr/local/go/src && ./make.bash
    ⑥查看golang版本 go version

    4.新下载的php本来就没有现成的php.ini文件。只是给了php.ini-development (开发环境用)与 php.ini-production(生产环境用)两个建议。这里我们基于生产环境,复制出一个php.ini文件。

    cp /usr/local/etc/php/php.ini-production  /usr/local/etc/php/php.ini

    5.系统层面的,我们环境就安装好了。Grpc 是使用protobuf来传输数据,protobuf是一个文件,那怎么让php和golang语言识别它,这里就需要一个编译工具了。

    apk add protobuf

    6. PHP安装Grpc,Protobuf扩展。

    pecl install grpc-1.25.0
    pecl install protobuf-3.11.4

    打开我们新建的php.ini文件,将扩展加进去。可以使用php -m 查看配置是否生效。

    extension=grpc.so    extension=protobuf.so

    7.安装PHP Protoc Plugin

    $ git clone -b v1.28.1 https://github.com/grpc/grpc
    $ cd grpc
    $ git submodule update --init
    $ make grpc_php_plugin
    make编译后,输出目录为bins/opt,输出文件grpc_php_plugin,将文件复制到/home/examples
    cp bins/opt/grpc_php_plugin  /home/examples/

    8.安装Golang Protoc Plugin

    go get -u github.com/golang/protobuf/protoc-gen-go
    export PATH=$PATH:$GOPATH/bin

    9.安装grpc

    go get -u google.golang.org/grpc

    10.环境都配好了,我们来写项目代码,我在创建docker容器的时候,就把容器目录挂载到了本地,可以使用ide工具开发。

      我这里配置的本地目录是,/Users/wuerzhilv-lq/examples。examples就相当于项目目录了,在这个项目中新建protos目录,用于存放protobuf文件;新建php目录用于存放客户端代码;新建go目录,用于存放服务端代码。

          我们新建一个protobuf文件(helloworld.proto),来定义服务方法,请求参数,请求返回值。 内容如下

    // 定义语法,一共是有两种proto2,proto3。我这里使用proto3。
    syntax = "proto3";
    
    // 定义服务(我感觉像类)
    service Greeter {
      // 定义服务方法为SayHello,可以有多个方法
      rpc SayHello (HelloRequest) returns (HelloReply) {}
    }
    
    //定义请求参数
    message HelloRequest {
      string name = 1;
    }
    
    //定义返回参数
    message HelloReply {
       string message = 1;
    }

    11. 服务端代码,进入容器中的项目目录,根据protobuf文件,使用proto工具生成服务端(golang)代码到go目录。执行命令之后,会在go文件夹中出现一个proto文件夹,里面包含一个helloworld.pb.go文件。

    protoc protos/helloworld.proto --go_out=plugins=grpc:go

        在examples/go 文件夹中,新建一个main.go文件,注册helloworld服务。

    package main
    
    import (
        "context"
        "log"
        "net"
    
        "google.golang.org/grpc"
        pb "./protos"
    )
    
    const (
        port = ":50051"
    )
    
    // server is used to implement helloworld.GreeterServer.
    type server struct {
        pb.UnimplementedGreeterServer
    }
    
    // SayHello implements helloworld.GreeterServer
    func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
        log.Printf("Received: %v", in.GetName())
        return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
    }
    
    func main() {
        lis, err := net.Listen("tcp", port)
        if err != nil {
            log.Fatalf("failed to listen: %v", err)
        }
        s := grpc.NewServer()
        pb.RegisterGreeterServer(s, &server{})
        log.Printf("服务启动"+port)
        if err := s.Serve(lis); err != nil {
            log.Fatalf("failed to serve: %v", err)
        }
    }

       运行go run main.go启动服务,看看服务是否能启动。

    12.客户端代码,进入容器中的项目目录,根据protobuf文件,使用proto工具生成客户端(PHP)代码到php目录。执行命令之后,会在php文件夹中出现一些文件。

    protoc --proto_path=protos   --php_out=php   --grpc_out=php   --plugin=protoc-gen-grpc=/home/examples/grpc_php_plugin protos/helloworld.proto

      更换composer源

    composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

      通过composer工具,我们初始化一个composer.json文件。

     composer.json内容如下:

    {

      "name": "grpc/grpc-demo",
      "description": "gRPC example for PHP",
      "require": {
        "grpc/grpc": "^1.27",
        "google/protobuf": "^3.11"
      },
      "authors": [

        {

          "name": "Li",
          "email": "calmliqi@gmail.com"
        }
      ]
    }

     composer install 安装扩展包。  

     在php目录下,新建客户端文件,greeter_client.php

    <?php
    require dirname(__FILE__).'/vendor/autoload.php'; @include_once dirname(__FILE__).'/Helloworld/GreeterClient.php'; @include_once dirname(__FILE__).'/Helloworld/HelloReply.php'; @include_once dirname(__FILE__).'/Helloworld/HelloRequest.php'; @include_once dirname(__FILE__).'/GPBMetadata/Helloworld.php'; function greet($name) { $client = new HelloworldGreeterClient('localhost:50051', [ 'credentials' => GrpcChannelCredentials::createInsecure(), ]); $request = new HelloworldHelloRequest(); $request->setName($name); list($reply, $status) = $client->SayHello($request)->wait(); $message = $reply->getMessage(); return $message; } $name = !empty($argv[1]) ? $argv[1] : 'world'; echo greet($name)." ";

    13.进入examples项目目录,

    启动服务端
    go run go/main.go
    执行客户端
    php php/greeter_client.php

      如果输出helloworld,那就是成功了。

    总结:
      这篇文章,只是简单粗略说了下,Grpc+protobuf的使用,比如针对protobuf文件,具体的内容没有说。以后,我会在写一篇针对protobuf文件详细讲解的文章。我懂得也不是太多,就是当自己做了一次总结。如有不对的地方,大家多提意见。我本机挂了代理,所以没有遇到墙的问题,如果被墙了,自行百度吧。
    参考了如下资料:
    https://developers.google.com/protocol-buffers
    https://grpc.io/docs/quickstart
    https://golang.org/doc/install/source
    https://tkstorm.com/posts-list/programming/go/build-golang-from-source-on-alpine/



  • 相关阅读:
    【JavaScript】RegExp 实例方法
    【JavaScript】RegExp 静态和实例属性
    【JavaScript】String 实例方法(三)
    【JavaScript】String 实例方法(一)
    【JavaScript】String 构造函数和静态方法
    【JavaScript】Symbol 实例属性和方法
    【JavaScript】Symbol 静态属性(二)
    第三节:备忘录模式——游戏角色恢复状态实例
    第二节:备忘录模式——原理&应用
    第一节:备忘录模式——需求说明&传统实现
  • 原文地址:https://www.cnblogs.com/jingying/p/12708670.html
Copyright © 2020-2023  润新知