RPC 編程 -三-:gRPC 快速入門

  1. 什麼是 gRPC

gRPC 是一個高性能、開源、通用的RPC框架,由Google推出,基於 HTTP2 協議標準設計開發,默認採用 Protocol Buffers 數據序列化協議,支持多種開發語言。gRPC提供了一種簡單的方法來精確的定義服務,並且爲客戶端和服務端自動生成可靠的功能庫。

2.gRPC 技術棧

最底層爲TCPUnix套接字協議,在此之上是HTTP/2協議的實現,然後在HTTP/2協議之上又構建了針對Go語言的gRPC核心庫(gRPC內核 + 解釋器)。應用程序通過gRPC插件生成的Stub代碼和gRPC核心庫通信,也可以直接和gRPC核心庫通信。

  1. 前置準備

3.1 安裝protoc

下面示例是在mac環境中安裝。

# 安裝
➜ brew install protobuf
# 查看安裝後版本
➜ protoc --version
libprotoc 3.17.3

3.2 安裝插件

安裝插件的目的是爲了將protobuf文件,生成Go代碼

$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1

3.3 設置插件環境變量

export PATH="$PATH:$(go env GOPATH)/bin"

3.4 驗證插件是否成功

# 查看protoc-gen-go版本
$ protoc-gen-go --version                                      
protoc-gen-go v1.26.0

# 查看protoc-gen-go-grpc版本
$ protoc-gen-go-grpc --version
protoc-gen-go-grpc 1.1.0
  1. 快速使用

4.1 定義protobuf文件

文件名: hello.proto

syntax = "proto3";
package hello;

// 定義go生成後的包名
option go_package = "server/hello";

// 定義入參
message Request {
  string name =1;
}
// 定義返回
message Response {
  string result = 1;
}

// 定義接口
service UserService {
  rpc Say(Request) returns (Response);
}

4.2 生成GO代碼

# 同時生成hello.pb.go 和 hello_grpc.pb.go
$ protoc --go-grpc_out=. --go_out=. hello.proto

4.3 查看生成代碼

1. hello.pb.go部分代碼

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
//  protoc-gen-go v1.26.0
//  protoc        v3.17.3
// source: grpc/hello.proto

package hello

import (
 protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 reflect "reflect"
 sync "sync"
)

const (
 // Verify that this generated code is sufficiently up-to-date.
 _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
 // Verify that runtime/protoimpl is sufficiently up-to-date.
 _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

// 定義入參
type Request struct {
 state         protoimpl.MessageState
 sizeCache     protoimpl.SizeCache
 unknownFields protoimpl.UnknownFields

 Name string `protobuf:"bytes,1,opt,`
}

func (x *Request) GetName() string {
 if x != nil {
  return x.Name
 }
 return ""
}

// 定義返回
type Response struct {
 state         protoimpl.MessageState
 sizeCache     protoimpl.SizeCache
 unknownFields protoimpl.UnknownFields

 Result string `protobuf:"bytes,1,opt,`
}
func (x *Response) GetResult() string {
 if x != nil {
  return x.Result
 }
 return ""
}

2. hello_grpc.pb.go部分代碼

// Code generated by protoc-gen-go-grpc. DO NOT EDIT.

package hello

import (
 context "context"
 "fmt"
 grpc "google.golang.org/grpc"
)

// ---------- 客戶端相關代碼 --------
// 客戶端接口
type UserServiceClient interface {
 Say(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}
// 實現客戶端接口
type userServiceClient struct {
 cc grpc.ClientConnInterface
}
// 實例化客戶端
func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient {
 return &userServiceClient{cc}
}
// 客戶端調用Say方法
func (c *userServiceClient) Say(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
 out := new(Response)
 err := c.cc.Invoke(ctx, "/hello.UserService/Say", in, out, opts...)
 if err != nil {
  return nil, err
 }
 return out, nil
}

// ---------- 服務端相關代碼 ------------------
// 定義服務端接口
type UserServiceServer interface {
 Say(context.Context, *Request) (*Response, error)
 mustEmbedUnimplementedUserServiceServer()
}

// 實現服務端接口
type UnimplementedUserServiceServer struct {
}

// 實現方法
func (UnimplementedUserServiceServer) Say(ctx context.Context, r *Request) (*Response, error) {
  // 自己編寫代碼
 reply := &Response{
  Result: fmt.Sprintf("%s say hello word!", r.Name),
 }
 return reply, nil
}
// 註冊服務
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
 s.RegisterService(&UserService_ServiceDesc, srv)
}

4.4 下載grpc

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

4.5 編寫服務端代碼

server.go

package main

import (
 "52lu/go-study-example/grpc/server/hello"
 "fmt"
 "google.golang.org/grpc"
 "net"
)

func main() {
 // 創建grpc服務
 grpcServer := grpc.NewServer()
 // 註冊服務
 hello.RegisterUserServiceServer(grpcServer, new(hello.UnimplementedUserServiceServer))
 // 監聽端口
 listen, err := net.Listen("tcp"":1234")
 if err != nil {
  fmt.Println("服務啓動失敗", err)
  return
 }
 grpcServer.Serve(listen)
}

4.6 編寫客戶端代碼

client.go

package main

import (
 "52lu/go-study-example/grpc/server/hello"
 "fmt"
 "golang.org/x/net/context"
 "google.golang.org/grpc"
)

func main() {
 // 建立鏈接
 conn, err := grpc.Dial("127.0.0.1:1234", grpc.WithInsecure())
 if err != nil {
  fmt.Println("Dial Error ", err)
  return
 }
 // 延遲關閉鏈接
 defer conn.Close()
 // 實例化客戶端
 client := hello.NewUserServiceClient(conn)
 // 發起請求
 reply, err := client.Say(context.TODO()&hello.Request{Name: "張三"})
 if err != nil {
  return
 }
 fmt.Println("返回:", reply.Result)
}

4.7 啓動 & 請求

# 啓動服務端
$ go run server.go
# 啓動客戶端
$ go run client.go   
返回: 張三 say hello word!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/mLEVmomeN-Vd1aFGX7gZug