0.1、索引
https://waterflow.link/articles/1665674508275
1、什么是grpc
在 gRPC 中,客户端应用程序能够间接调用不同机器上的服务器应用程序上的办法,就像它是本地对象一样,使您更容易创立分布式应用程序和服务。 与许多 RPC 零碎一样,gRPC 基于定义服务的思维,指定能够近程调用的办法及其参数和返回类型。 在服务端,服务端实现这个接口并运行一个 gRPC 服务器来解决客户端调用。 在客户端,客户端有一个stub(在某些语言中仅称为客户端),它提供与服务器雷同的办法。
所以grpc是跨语言的。
2、什么是Protocol Buffers
Protocol Buffers提供了一种语言中立、平台中立、可扩大的机制,用于以向前兼容和向后兼容的形式序列化结构化数据。 它相似于 JSON,只是它更小更快,并且生成本地语言绑定。
能够通过 .proto定义数据结构,而后就能够应用Protocol Buffers编译器 protoc 从. proto 定义中生成咱们喜爱的语言的数据拜访类。 它们为每个字段提供简略的拜访器,如 name() 和 set_name(),以及将整个构造序列化/解析到原始字节/从原始字节中提取的办法。
3、grpc服务端
1、首先咱们须要下载go对应的protoc插件
go install google.golang.org/protobuf/cmd/[email protected]go install google.golang.org/grpc/cmd/[email protected]
而后把GOPATH放到PATH
export PATH="$PATH:$(go env GOPATH)/bin"
接着打印下看看有没有进去
echo $PATH/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$GOPATH/bin:/usr/local/go/bin
接着开新窗口,执行上面命令看下protoc是否装置胜利
protoc --versionlibprotoc 3.19.1
2、接着咱们创立一个hello.proto
内容如下(不懂构造的可自行百度)
syntax = "proto3";package helloservice;option go_package = ".;helloservice"; // 指定包名message String { string value = 1;}service HelloService { rpc Hello(String) returns (String); // 一元办法 rpc Channel (stream String) returns (stream String); // 流式办法}
目录构造如下
.├── go.mod├── go.sum├── helloclient│ └── main.go├── helloservice│ ├── hello.proto
3、接着命令行生成对应语言的类代码
cd helloservice protoc --go_out=./ --go-grpc_out=./ hello.proto
咱们能够看下当初的目录构造(其余文件目录可疏忽,前面会创立,当初只须要关注hello_grpc.pb.go)
.├── go.mod├── go.sum├── helloclient│ └── main.go├── helloservice│ ├── hello.pb.go│ ├── hello.proto│ ├── hello_grpc.pb.go│ ├── hello_service.go│ └── main│ └── main.go
4、实现本人的hello service
在下面生成的hello_grpc.pb.go中咱们能够看到这样的接口
// HelloServiceServer is the server API for HelloService service.// All implementations must embed UnimplementedHelloServiceServer// for forward compatibilitytype HelloServiceServer interface { Hello(context.Context, *String) (*String, error) Channel(HelloService_ChannelServer) error mustEmbedUnimplementedHelloServiceServer()}
翻译一下就是
// HelloServiceServer 是 HelloService 服务的服务端 API。
// 所有实现都必须嵌入 UnimplementedHelloServiceServer
// 为了向前兼容
所以咱们在helloservice中创立一个hello_service.go文件,用来实现下面的接口
package helloserviceimport ( "context" "io" "time")type HelloService struct {}func (h HelloService) mustEmbedUnimplementedHelloServiceServer() { panic("implement me")}func (h HelloService) Hello(ctx context.Context, args *String) (*String, error) { time.Sleep(time.Second) reply := &String{Value: "hello:" + args.GetValue()} return reply, nil}func (h HelloService) Channel(stream HelloService_ChannelServer) error { for { recv, err := stream.Recv() if err != nil { if err == io.EOF { return nil } return err } reply := &String{Value: "hello:" + recv.Value} err = stream.Send(reply) if err != nil { return err } }}
下面的办法和简略,就是打印咱们自定义的字符串。
而后咱们在main中编写下服务启动的代码
package mainimport ( "google.golang.org/grpc" "grpcdemo/helloservice" "log" "net")func main() { // NewServer 创立一个 gRPC 服务器,它没有注册服务,也没有开始承受申请。 grpcServer := grpc.NewServer() // 注册服务 helloservice.RegisterHelloServiceServer(grpcServer, new(helloservice.HelloService)) // 开启一个tcp监听 listen, err := net.Listen("tcp", ":1234") if err != nil { log.Fatal(err) } log.Println("server started...") // 在监听器 listen 上承受传入的连贯,创立一个新的ServerTransport 和 service goroutine。 服务 goroutine读取 gRPC 申请,而后调用注册的处理程序来回复它们。 log.Fatal(grpcServer.Serve(listen))}
而后咱们启动下看下成果
go run helloservice/main/main.go2022/10/13 23:07:46 server started...
4、grpc客户端
接着咱们编写客户端的代码helloclient/main.go
package mainimport ( "context" "fmt" "google.golang.org/grpc" "grpcdemo/helloservice" "io" "log" "time")func main() { // 连贯grpc服务端 conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure()) if err != nil { log.Fatal(err) } defer conn.Close() // 一元rpc unaryRpc(conn) // 流式rpc streamRpc(conn)}func unaryRpc(conn *grpc.ClientConn) { // 创立grpc客户端 client := helloservice.NewHelloServiceClient(conn) // 发送申请 reply, err := client.Hello(context.Background(), &helloservice.String{Value: "hello"}) if err != nil { log.Fatal(err) } log.Println("unaryRpc recv: ", reply.Value)}func streamRpc(conn *grpc.ClientConn) { // 创立grpc客户端 client := helloservice.NewHelloServiceClient(conn) // 生成ClientStream stream, err := client.Channel(context.Background()) if err != nil { log.Fatal(err) } go func() { for { // 发送音讯 if err := stream.Send(&helloservice.String{Value: "hi"}); err != nil { log.Fatal(err) } time.Sleep(time.Second) } }() for { // 接管音讯 recv, err := stream.Recv() if err != nil { if err == io.EOF { break } log.Fatal(err) } fmt.Println("streamRpc recv: ", recv.Value) }}