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 Helloworld\GreeterClient('localhost:50051', [ 'credentials' => Grpc\ChannelCredentials::createInsecure(), ]); $request = new Helloworld\HelloRequest(); $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)."\n";
复制代码

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/
posted @ 2020-07-26 20:59  zbs666  阅读(1253)  评论(0编辑  收藏  举报