grpc简单实用 go服务端 php客户端

文章目录

  • 软件
  • 安装
  • 创建proto文件
  • go客户端和服务端
    • 服务端
    • 客户端
  • PHP客户端
    • 安装
    • 生成proto
    • 创建客户端类
    • 调用文件

软件

go 12.7
protobuf 3.8 https://github.com/protocolbuffers/protobuf/releases
php7.2
laravel 5.1
composer

安装

任意位置创建文件夹tgrpc

mkdir tgrpc

初始化go mod,包名为tgrpc

 go mod init tgrpc

在tgrpc目录下执行包获取,如果下不下来就设置下阿里的代理https://mirrors.aliyun.com/goproxy/

go get google.golang.org/grpc
go get github.com/golang/protobuf/proto

下载protobuf,解压后看bin/protoc

创建proto文件

在tgrpc目录下创建person/person.proto文件

syntax = "proto3";
// 生成的文件的包名
package person;

// 定义一个单项 RPC服务
service Greeter {
    // 提供一个添加人员
    rpc AddInfo (Person) returns (Status) {
    }
    // 获取年龄
    rpc GetAge (Name) returns (Status) {
    }
}
// Person消息类
message Person {
    // 类型 字段名 字段标记
    string name = 1;
    int32 age = 2;
}
message Status {
    bool status = 1;
}
message Name {
    string name = 1;
}
message Age {
    int32 age = 1;
}

进入person文件夹,执行命令生成person.pb.go

protoc --go_out=plugins=grpc:. person.proto //生成服务端的时候必须使用该段
// protoc --go_out=. person.proto //客户端用的时候这个也行,会少RegisterGreeterServer等方法

go客户端和服务端

服务端

回到tgrpc目录,创建server.go

package main
import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "log"
    "net"
    "sync"
    pb "tgrpc/person"
)
var person map[string]int32
var lock sync.RWMutex
func init() {
    person = make(map[string]int32)
}
type gs struct {
}
func (s gs) AddInfo(c context.Context, p *pb.Person) (*pb.Status, error) {
    fmt.Println(p.Name, p.Age)
    lock.Lock()
    defer lock.Unlock()
    person[p.Name] = p.Age
    return &pb.Status{Status: true}, nil
}
func (s gs) GetAge(c context.Context, name *pb.Name) (*pb.Age, error) {
    fmt.Println(name.Name)
    lock.RLock()
    defer lock.RUnlock()
    value := person[name.Name]
    return &pb.Age{Age: value}, nil
}
func main() {
    var listener net.Listener
    var server *grpc.Server
    var err error
    if listener, err = net.Listen("tcp", "0.0.0.0:10000"); err != nil {
        log.Fatalln(err)
    }
    server = grpc.NewServer()
    //注册服务器
    // 服务器 GreeterServer类型的数据
    //type GreeterServer interface {
    //  // 提供一个添加人员
    //  AddInfo(context.Context, *Person) (*Status, error)
    //  // 获取年龄
    //  GetAge(context.Context, *Name) (*Age, error)
    //}
    pb.RegisterGreeterServer(server, gs{})
    log.Println("服务开启")
    if err = server.Serve(listener); err != nil {
        log.Fatalln(err)
    }
}

客户端

回到tgrpc目录,创建client.go

package main
import (
    "context"
    "flag"
    "google.golang.org/grpc"
    "log"
    pb "tgrpc/person"
)
const (
    address     = "localhost:50051"
    defaultName = "world"
)
func main() {
    var conn *grpc.ClientConn
    var err error
    var action string
    var name string
    var age int
    var status *pb.Status
    var pAge *pb.Age
    //读取参数
    flag.StringVar(&action, "action", "get", "方法名")
    flag.StringVar(&name, "name", "test", "姓名")
    flag.IntVar(&age, "age", 0, "年龄")
    flag.Parse()
    // 创建连接
    if conn, err = grpc.Dial("127.0.0.1:10000", grpc.WithInsecure()); err != nil {
        log.Fatalln(err)
    }
    defer conn.Close()
    // 创建客户端
    c := pb.NewGreeterClient(conn)
    // 添加人
    if action == "add" {
        if status, err = c.AddInfo(context.TODO(), &pb.Person{Name: name, Age: int32(age)}); err != nil {
            log.Fatalln(err)
        } else {
            log.Println(status.Status)
        }
    } else { // 获取Age
        if pAge, err = c.GetAge(context.TODO(), &pb.Name{Name: name}); err != nil {
            log.Fatalln(err)
        } else {
            log.Println(pAge.Age)
        }
    }
    log.Println("end")
}

PHP客户端

据说PHP不能做服务端,然后就弄了个使用laravel作为PHP客户端的例子

安装

php grpc的c扩展http://pecl.php.net/package/gRPC

composer安装两个扩展,下不下来的话弄阿里的代理https://developer.aliyun.com/composer

composer require grpc/grpc
composer require google/protobuf

生成proto

修改person.proto的package字段,将其改为想要的namespace
例如我想将生成的文件放到namespace App\Services\Grpc\Person下,即项目目录/app/Services/Grpc/Person目录下

package App.Services.Grpc.Person;

运行protoc.exe --php_out=. person.proto,生成两个文件夹App GPBMetadata
当前目录/App/Services/Grpc/Person下的4文件复制到项目目录/app/Services/Grpc/Person目录下
当前目录/GPBMetadata文件夹复制到项目目录/app/Services/Grpc/Person目录下
修改项目目录/app/Services/Grpc/Person/GPBMetadata下Person.php的namespace

namespace GPBMetadata;
改为
namespace App\Services\Grpc\Person\GPBMetadata;

修改项目目录/app/Services/Grpc/Person下的4个文件

将\GPBMetadata替换为\App\Services\Grpc\Person\GPBMetadata

创建客户端类

项目目录/app/Services/Grpc/Person

_simpleRequest('/person.Greeter/AddInfo',
            $person,
            ['\App\Services\Grpc\Person\Status', 'decode'],
            $metadata, $options);
    }
    public function GetAge(\App\Services\Grpc\Person\Name $name, $metadata = [], $options = [])
    {
        return $this->_simpleRequest('/person.Greeter/GetAge',
            $name,
            ['\App\Services\Grpc\Person\Age', 'decode'],
            $metadata, $options);
    }
}

调用文件

php artisan make:console Tgrpc //创建一个脚本

将Tgrpc.php文件中protected $signature改为

protected $signature = 'grpc:person';

在Kernel中注册下

在handle中调用

$personService = new \App\Services\Grpc\Person\PersonService("127.0.0.1:10000", [
    'credentials' => \Grpc\ChannelCredentials::createInsecure()
]);
$person = new \App\Services\Grpc\Person\Person();
$person->setName("php_name");
$person->setAge(100);
list($status, $error) = $personService->AddInfo($person, [])->wait();
var_dump($status->getStatus(), $error);
$name = new \App\Services\Grpc\Person\Name();
$name->setName("php_name");
list($age, $error) = $personService->GetAge($name, [])->wait();
var_dump($age->getAge(), $error);

你可能感兴趣的:(grpc)