服务化
grpcgolanghttpswofthyperf

gin 实现 grpc 简单的应答

安装protobuf

1、安装相关软件 ,我用的是 contOS7+windows , mac应该更好装一点

    yum install autoconf automake libtool gcc gcc-c++ zlib-devel

2、 下载protobuf,并安装

去到 Protocol Buffers 下载最新版本,然后解压到本地。

    wget https://github.com/protocolbuffers/protobuf/releases/download/v3.10.1/protoc-3.10.1-linux-x86_64.zip
    unzip protoc-3.10.1-linux-x86_64.zip
    protoc --version # 能看到 版本信息就安装成功了

安装 protobuf golang 插件

    go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

前提条件

      export GOPROXY=https://goproxy.cn
      export GO111MODULE=on
  - rpc-hello
      - pb
          - hello.proto
      - go.mod
  syntax = "proto3";

  package hello;

  // 定义服务
  service Hello {
      rpc SayHello (HelloRequest) returns (HelloReply) {}
  }

  // 请求体的结构体
  message HelloRequest {

  string name = 1;

  }

  // 响应的结构体
  message HelloReply {
      string message = 1;
      int64 code = 2;
  }
    protoc --go_out=plugins=grpc:. hello.proto
rpc-hello\pb\hello.pb.go

grpc四种服务类型:

1、简单方式:这就是一般的rpc调用,一个请求对象对应一个返回对象

2、服务端流式(Sever-side streaming )

3、客户端流式(Client-side streaming RPC)

4、双向流式(Bidirectional streaming RPC)

简单方式的调用

  rpc-hello
      - pb
          - hello.proto
          - hello.pb.go
      - go.mod
      - service
          - service.go
      - client
          - client.go

  package main

  import (
      "golang.org/x/net/context"
      "google.golang.org/grpc"
      "google.golang.org/grpc/reflection"
      "log"
      "net"
      pb "rpc-hello/pb"
  )

  // server 用来实现 hello.HelloServer

  type server struct{}

  // 实现 hello.SayHello 方法

  // (context.Context, *HelloRequest) (*HelloReply, error)

  func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
      return &pb.HelloReply{Message: "Hello " + in.Name, Code: 200}, nil
  }

  func main() {

      lis, err := net.Listen("tcp", ":50051")

      if err != nil {
          log.Fatalf("failed to listen: %v", err)
      }

      s := grpc.NewServer()

      pb.RegisterHelloServer(s, &server{})

      //在 server 中 注册 gRPC 的 reflection service

      reflection.Register(s)

      if err := s.Serve(lis); err != nil {
          log.Fatalf("failed to serve: %v", err)
      }
  }
  package main

  import (
      "fmt"
      "github.com/gin-gonic/gin"
      "google.golang.org/grpc"
      "log"
      "net/http"
      pb "rpc-hello/pb"
  )

  func main() {

      r := gin.Default()

      r.GET("/rpc/hello", func(c *gin.Context) {
          sayHello(c)
      })

      // Run http server
      if err := r.Run(":8052"); err != nil {
          log.Fatalf("could not run server: %v", err)
      }
  }

  func sayHello(c *gin.Context) {

      // Set up a connection to the server.
      conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
      if err != nil {
          log.Fatalf("did not connect: %v", err)
      }

      defer conn.Close()

      client := pb.NewHelloClient(conn)
      name := c.DefaultQuery("name","战士上战场")
      req := &pb.HelloRequest{Name: name}
      res, err := client.SayHello(c, req)

      if err != nil {
          c.JSON(http.StatusInternalServerError, gin.H{
              "error": err.Error(),
          })
          return
      }

      c.JSON(http.StatusOK, gin.H{
          "result": fmt.Sprint(res.Message),
          "code": fmt.Sprint(res.Code),
      })

  }

php 原生实现客户端

安装 protoc-gen-php 扩展

git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc
cd grpc
git submodule update --init # 这一步 要下4个小时,建议还是要挂git代理
make grpc_php_plugin # 警告没事,没error 就行
# 建议 国内用户换这个 ,再切换到对应版本分支,不然太难受了
git clone https://gitee.com/devin2019/grpc.git
cd grpc
git checkout xxxx # 对应分支
git submodule update --init #这一步 要下4个小时,建议还是要挂git代理
make grpc_php_plugin # 警告没事,没error 就行
grpc_php_plugin/pathto/grpc/bins/opt/grpc_php_plugin

安装 grpc 扩展

wget https://pecl.php.net/get/grpc-1.25.0.tgz
tar -zxvf grpc-1.25.0.tgz
/usr/bin/phpize #(这个根据`phpize`实际情况来)
./configure --with-php-config=/usr/bin/php-config #(这个根据`php-config`实际情况来)
make && make install
vim /etc/php.d/grpc.ini #这个根据实际情况去决定 是改`php.ini`还是别的什么
写入 extension=grpc.so

