Go開發pb

 
  1. pb開發-proto文件 以及 PB在 go項目中的開發過程

 
以下是一個簡單的示例,演示如何在Go項目中使用PB進行開發:
  1. 編寫Proto文件
假設我們要開發一個簡單的分佈式計算系統。首先,我們需要編寫一個Proto文件來定義請求和響應消息類型以及服務接口。以下是一個示例:
複製代碼
syntax = "proto3";

message ComputeRequest {
  int32 a = 1;
  int32 b = 2;
}

message ComputeResponse {
  int32 result = 1;
}

service Calculator {
  rpc Add(ComputeRequest) returns (ComputeResponse) {}
}
該文件定義了兩個消息類型 ComputeRequestComputeResponse,以及一個名爲 Calculator 的服務接口,該接口包含一個 Add 方法,用於將兩個整數相加並返回結果。
  1. 生成Go代碼
在命令行中運行以下命令,使用protoc-gen-go插件將Proto文件編譯爲Go代碼:
複製代碼
protoc --go_out=. calculator.proto
這將在當前目錄中生成一個名爲 calculator.pb.go 的文件,其中包含所有通過Proto文件定義的消息類型和服務接口。
  1. 實現服務端
在服務器端,我們需要實現定義的RPC服務。爲此,我們需要創建一個實現接口的結構體,並編寫處理請求和生成響應的代碼。以下是一個示例:
go複製代碼
package main

import (
        "context""log""net""google.golang.org/grpc"

        pb "path/to/calculator.pb"
)

type server struct{}

func (s *server) Add(ctx context.Context, req *pb.ComputeRequest) (*pb.ComputeResponse, error) {
        result := req.A + req.B
        return &pb.ComputeResponse{Result: result}, nil
}

func main() {
        lis, err := net.Listen("tcp", ":8080")
        if err != nil {
                log.Fatalf("failed to start server: %v", err)
        }

        srv := grpc.NewServer()
        pb.RegisterCalculatorServer(srv, &server{})

        log.Printf("server listening on %s", lis.Addr().String())
        if err := srv.Serve(lis); err != nil {
                log.Fatalf("failed to serve: %v", err)
        }
}
在上面的示例中,我們定義了一個名爲 Server 的結構體,並實現了 Add 方法,該方法接收一個 ComputeRequest 請求對象並返回一個 ComputeResponse 響應對象,該響應對象包含兩個數字的總和。然後,我們創建一個 grpc.Server 對象,並向其註冊 Calculator 服務。最後,我們啓動服務器並開始監聽傳入的連接。
  1. 實現客戶端
在客戶端上,我們需要編寫代碼來使用通過Proto文件生成的客戶端類與服務器進行通信。以下是一個示例:
go複製代碼
package main

import (
        "context""log""time""google.golang.org/grpc"

        pb "path/to/calculator.pb"
)

func main() {
        conn, err := grpc.Dial(":8080", grpc.WithInsecure())
        if err != nil {
                log.Fatalf("failed to connect: %v", err)
        }
        defer conn.Close()

        c := pb.NewCalculatorClient(conn)

        req := &pb.ComputeRequest{A: 10, B: 20}
        ctx, cancel := context.WithTimeout(context.Background(), time.Second)
        defer cancel()

        res, err := c.Add(ctx, req)
        if err != nil {
                log.Fatalf("failed to add: %v", err)
        }

        log.Printf("result: %d", res.Result)
}
在上面的示例中,我們使用 grpc.Dial() 連接到服務器,並使用通過Proto文件生成的 NewCalculatorClient() 函數創建一個客戶端對象。然後,我們創建一個 ComputeRequest 請求對象,並使用 context.WithTimeout() 創建一個有超時限制的上下文。最後,我們調用客戶端的 Add 方法,並打印響應。
  1. 測試和調試
在開發過程中,我們需要對我們的代碼進行測試和調試,以確保它能夠正確地序列化和反序列化數據,並且客戶端和服務器可以正常通信。
 
 
 
  1. GRPC 結合實例 說明 其使用及原理

