Skip to content
cym edited this page Nov 26, 2017 · 1 revision

grpc 的安装和使用

gRPC 是一个高性能、开源、通用的RPC框架,由Google推出,基于HTTP/2协议标准设计开发,默认采用Protocol Buffers数据序列化协议,支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。

安装

1. 安装 grpc 库与 protobuf 编译器

在 Mac 下:

$ brew tap grpc/grpc
$ brew install --with-plugins grpc

2. 安装 Golang protobuf插件

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

3. 获取 grpc-go

$ go get -u google.golang.org/grpc

编译器使用

使用protoc命令编译.proto文件,不同语言支持需要指定输出参数,如:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --javanano_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto

golang的编译参数:

  • -I 参数:指定 import 路径,可以指定多个 -I 参数,编译时按顺序查找,不指定时默认查找当前目录
    • --go_out :golang 编译支持,支持以下参数
      • plugins=plugin1+plugin2 - 指定插件,目前只支持grpc,即:plugins=grpc
      • M 参数 - 指定导入的.proto文件路径编译后对应的golang包名(不指定本参数默认就是.proto文件中import语句的路径)
      • import_prefix=xxx - 为所有import路径添加前缀,主要用于编译子目录内的多个proto文件,这个参数按理说很有用,尤其适用替代一些情况时的M参数,但是实际使用时有个蛋疼的问题导致并不能达到我们预想的效果,自己尝试看看吧
      • import_path=foo/bar - 用于指定未声明packagego_package的文件的包名,最右面的斜线前的字符会被忽略
      • 末尾 :[编译文件路径] xxx.proto(文件路径(支持通配符))

完整示例:

protoc -I . --go_out=plugins=grpc,Mfoo/bar.proto=bar,import_prefix=foo/,import_path=foo/bar:. ./*.proto

使用

定义了一个Hello Service,客户端发送包含字符串名字的请求,服务端返回Hello消息。

流程:

  1. 编写.proto描述文件
  2. 编译生成.pb.go文件
  3. 服务端实现约定的接口并提供服务
  4. 客户端按照约定调用方法请求服务

描述文件:proto/hello.proto

syntax = "proto3"; // 指定proto版本

package proto;     // 指定包名

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

// HelloRequest 请求结构
message HelloRequest {
    string name = 1;
}

// HelloReply 响应结构
message HelloReply {
    string message = 1;
}

hello.proto文件中定义了一个Hello Service,该服务包含一个SayHello方法,同时声明了HelloRequestHelloReply消息结构用于请求和响应。客户端使用HelloRequest参数调用SayHello方法请求服务端,服务端响应HelloReply消息。

编译生成.pb.go文件:

# 编译hello.proto
$ protoc -I . --go_out=plugins=grpc:. ./hello.proto

生成的.pb.go文件,按照.proto文件中的说明,包含服务端接口HelloServer描述,客户端接口及实现HelloClient,及HelloRequestHelloResponse结构体,不要手动编辑该文件。

服务端代码

package main

import (
	"net"
	"log"

	pb "projects/grpcExample/proto" // 引入编译生成的包

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"

)

const (
	// Address gRPC服务地址
	Address = "127.0.0.1:50052"
)

// 定义helloService并实现约定的接口
type helloService struct{}

// HelloService ...
var HelloService = helloService{}

func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	resp := new(pb.HelloReply)
	resp.Message = "Hello " + in.Name + "."

	return resp, nil
}

func main() {
	lis, err := net.Listen("tcp", Address)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterHelloServer(s, &helloService{})
	// Register reflection service on gRPC server.
	reflection.Register(s)
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

客户端代码

package main

import (
	pb "projects/grpcExample/proto" // 引入proto包
	"log"
	"os"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	//"google.golang.org/grpc/grpclog"
)

const (
	// Address gRPC服务地址
	Address = "127.0.0.1:50052"
	//defaultName = "world"
)

func main() {
	// 连接
	conn, err := grpc.Dial(Address, grpc.WithInsecure())

	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}

	defer conn.Close()

	// 初始化客户端
	c := pb.NewHelloClient(conn)

	// 调用方法
	reqBody := new(pb.HelloRequest)
	reqBody.Name = "gRPC"
	if len(os.Args) > 1 {
		reqBody.Name = os.Args[1]
	}
	r, err := c.SayHello(context.Background(), reqBody)
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	//grpclog.Info(r.Message)
	//grpclog.Infoln(r.Message)
	log.Printf("Greeting: %s", r.Message)
}

服务端引入编译后的proto包,实现约定的接口,接口描述可以查看hello.pb.go文件中的HelloServer接口描述。实例化grpc Server并注册HelloService,开始提供服务。

客户端初始化连接后直接调用声明的方法,即可向服务端发起请求,使用姿势就像调用本地方法一样。


参考:

  1. gRPC介绍与安装
  2. Golang gRPC实践 连载二 Hello gRPC
Clone this wiki locally