Go 通過 grpc-protobuf 實現高性能用戶服務實戰,從 0 到 1 超級詳細流程!!

基礎知識準備

在在代碼實戰 gRPC 之前,我們需要了解一些基礎知識:

  1. RPC(Remote Procedure Call):遠程過程調用,是一種通信協議,允許應用程序在不同的計算機上請求服務而不需要了解底層網絡細節。

  2. gRPC:gRPC 是一種高性能、開源的遠程過程調用(RPC)框架,由 Google 開發,並基於 HTTP/2、Protocol Buffers 等技術實現。

  3. Protocol Buffers(ProtoBuf):是一種由 Google 開發的輕量級、高效、可擴展的結構化數據序列化方法,常用於通信協議、數據存儲等領域。它採用了二進制格式,相比於 XML、JSON 等文本格式,更加緊湊,速度更快,同時具備了跨語言、跨平臺的特性。

  4.grpc 將 Protocol Buffers 作爲其默認的序列化格式, 就說明 grpc 中的消息通信使用 pro tobuf 格式定義,

  5. 而 grpc 編譯器將根據 protobuf 文件生成相應語言的代碼,包括消息結構和 RPC 服務接口,但是 protobuf 卻不依賴於 gRPC

RPC 開發流程

gRPC 的開發流程一般分爲以下幾個步驟:

  1. 定義服務和消息:使用 Protocol Buffers 定義 RPC 服務的接口和消息格式。

  2. 生成代碼:利用 Protocol Buffers 的編譯器生成服務端和客戶端的代碼。

  3. 實現服務:在服務端實現定義的服務接口。

  4. 啓動服務:啓動 gRPC 服務並監聽指定端口。

  5. 調用服務:在客戶端調用 gRPC 生成的客戶端代碼來與服務端進行通信。

應用流程

  1. 客戶端請求:客戶端調用本地的 gRPC 方法,傳入參數。

  2. 消息序列化:客戶端將參數序列化成 Protocol Buffers 格式的消息。

  3. HTTP/2 傳輸:序列化後的消息通過 HTTP/2 協議傳輸到服務端。

  4. 消息反序列化:服務端接收到消息後,將其反序列化爲相應的參數。

  5. 處理請求:服務端調用相應的函數處理請求,並生成返回結果。

  6. 結果序列化:服務端將返回結果序列化成 Protocol Buffers 格式的消息。

  7. 返回結果:序列化後的結果通過 HTTP/2 協議傳輸回客戶端。

  8. 消息反序列化:客戶端接收到結果後,將其反序列化爲相應的數據類型。

  9. 返回結果:客戶端獲取到最終的返回結果。

Protocol Buffers 語法詳解

1. 定義消息類型

使用 Protobuf,首先要定義消息類型。消息類型是結構化數據的抽象,類似於面向對象編程中的類。

message Person {
  required int32 id = 1;
  required string name = 2;
  optional string email = 3;
}

上面定義了一個 Person 消息類型,它包含了 idname 和可選的 email 字段。

2. 消息字段規則

3. 默認值

可以爲字段指定默認值,當字段未被設置時將採用默認值。

message Person {
  required int32 id = 1;
  required string name = 2 [default = "John"];
  optional string email = 3 [default = "john@example.com"];
}

4. 枚舉類型

Protobuf 支持枚舉類型,用於定義一組命名的常量值。

enum PhoneType {
  MOBILE = 0;
  HOME = 1;
  WORK = 2;
}

5. 嵌套消息類型

消息類型可以嵌套定義在其他消息類型內部。

message PhoneNumber {
  required string number = 1;
  optional PhoneType type = 2 [default = HOME];
}

message Person {
  required int32 id = 1;
  required string name = 2;
  optional string email = 3;
  repeated PhoneNumber phones = 4;
}

RPC 結合 Protobuf 案例實戰

場景需求

通過 protobuf+grpc 實現查詢用戶信息的服務

proto 定義消息的結構和服務的接口

syntax = "proto3";

package example;

option go_package = "./pb";  //這裏要注意哈 這裏是代表將生成的go服務代碼放在哪個目錄
message UserRequest {
    string id = 1;
}

message UserResponse {
    string name = 1;
    int32 age = 2;
}