以下是一個示例以說明gRPC的使用及原理。
我們假設需要在分佈式系統中實現排序功能,可以使用gRPC來定義服務接口,並通過該接口使客戶端向服務器發送請求,服務器對接收到的數組進行排序並返回已排序的結果。以下是Proto文件示例:
protobuf複製代碼
syntax = "proto3";

message SortRequest {
  repeated int32 nums = 1;
}

message SortResponse {
  repeated int32 sorted_nums = 1;
}

service Sorter {
  rpc Sort(SortRequest) returns (SortResponse) {}
}
上述Proto文件定義了兩個消息類型 SortRequestSortResponse,以及一個名爲 Sorter 的服務接口,該接口包含一個 Sort 方法,用於對傳入的整數數組進行排序並返回已排序的數組。
根據這個Proto文件,我們可以使用protoc編譯器生成Go語言的代碼,如下所示:
bash複製代碼
protoc --go_out=. sorter.proto
執行以上命令會在當前目錄下生成一個名爲 sorter.pb.go 的文件,其中包含所有通過Proto文件定義的消息類型和服務接口。
接下來,我們需要編寫一個服務器端程序,用於實現定義的 Sorter 服務接口。以下是示例代碼:
go複製代碼
package main

import (
        "context""log""net""google.golang.org/grpc"

        pb "path/to/sorter.pb"
)

type server struct{}

func (s *server) Sort(ctx context.Context, req *pb.SortRequest) (*pb.SortResponse, error) {
        nums := req.GetNums()
        // 使用快排算法對整數數組進行排序
        quickSort(nums, 0, len(nums)-1)
        return &pb.SortResponse{SortedNums: nums}, nil
}

func quickSort(nums []int32, left, right int) {
        if left >= right {
                return
        }
        pivot := partition(nums, left, right)
        quickSort(nums, left, pivot-1)
        quickSort(nums, pivot+1, right)
}

func partition(nums []int32, left, right int) int {
        pivot := nums[right]
        i := left
        for j := left; j < right; j++ {
                if nums[j] < pivot {
                        nums[i], nums[j] = nums[j], nums[i]
                        i++
                }
        }
        nums[i], nums[right] = nums[right], nums[i]
        return i
}

func main() {
        lis, err := net.Listen("tcp", ":8080")
        if err != nil {
                log.Fatalf("failed to start server: %v", err)
        }

        srv := grpc.NewServer()
        pb.RegisterSorterServer(srv, &server{})

        log.Printf("server listening on %s", lis.Addr().String())
        if err := srv.Serve(lis); err != nil {
                log.Fatalf("failed to serve: %v", err)
        }
}
在以上示例中,我們首先定義了一個名爲 Server 的結構體,並實現了 Sort 方法,該方法接收一個 SortRequest 請求對象,並對數組進行排序。然後,我們創建一個 grpc.Server 對象,並向其註冊 Sorter 服務。最後,我們啓動服務器並開始監聽傳入的連接。
現在,我們需要編寫一個客戶端程序,用於向服務器發送請求並獲取響應。以下是示例代碼:
go複製代碼
package main

import (
        "context""log""google.golang.org/grpc"

        pb "path/to/sorter.pb"
)

func main() {
        conn, err := grpc.Dial(":8080", grpc.WithInsecure())
        if err != nil {
                log.Fatalf("failed to connect: %v", err)
        }
        defer conn.Close()

        c := pb.NewSorterClient(conn)

        req := &pb.SortRequest{Nums: []int32{9, 3, 5, 2, 6, 4, 8, 7, 1}}
        res, err := c.Sort(context.Background(), req)
        if err != nil {
                log.Fatalf("failed to sort: %v", err)
        }

        log.Printf("sorted nums: %v", res.SortedNums)
}
以上代碼是一個gRPC客戶端程序的示例,其主要流程如下:
  1. 使用 grpc.Dial 函數連接服務器,並返回一個連接對象 conn
  2. 創建一個 SorterClient 對象 c,用於調用 Sort 方法。這個對象由 pb.NewSorterClient(conn) 創建,其中 pb 是通過 protoc 編譯器生成的Go語言代碼包。
  3. 創建一個 SortRequest 對象 req,將要排序的整數數組作爲參數傳遞給 Sort 方法。
  4. 調用 Sort 方法,並傳遞請求對象和上下文信息作爲參數,獲取響應結果和錯誤信息。
  5. 在獲取到響應結果後,打印已排序的整數數組。