安装 protobuf 扩展

  wget https://pecl.php.net/get/protobuf-3.10.0.tgz
  tar -zxvf protobuf-3.10.0.tgz
  /usr/bin/phpize #(这个根据`phpize`实际情况来)
  ./configure --with-php-config=/usr/bin/php-config #(这个根据`php-config`实际情况来)
  make && make install
  vim /etc/php.d/protobuf.ini #这个根据实际情况去决定 是改`php.ini`还是别的什么
  写入 extension=protobuf.so
  "require": {
      "google/protobuf": "^v3.10.0"
  },

生成文件 带客户端

  |-- composer.json
  |-- composer.lock
  |-- GPBMetadata
      | |-- Hello.php
  |-- Hello
      | |-- HelloClient.php
      | |-- HelloReply.php
      | |-- HelloRequest.php
  |-- hello.proto
  |-- index.php
  |-- vendor

index.php

<?php

require_once __DIR__ . '/vendor/autoload.php';

use \Grpc\ChannelCredentials;
use \Hello\HelloClient;
use \Hello\HelloRequest;
use \Hello\HelloReply;

// 创建客户端实例

$helloClient = new HelloClient('127.0.0.1:50051', [
    'credentials' => ChannelCredentials::createInsecure()
]);

$helloRequest = new HelloRequest();
$helloRequest->setName("有事别找我");
$request = $helloClient->SayHello($helloRequest)->wait();

//返回数组

/** @var array $status */
/** @var HelloReply $response */
list($response, $status) = $request;

var_dump($response->getMessage());
echo PHP_EOL;
var_dump($status);

composer.json

{
    "name": "rpc/test",
    "require": {
        "grpc/grpc": "v1.25.0",
        "google/protobuf": "^v3.10.0"
    },
    "autoload":{
        "psr-4":{
            "GPBMetadata\\":"GPBMetadata/",
            "Hello\\":"Hello/"
        }
    },
    "repositories": {
        "packagist": {
            "type": "composer",
            "url": "https://mirrors.aliyun.com/composer/"
        }
    }
}
composer dump -oservice.gophp index.php
php swoolehyperfgolang
swooleswoole2.xhyperfphp.iniswoole.use_shortname=offcomposer create-project hyperf/hyperf-skeletongrpc

服务端

  |-- GPBMetadata
      | |-- Hello.php
  |-- Hello
      | |-- HelloClient.php
      | |-- HelloReply.php
      | |-- HelloRequest.php
      "GPBMetadata\\": "grpc/GPBMetadata",
      "Grpc\\": "grpc/Grpc"
 [
     'name' => 'grpc',
     'type' => Server::SERVER_HTTP,
     'host' => '0.0.0.0',
     'port' => 50051,
     'sock_type' => SWOOLE_SOCK_TCP,
     'callbacks' => [
             SwooleEvent::ON_REQUEST => [\Hyperf\GrpcServer\Server::class, 'onRequest'
         ],
     ],
 ],
  Router::addServer("grpc", function () {
      Router::addGroup('/hello.Hello', function () {
          Router::post('/SayHello', 'App\Controller\IndexController@sayHello');
      });
  });
  public function sayHello(HelloRequest $request)
  {
      $message = new HelloReply();
      $message->setMessage("Hello {$request->getName()}");
      $message->setCode(200);
      return $message;
  }

客户端

  use Hello\HelloClient;
  use Hello\HelloReply;
  use Hello\HelloRequest;
  use Hyperf\HttpServer\Contract\RequestInterface;

  public function index(RequestInterface $request)
  {
      $client = new HelloClient("127.0.0.1:50051", ['credentials' => null]);

      $name = $request->input("name","战士上战场");
      $helloRequest = new HelloRequest();
      $helloRequest->setName($name);

      /**
      * @var HelloReply $reply
      */
      list($reply, $status) = $client->sayHello($helloRequest);

      $message = $reply->getMessage();
      $code = $reply->getCode();

      $client->close();
      var_dump(memory_get_usage(true));

      return [
          'message' => $message,
          'code' => $code,
      ];
  }

  namespace Hello;

  use Hyperf\GrpcClient\BaseClient;

  /**
  * 定义服务
  */
  class HelloClient extends BaseClient{

      public function sayHello(HelloRequest $argument)
      {
          return $this->simpleRequest(
                  '/hello.Hello/SayHello',
                  $argument,
                  [HelloReply::class, 'decode']
              );
      }
  }
  namespace Hello;

  /**
  * 定义服务
  */
  class HelloClient extends \Grpc\BaseStub {

      /**
      * @param string $hostname hostname
      * @param array $opts channel options
      * @param \Grpc\Channel $channel (optional) re-use channel object
      */
      public function __construct($hostname, $opts, $channel = null) {
          parent::__construct($hostname, $opts, $channel);
      }

      /**
      * @param \Hello\HelloRequest $argument input argument
      * @param array $metadata metadata
      * @param array $options call options
      */

      public function SayHello(\Hello\HelloRequest $argument,
      $metadata = [], $options = []) {
          return $this->_simpleRequest(
                              '/hello.Hello/SayHello',
                              $argument,
                              ['\Hello\HelloReply', 'decode'],
                              $metadata, $options);
          }

  }
本作品采用《CC 协议》,转载必须注明作者和本文链接