service UserService {
    rpc GetUser (UserRequest) returns (UserResponse);
}
解析下上面的 proto 文件
  1. syntax = "proto3";: 這行指定了使用的 protobuf 版本,這裏是 proto3 版本。

  2. package example;: 這個語句指定了包的名稱。

  3. option go_package = "./pb";: 指定了生成的輸出目錄。

  4. message UserRequest {: 定義了一個消息類型,名爲 UserRequest,包含一個字符串字段 id,字段標識爲 1。

  5. message UserResponse {: 另一個消息類型的定義,名爲 UserResponse,包含一個字符串字段 name 和一個整數字段 age,它們分別標識爲 1 和 2。

  6. service UserService {: 這裏定義了一個服務接口,名爲 UserService,包含一個名爲 GetUser 的遠程過程調用(RPC)方法,輸入參數爲 UserRequest,返回類型爲 UserResponse。

總的來說,這段 protobuf 文件定義了兩個消息類型(UserRequest 和 UserResponse), 以及一個包含一個 RPC 方法的服務接口(UserService), 該方法接收 UserRequest 並返回 UserResponse。(我說的很詳細,我不怕囉嗦,我只怕你們看不懂)

生成 grpc 代碼

protoc --go_out=. --go-grpc_out=. --plugin=protoc-gen-go-grpc=/你自己的路徑/protoc-gen-go-grpc example.proto

如果出現下面這個異常, 但是不要慌:

protoc-gen-go-grpc: 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-grpc_out: protoc-gen-go-grpc: Plugin failed with status code 1.

這說明你的 protoc-gen-go-grpc 沒有安裝,或者安裝了但是沒有配置環境變量

安裝:go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
配置環境變量:
   1: export PATH=$PATH:/Users/gaoxin/go/bin
   2: source ~/.bash_profile
Windows 我不太熟悉,你們百度下。。

RPC 服務端

package main

import (
 "awesomeProject1/rpc/pb"
 "context"
 "google.golang.org/grpc"
 "google.golang.org/grpc/codes"
 "google.golang.org/grpc/status"
 "log"
 "net"
)

type server struct{}

func (s *server) GetUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
 // Simulate fetching user data from database
 userData := map[string]pb.UserResponse{
  "1"{Name: "大美人", Age: 30},
  "2"{Name: "小帥哥", Age: 25},
  // Add more user data as needed
 }

 user, ok := userData[req.Id]
 if !ok {
  return nil, status.Errorf(codes.NotFound, "User not found")
 }

 return &user, nil
}

func main() {
 lis, err := net.Listen("tcp"":50051")
 if err != nil {
  log.Fatalf("failed to listen: %v", err)
 }
 s := grpc.NewServer()
 pb.RegisterUserServiceServer(s, &server{})
 log.Println("Server started at :50051")
 if err := s.Serve(lis); err != nil {
  log.Fatalf("failed to serve: %v", err)
 }
}

客戶端 client 代碼

package main

import (
 "awesomeProject1/rpc/pb"
 "context"
 "google.golang.org/grpc"
 "log"
)

func main() {
 conn, err := grpc.Dial(":50051", grpc.WithInsecure())
 if err != nil {
  log.Fatalf("did not connect: %v", err)
 }
 defer conn.Close()
 c := pb.NewUserServiceClient(conn)

 userID := "1" //todo  比如我們要獲取用戶ID 1 這個用戶的信息

 resp, err := c.GetUser(context.Background()&pb.UserRequest{Id: userID})
 if err != nil {
  log.Fatalf("could not call service: %v", err)
 }
 log.Printf("用戶信息: Name - %s, Age - %d", resp.Name, resp.Age)
}

運行 grpc 服務端

運行 grpc 客戶端獲取用戶信息

5. 交互流程細節

  1. 客戶端啓動,連接到服務器的 gRPC 服務端口。

  2. 客戶端調用 GetUser 方法,並傳遞用戶的 ID。

  3. 服務端收到請求後,根據傳遞的用戶 ID,從數據庫中獲取對應用戶的信息。

  4. 服務端將獲取到的用戶信息構建成 UserResponse 對象,併發送給客戶端。

  5. 客戶端接收到服務端返回的用戶信息後,打印出用戶的姓名和年齡。

最後的心裏話

到此,我們通過學習 proto 文件語法,並生成 go 的 grpc 代碼,然後創建了一個 gRPC 客戶端。並且構造了查詢用戶信息的請求,並通過客戶端調用了遠程 grpc 服務。最終我們處理了服務端返回的響應數據。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/7kxKL53ONDNzcwo8huMPyg