在調用 Sort 方法時,gRPC會自動處理序列化和反序列化,以及TCP連接的管理等底層細節,這樣我們就可以專注於定義服務接口和業務邏輯,並使用gRPC輕鬆地構建分佈式應用程序。
  1. 總結 關於grpc 客戶端調用步驟有哪些 都做了什麼事

以下是基於前面的示例代碼對gRPC客戶端調用步驟及其所做的工作進行的詳細總結:
  1. 創建 grpc.ClientConn 對象連接服務器。在本例中,使用了 grpc.Dial 函數,指定了服務器地址和連接選項(WithInsecure() 表示不使用TLS安全連接)。
  2. 使用 pb.NewSorterClient(conn) 函數創建一個 SorterClient 對象,其中 pb 是通過 Protobuf 編譯器生成的包含服務接口和消息的Go語言代碼包。該客戶端對象表示與服務器交互的實體。
  3. 創建一個 SortRequest 對象 req 作爲請求消息,將要排序的整數數組作爲參數傳遞給 Sort 方法。
  4. 調用 c.Sort(context.Background(), req) 方法,向服務器發送請求消息並獲取響應信息。在這裏,我們傳遞了上下文信息 context.Background()和請求消息 req 作爲參數,並從 SorterClient 對象 c 中調用了 Sort 方法,該方法與定義的服務接口相匹配。
  5. gRPC處理序列化和反序列化,以及TCP連接的管理等底層細節。它將請求消息從結構化數據轉換爲二進制格式,並將其發送到服務器。然後,它接收來自服務器的響應消息,並將其從二進制格式轉換爲結構化數據。在此過程中,我們無需關心底層通信協議。
  6. 處理RPC調用的上下文信息,如超時時間、取消請求等。在這個例子中,使用了默認上下文信息 context.Background()
  7. 處理元數據(metadata),如身份驗證令牌等。在這個例子中,沒有使用自定義元數據。
  8. 處理錯誤信息,並在需要時重試請求。如果出現錯誤,gRPC將返回錯誤信息,並可以根據錯誤類型和錯誤碼進行處理。在這個例子中,如果有錯誤,則會在控制檯上打印錯誤信息並停止程序。
  9. 打印已排序的整數數組。如果一切順利,我們將獲得一個包含已排序的整數數組的響應結果。
綜上所述,gRPC客戶端調用主要涉及與服務器的連接、消息序列化和反序列化,上下文信息和元數據處理,錯誤處理等方面的工作。通過封裝這些複雜的操作和細節,gRPC能夠提供簡單但強大的方式使開發人員創建分佈式系統。
以下是基於前面的示例代碼對gRPC客戶端調用步驟及其所做的工作進行的詳細總結:
  1. 創建 grpc.ClientConn 對象連接服務器。在本例中,使用了 grpc.Dial 函數,指定了服務器地址和連接選項(WithInsecure() 表示不使用TLS安全連接)。
  2. 使用 pb.NewSorterClient(conn) 函數創建一個 SorterClient 對象,其中 pb 是通過 Protobuf 編譯器生成的包含服務接口和消息的Go語言代碼包。該客戶端對象表示與服務器交互的實體。
  3. 創建一個 SortRequest 對象 req 作爲請求消息,將要排序的整數數組作爲參數傳遞給 Sort 方法。
  4. 調用 c.Sort(context.Background(), req) 方法,向服務器發送請求消息並獲取響應信息。在這裏,我們傳遞了上下文信息 context.Background()和請求消息 req 作爲參數,並從 SorterClient 對象 c 中調用了 Sort 方法,該方法與定義的服務接口相匹配。
  5. gRPC處理序列化和反序列化,以及TCP連接的管理等底層細節。它將請求消息從結構化數據轉換爲二進制格式,並將其發送到服務器。然後,它接收來自服務器的響應消息,並將其從二進制格式轉換爲結構化數據。在此過程中,我們無需關心底層通信協議。
  6. 處理RPC調用的上下文信息,如超時時間、取消請求等。在這個例子中,使用了默認上下文信息 context.Background()
  7. 處理元數據(metadata),如身份驗證令牌等。在這個例子中,沒有使用自定義元數據。
  8. 處理錯誤信息,並在需要時重試請求。如果出現錯誤,gRPC將返回錯誤信息,並可以根據錯誤類型和錯誤碼進行處理。在這個例子中,如果有錯誤,則會在控制檯上打印錯誤信息並停止程序。
  9. 打印已排序的整數數組。如果一切順利,我們將獲得一個包含已排序的整數數組的響應結果。
