概述
官方网站https://grpc.https://grpc.io/
gRPC is a modern open source high performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.
gRPC是当前一个开源的高性能RPC框架,它可以在各种环境中运行。可以高效地连接数据中心内和跨数据中心的服务,支持可插拔的负载平衡、链路跟踪、健康检查和权限校验。同时也适用于分布式计算的后端服务和最后一英里中的设备,移动应用程序和浏览器相互连接。
grpc解决的问题是不同语言之间通信。
IDL
IDL就是接口描述语言(interface description language)的缩写,通过一种中立的方式来描述接口,使得在不同的平台上运行的对象和不同语言编写的程序可以相互通信交流。
gRPC协议使用Protobuf简称proto文件来定义接口名、调用参数以及返回值类型。
Protobuf的官方文档https://developers.google.com/protocol-buffers/docs/overview
helloword.proto定义了一个接口SayHello方法,它的请求参数是HelloRequest,它的返回值是HelloReply。
// The greeter service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
gRPC协议的服务描述是通过proto文件来定义接口的,然后再使用protoc来生成不同语言平台的客户端和服务端代码,从而具备跨语言服务调用能力。
有一点特别需要注意的是,在描述接口定义时,IDL文件需要对接口返回值进行详细定义。如果接口返回值的字段比较多,并且经常变化时,采用IDL文件方式的接口定义就不太合适了。一方面可能会造成IDL文件过大难以维护,另一方面只要IDL文件中定义的接口返回值有变更,都需要同步所有的服务消费者都更新,管理成本就太高了。
环境配置
protobuf
项目地址:google/protobuf,安装地址是https://github.com/protocolbuffers/protobuf/releases,选择自己系统的版本;例如Linux版本的64位系统安装如下
$ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.11.4/protoc-3.11.4-linux-x86_64.zip
$ unzip protoc-3.11.4-linux-x86_64.zip -d protoc
$ sudo mv protoc/bin/protoc /usr/bin/
$ protoc --version
libprotoc 3.11.4
mac系统也可以使用用brew
工具安装
$ brew install protobuf
执行protoc
命令查看当前版本:
$ protoc --version
libprotoc 3.7.1
官方Demo
下载example
$ git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
启动服务端
$cd $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_server
$go run main.go
启动客户端
$cd $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_client
$go run main.go
HelloWorld
文件结构
$ tree
.
├── client.go
├── proto
│ ├── hello.pb.go
│ └── hello.proto
└── server.go
创建IDL文件hello.proto
syntax = "proto3";
package proto;
service HelloService {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string request = 1;
}
message HelloResponse {
string response = 1;
}
进入到工程的proto目录下,执行下面的protoc命令,将得到一个hello.pb.go 文件
protoc --go_out=plugins=grpc:. hello.proto
定义的 proto 文件是涉及了 RPC 服务的,而默认是不会生成 RPC 代码的,因此需要给出 plugins
参数传递给 protoc-gen-go
,因此需要指定了gRPC
- —go_out=.:设置 Go 代码输出的目录,为当前目录,因为需要生成go相关的代码文件 保证已经安装了protoc-gen-go 插件,否则无法生成的以 .pb.go 为文件后缀
- : (冒号)冒号充当分隔符的作用,后跟所需要的参数集。
如果出现下面错误
protoc-gen-go: program not found or is not executable
Please specify a program using absolute path or make sure the program is available in your PATH system variable
--go_out: protoc-gen-go: Plugin failed with status code 1.
需要安装插件protoc-gen-go
创建service端文件server.go
package main
import (
"context"
"github.com/baxiang/go-note/grpc-web/proto"
"google.golang.org/grpc"
"log"
"net"
)
type HelloService struct {
}
func (h *HelloService) Hello(c context.Context, r *proto.HelloRequest) (*proto.HelloResponse, error) {
return &proto.HelloResponse{Response: "Hello " + r.GetRequest()}, nil
}
func main() {
server := grpc.NewServer()
proto.RegisterHelloServiceServer(server, &HelloService{})
listen, err := net.Listen("tcp", ":8090")
if err != nil {
log.Fatalf("listen err: %v", err)
}
err = server.Serve(listen)
if err != nil {
log.Fatalf(" serve err: %v", err)
}
}
- 注册HelloService gRPC Server 的内部注册中心。
- 创建 Listen,监听 TCP 端口
启动服务端
$ go run server.go
创建客户端文件client.go
package main
import (
"context"
"github.com/baxiang/go-note/grpc-web/proto"
"google.golang.org/grpc"
"log"
)
func main() {
conn, err := grpc.Dial(":8090", grpc.WithInsecure())
if err != nil {
log.Fatalf("grpc.Dial err: %v", err)
}
defer conn.Close()
client := proto.NewHelloServiceClient(conn)
resp, err := client.Hello(context.Background(), &proto.HelloRequest{
Request: "gRPC",
})
if err != nil {
log.Fatalf("client err: %v", err)
}
log.Printf("resp: %s", resp.GetResponse())
}
启动客户端
$ go run client.go
resp: Hello gRPC
参考
https://grpc.io/docs/quickstart/
https://github.com/Jergoo/go-grpc-example
https://studygolang.com/articles/24504#reply0
https://grpc.io/docs/tutorials/basic/go/
https://cloud.tencent.com/developer/article/1242108