说明
GRPC是一个高性能、通用的开源RPC框架,基于HTTP/2协议标准和Protobuf序列化协议开发,支持众多的开发语言。
安装依赖包
go get golang.org/x/net/context
go get google.golang.org/grpc
go get google.golang.org/protobuf/reflect/protoreflect
go get google.golang.org/protobuf/runtime/protoimpl
编写IDL生成go代码
参考李文周的博客,编写了一个helloworld.proto文件,里面定义了两个服务:Greeter和GreeterCn。
syntax = "proto3"; // 版本声明,使用Protocol Buffers v3版本
option go_package = "./pb;pb";
// 定义一个英文打招呼服务
service Greeter {
// SayHello 方法
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 包含人名的一个请求消息
message HelloRequest {
string name = 1;
}
// 包含问候语的响应消息
message HelloReply {
string message = 1;
}
// 定义一个中文打招呼服务
service GreeterCn {
// SayHello 方法
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
把这个IDL转化为go源码:
$ protoc -I ./ ./helloworld.proto --go_out=plugins=grpc:./
$ ls pb/
helloworld.pb.go
编写服务端
对应helloworld.proto,编写了两个服务。
package main
import (
"fmt"
"net"
pb "test_grpc/pb"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
type server struct{}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
type server_cn struct{}
func (s *server_cn) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "你好 " + in.Name}, nil
}
func main() {
// 监听本地的8972端口
lis, err := net.Listen("tcp", ":8972")
if err != nil {
fmt.Printf("failed to listen: %v", err)
return
}
s := grpc.NewServer() // 创建gRPC服务器
pb.RegisterGreeterServer(s, &server{}) // 在gRPC服务端注册服务
pb.RegisterGreeterCnServer(s, &server_cn{}) // 在gRPC服务端注册服务
reflection.Register(s) //在给定的gRPC服务器上注册服务器反射服务
// Serve方法在lis上接受传入连接,为每个连接创建一个ServerTransport和server的goroutine。
// 该goroutine读取gRPC请求,然后调用已注册的处理程序来响应它们。
err = s.Serve(lis)
if err != nil {
fmt.Printf("failed to serve: %v", err)
return
}
}
grpcurl对于其他grpc服务的感知皆来自reflection服务。这里不调用reflection.Register(s)这一行,也不影响基本的功能。
编写客户端
package main
import (
"context"
"fmt"
"time"
pb "test_grpc/pb"
"google.golang.org/grpc"
)
func main() {
// 连接服务器
conn, err := grpc.Dial(":8972", grpc.WithInsecure())
if err != nil {
fmt.Printf("faild to connect: %v", err)
}
defer conn.Close()
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
//调用英文版的接口
c := pb.NewGreeterClient(conn)
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "yuanlulu"})
if err != nil {
fmt.Printf("could not greet: %v", err)
}
fmt.Printf("Greeting: %s !\n", r.Message)
//调用中文版的接口
c_cn := pb.NewGreeterCnClient(conn)
r, err = c_cn.SayHello(ctx, &pb.HelloRequest{Name: "袁露露"})
if err != nil {
fmt.Printf("could not greet: %v", err)
}
fmt.Printf("Greeting: %s !\n", r.Message)
}
终端输出:
$ go run client.go
Greeting: Hello yuanlulu !
Greeting: 你好 袁露露 !
gRPC的四种传输方式
gRPC有四种通信方式
- 简单rpc:这就是一般的rpc调用,一个请求对象对应一个返回对象
- 服务端流式rpc:一个请求对象,服务端可以传回多个结果对象
- 客户端流式rpc:客户端传入多个请求对象,服务端返回一个响应结果
- 双向流式rpc:结合客户端流式rpc和服务端流式rpc,可以传入多个对象,返回多个响应对象
streaming rpc相比于simple rpc来说可以很好的解决一个接口发送大量数据的场景。
上面我的代码只是第一种简单rpc模式,暂时用不到,以后再摸索。