綜上所述,gRPC客戶端調用主要涉及與服務器的連接、消息序列化和反序列化,上下文信息和元數據處理,錯誤處理等方面的工作。通過封裝這些複雜的操作和細節,gRPC能夠提供簡單但強大的方式使開發人員創建分佈式系統。
以下是基於前面的示例代碼對gRPC客戶端調用步驟及其所做的工作進行的詳細總結:
  1. 創建 grpc.ClientConn 對象連接服務器。在本例中,使用了 grpc.Dial 函數,指定了服務器地址和連接選項(WithInsecure() 表示不使用TLS安全連接)。
  2. 使用 pb.NewSorterClient(conn) 函數創建一個 SorterClient 對象,其中 pb 是通過 Protobuf 編譯器生成的包含服務接口和消息的Go語言代碼包。該客戶端對象表示與服務器交互的實體。
  3. 創建一個 SortRequest 對象 req 作爲請求消息,將要排序的整數數組作爲參數傳遞給 Sort 方法。
  4. 調用 c.Sort(context.Background(), req) 方法,向服務器發送請求消息並獲取響應信息。在這裏,我們傳遞了上下文信息 context.Background()和請求消息 req 作爲參數,並從 SorterClient 對象 c 中調用了 Sort 方法,該方法與定義的服務接口相匹配。
  5. gRPC處理序列化和反序列化,以及TCP連接的管理等底層細節。它將請求消息從結構化數據轉換爲二進制格式,並將其發送到服務器。然後,它接收來自服務器的響應消息,並將其從二進制格式轉換爲結構化數據。在此過程中,我們無需關心底層通信協議。
  6. 處理RPC調用的上下文信息,如超時時間、取消請求等。在這個例子中,使用了默認上下文信息 context.Background()
  7. 處理元數據(metadata),如身份驗證令牌等。在這個例子中,沒有使用自定義元數據。
  8. 處理錯誤信息,並在需要時重試請求。如果出現錯誤,gRPC將返回錯誤信息,並可以根據錯誤類型和錯誤碼進行處理。在這個例子中,如果有錯誤,則會在控制檯上打印錯誤信息並停止程序。
  9. 打印已排序的整數數組。如果一切順利,我們將獲得一個包含已排序的整數數組的響應結果。
綜上所述,gRPC客戶端調用主要涉及與服務器的連接、消息序列化和反序列化,上下文信息和元數據處理,錯誤處理等方面的工作。通過封裝這些複雜的操作和細節,gRPC能夠提供簡單但強大的方式使開發人員創建分佈式系統。
 
 
  1. Proto文件 基本的語法

 
 
Proto文件是gRPC中用來定義服務接口和消息結構的文件,其基本語法如下:
  1. 使用 syntax 關鍵字指定Proto文件所使用的語法版本。目前最新的版本是proto3,可以使用以下語句指定:
protobuf複製代碼
syntax = "proto3";
  1. 使用 package 關鍵字指定當前Proto文件所屬的包名。包名應該採用反向DNS格式,並以分號結尾。例如:
protobuf複製代碼
package com.example.grpc;
  1. 使用 message 關鍵字定義一個消息類型。一個消息類型可以包含多個字段,每個字段都有一個名稱、類型和唯一的數字標識符。例如:
protobuf複製代碼
message Person {
    string name = 1;
    int32 age = 2;
    bool is_student = 3;
}
  1. 使用 repeated 關鍵字定義一個重複字段。重複字段可以出現零次或多次,並且會按照添加的順序進行編碼和解碼。例如:
protobuf複製代碼
message Book {
    string title = 1;
    repeated string author = 2;
}
 
  1. 使用 enum 關鍵字定義一個枚舉類型。枚舉類型包含多個命名的常量,每個常量都有一個名稱和唯一的數字值。例如:
protobuf複製代碼
enum Gender {
    UNKNOWN = 0;
    MALE = 1;
    FEMALE = 2;
}
  1. 使用 使用上述是Proto文件中最基本的語法,通過這些語法我們可以定義自己的服務接口和消息結構,從而實現分佈式系統間的通信。在實際開發中,可能還會涉及到嵌套類型、子消息、擴展等高級語法。
  1. proto文件中不指定 syntax

如果在Proto文件中沒有指定 syntax 關鍵字,gRPC會假設使用的是proto2語法,並默認採用一些proto2的規則和特性。這可能會導致與proto3語法不兼容的問題,因此建議始終在Proto文件中明確指定所使用的語法版本。
在Proto3語法中,一些proto2特性已被取消或更改,而新的特性也已被引入。例如:
  • 在proto3語法中,所有字段都必須顯式地分配一個數字標識符。
  • proto3不再支持required關鍵字,因爲它對於可選字段和必需字段之間的區別沒有太大意義。
  • proto3不再支持默認值,因爲它可以導致歧義和錯誤。
  • 在proto3語法中,枚舉類型的第一個常量必須具有值0,以避免混淆和錯誤。
因此,如果未在Proto文件中指定 syntax 關鍵字,則可能會導致解析和編碼出現問題。因此,在編寫Proto文件時,應該始終指定正在使用的語法版本,以便在不同的平臺和語言中實現正確的通信。
 
 
  1. proto文件中 的repeated 在GO語言中是代表 集合嗎

 
是的,在Go語言中,與集合、列表或數組等數據結構類似。
在Protobuf編譯器生成的Go代碼中,對於使用了 repeated 關鍵字定義的字段,會被轉換爲相應類型的切片(slice),例如:
protobuf複製代碼
message Person {
    string name = 1;
    int32 age = 2;
    repeated string email = 3;
}
在這個例子中,Person 消息類型有三個字段:nameageemail。其中 email 字段使用 repeated 關鍵字定義,表示一個人可以有零個或多個郵件地址。當使用 Protobuf 編譯器生成 Go 代碼時,將會自動生成具有以下簽名的方法:
go複製代碼
func (m *Person) GetEmail() []string
該方法返回一個字符串類型的切片,其中包含了所有的 email 字段值。
因此,repeated 關鍵字在Go語言中確實代表了一種集合/列表/數組類型,並且可以用切片來表示和訪問。這種方式可以方便我們處理消息中包含多個值的情況,並且在不同的平臺和語言之間實現兼容性。
 
  1. proto文件中 optional 關鍵字

在最新的Proto3語法中,optional 關鍵字已被廢棄,不再被支持。在之前的Proto2語法中,
例如,在Proto2語法中,可以使用 optional 關鍵字來定義一個可選字段:
protobuf複製代碼
message Person {
    optional string email = 1;
    // ...
}
然而,這種方式雖然在語言層面上提供了一些便利,但在實際使用中可能會引起歧義和錯誤。因此,在Proto3語法中,optional 關鍵字已被取消,所有字段都默認爲可選的,即可以包含零個或一個值。
  • proto文件中service 中的 option關鍵字

在Proto文件中,
option 關鍵字用於爲服務(service)或方法(method)指定附加選項。
這些選項可以影響代碼生成、運行時行爲和通信協議等方面。
在服務(service)級別上,可以使用 option 關鍵字來指定以下選項:
  • rpc.greeter:用於指定RPC方法的名稱前綴。例如,如果將此選項設置爲“Hello”,則所有RPC方法的名稱前綴都會自動添加“Hello”。
  • cc_enable_arenas:控制是否啓用內存分配器(Memory Arena Allocator)。默認情況下,gRPC使用內存分配器來管理消息大小和生命週期,以提高性能。但是,對於某些平臺和應用程序,可能需要禁用內存分配器。通過設置此選項,可以靈活地控制內存分配器的使用。
 
例如,以下是一個使用 option 關鍵字定義服務級別選項的例子:
protobuf複製代碼
service MyService {
    option cc_enable_arenas = false;

    rpc MyRpcMethod(MyRequest) returns(MyResponse) {}
}
在這個例子中,
我們使用 option 關鍵字在 MyService 服務中指定了一個選項:禁用內存分配器。
這將使gRPC在處理消息時不再使用內存分配器,從而提高代碼兼容性和靈活性。
當然,在實際開發中,還可以根據需要在方法(method)級別上指定選項,
例如,可以使用 option 關鍵字指定每個方法的超時時間、重試次數、最大請求數等等。
這些選項可以提高系統的穩定性和可靠性,同時也爲開發人員提供了更多的靈活性和自由度。
 
 
請解讀一下 以下代碼中的option 做了什麼 service EmailConfigSvr { option (srpc.service_option_id) = 0x8628; //添加郵件配置 rpc AddEmailConfig(AddEmailConfigReq) returns (AddEmailConfigRsp) { option (srpc.method_option_id) = 0x01; } //更新郵件配置 rpc UpdateEmailConfig(UpdateEmailConfigReq) returns (UpdateEmailConfigRsp) { option (srpc.method_option_id) = 0x02; } //刪除郵件配置 rpc DelEmailConfig(DelEmailConfigReq) returns (DelEmailConfigRsp) { option (srpc.method_option_id) = 0x03; } //批量查詢郵箱配置 rpc BatchQueryEmailConfig (QueryEmailConfigReq) returns (QueryEmailConfigRsp) { option (srpc.method_option_id) = 0x04; } }
 
在這段代碼中,option 關鍵字用於定義了 srpc.service_option_idsrpc.method_option_id 兩個選項,它們會影響生成的 gRPC 代碼的行爲。具體來說:
  • srpc.service_option_id:該選項定義了服務(service)級別的選項 ID,值爲 0x8628。這個選項可以用來標識當前服務的版本、權限等信息,在運行時被解析和使用。例如,在微服務架構中,可能需要使用此選項指示服務的負載均衡策略、調用頻率限制等信息。
  • srpc.method_option_id:該選項定義了方法(method)級別的選項 ID,分別對應四個 RPC 方法的不同取值:0x010x020x030x04。這些選項可以用來控制每個方法的行爲、參數驗證規則、超時時間等等。例如,AddEmailConfig 方法的選項是 0x01,表示該方法的行爲是添加郵件配置,其他方法的選項與之類似。
通過使用 option 關鍵字定義這些選項,我們可以在Proto文件中指定更多的屬性和元數據,以便在生成gRPC代碼時進行處理和使用。一些選項可以用於控制編碼/解碼過程、調試/追蹤數據流、優化網絡傳輸等方面,從而提高系統性能和可維護性。
 
option 可以做哪些事
 
option 關鍵字可以用於爲Proto文件中的服務(service)或方法(method)定義元數據,從而影響代碼生成、運行時行爲和通信協議等方面。雖然選項並不直接影響消息的結構或語義,但它們提供了一種靈活和可擴展的方式來增加附加信息和屬性。
具體來說,option 可以用於以下場景:
  1. 控制代碼生成:使用 option 關鍵字可以控制編譯器生成代碼的方式和輸出形式。例如,可以使用 gogoproto.jsontag 選項來定義JSON標籤,以控制序列化輸出的格式;或者使用 go_package 選項指定生成Go代碼時的包名和路徑。
  2. 定義元數據:使用 option 關鍵字可以爲服務或方法定義元數據,例如版本號、權限、調用頻率限制等信息。這些元數據可以在運行時被解析和使用,以便爲系統提供更多的控制和保障。
  3. 指示運行時行爲:使用 option 關鍵字可以指示運行時行爲和規則,例如超時時間、請求重試次數、TLS設置等。這些選項可以用於優化網絡傳輸、提高系統穩定性和可靠性等方面,從而滿足不同業務需求。
  4. 擴展Protobuf語法:使用 option 關鍵字可以擴展Protobuf語法,添加新的類型、操作符和語法規則。這些擴展可以通過自定義插件或編寫DSL腳本來實現,並與其他工具集成,從而提高代碼質量和效率。
總之,option 關鍵字是一個十分強大和靈活的特性,在Proto文件的設計和實現過程中都有着廣泛的應用和價值。開發人員可以根據需要自由地定義和使用選項,以適應不同的場景和需求。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章