3.微服務--GRPC

1.gRPC入門

1.1gRPC簡介

 gRPC 由 google 開發,是一款語言中立、平臺中立、開源的遠程過程調用系統
 gRPC 客戶端和服務端可以在多種環境中運行和交互,例如用 java 寫一個服務端,可以用 go 語言寫客戶端調用

1.2gRPC與Protobuf介紹

微服務架構中,由於每個服務對應的代碼庫是獨立運行的,無法直接調用,彼此間的通信就是一個大問題。
gRPC可以實現微服務,將大的項目拆分成多個小且獨立的業務模塊,即服務,各服務間使用搞笑的protobuf協議進行RPC調用,gRPC默認使用protocol buffers,這是google開源的一套成熟的結構數據序列化機制(也可以使用其他數據格式如JSON)
可以用proto files創建gRPC服務,用message類型來定義方法參數和返回類型

1.3安裝gRPC和Protobuf

⚫ go get -u -v github.com/golang/protobuf/proto
⚫ go get google.golang.org/grpc(無法使用,用如下命令代替) ◼ git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
◼ git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
◼ git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
◼ go get -u github.com/golang/protobuf/{proto,protoc-gen-go} ◼ git clone https://github.com/google/go-genproto.git 
$GOPATH/src/google.golang.org/genproto
◼ cd $GOPATH/src/
◼ go install google.golang.org/grpc
⚫ go get github.com/golang/protobuf/protoc-gen-go

⚫ 上面安裝好後,會在 GOPATH/bin 下生成 protoc-gen-go.exe
⚫ 但還需要一個 protoc.exe,windows 平臺編譯受限,很難自己手動編譯,直接去網 站下載一個,地址:https://github.com/protocolbuffers/protobuf/releases/tag/v3.9.0 ,
同樣放到 GOPATH/bin下

2 Protobuf語法

2.1基本規範

⚫ 文件以.proto 做爲文件後綴,除結構定義外的語句以分號結尾
⚫ 結構定義可以包含:message、service、enum
⚫ rpc 方法定義結尾的分號可有可無
⚫ Message 命名採用駝峯命名方式,字段命名採用小寫字母加下劃線分隔方式
message SongServerRequest {
required string song_name = 1;
} 
⚫ Enums 類型名採用駝峯命名方式,字段命名採用大寫字母加下劃線分隔方式
enum Foo {
FIRST_VALUE = 1;
SECOND_VALUE = 2;
} 
⚫ Service 與 rpc 方法名統一採用駝峯式命名

2.2字段規則

⚫ 字段格式:限定修飾符 | 數據類型 | 字段名稱 | = | 字段編碼值 | [字段默認值]
⚫ 限定修飾符包含 required\optional\repeated
◼ Required: 表示是一個必須字段,必須相對於發送方,在發送消息之前必須設置該字段的值,對於接收方,必須能夠識別該字段的意思。發送之前沒有設置required 字段或者無法識別 required 字段都會引發編解碼異常,導致消息被丟 棄 
◼ Optional:表示是一個可選字段,可選對於發送方,在發送消息時,可以有選擇性的設置或者不設置該字段的值。對於接收方,如果能夠識別可選字段就進行相應的處理,如果無法識別,則忽略該字段,消息中的其它字段正常處理。
---因爲 optional 字段的特性,很多接口在升級版本中都把後來添加的字段都統一的設置爲 optional 字段,這樣老的版本無需升級程序也可以正常的與新的軟件進行通信,只不過新的字段無法識別而已,因爲並不是每個節點都需要新的功能,因此可以做到按需升級和平滑過渡
◼ Repeated:表示該字段可以包含 0~N 個元素。其特性和 optional 一樣,但是每一次可以包含多個值。可以看作是在傳遞一個數組的值
⚫ 數據類型
◼ Protobuf 定義了一套基本數據類型。幾乎都可以映射到 C++\Java 等語言的基礎數據類型

3.微服務--GRPC

◼ N 表示打包的字節並不是固定。而是根據數據的大小或者長度
◼ 關於 fixed32 和 int32 的區別。fixed32 的打包效率比 int32 的效率高,但是使用的空間一般比 int32 多。因此一個屬於時間效率高,一個屬於空間效率高
⚫ 字段名稱
◼ 字段名稱的命名與 C、C++、Java 等語言的變量命名方式幾乎是相同的 
◼ protobuf 建議字段的命名採用以下劃線分割的駝峯式。例如 first_name 而不是firstName
⚫ 字段編碼值
◼ 有了該值,通信雙方纔能互相識別對方的字段,相同的編碼值,其限定修飾符和數據類型必須相同,編碼值的取值範圍1~2^32(4294967296)
◼ 其中 1~15 的編碼時間和空間效率都是最高的,編碼值越大,其編碼的時間和空間效率就越低,所以建議把經常要傳遞的值把其字段編碼設置爲1-15 之間的值
◼ 1900~2000 編碼值爲 Google protobuf 系統內部保留值,建議不要在自己的項目中使用
⚫ 字段默認值
◼ 當在傳遞數據時,對於 required 數據類型,如果用戶沒有設置值,則使用默認值傳遞到對端

2.3service如何定義

⚫ 如果想要將消息類型用在 RPC 系統中,可以在.proto 文件中定義一個 RPC 服務接口,protocol buffer 編譯器會根據所選擇的不同語言生成服務接口代碼
⚫ 例如,想要定義一個 RPC 服務並具有一個方法,該方法接收 SearchRequest 並返回一個 SearchResponse,此時可以在.proto 文件中進行如下定義:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse) {}
} 
⚫ 生成的接口代碼作爲客戶端與服務端的約定,服務端必須實現定義的所有接口方法,客戶端直接調用同名方法向服務端發起請求,比較麻煩的是,即便業務上不需要參數也必須指定一個請求消息,一般會定義一個空 message

2.4Message如何定義

⚫ 一個 message 類型定義描述了一個請求或響應的消息格式,可以包含多種類型字段
⚫ 例如定義一個搜索請求的消息格式,每個請求包含查詢字符串、頁碼、每頁數目
⚫ 字段名用小寫,轉爲 go 文件後自動變爲大寫,message 就相當於結構體
syntax = "proto3";
message SearchRequest {
string query = 1; // 查詢字符串
int32 page_number = 2; // 頁碼
int32 result_per_page = 3; // 每頁條數
} 
⚫ 首行聲明使用的 protobuf 版本爲 proto3
⚫ SearchRequest 定義了三個字段,每個字段聲明以分號結尾,.proto 文件支持雙斜
線 // 添加單行註釋

2.5更多Message類型

⚫ 一個.proto 文件中可以定義多個消息類型,一般用於同時定義多個相關的消息,例如在同一個.proto 文件中同時定義搜索請求和響應消息
syntax = "proto3";
// SearchRequest 搜索請求
message SearchRequest {
string query = 1; // 查詢字符串
int32 page_number = 2; // 頁碼
int32 result_per_page = 3; // 每頁條數
}
// SearchResponse 搜索響應
message SearchResponse {
...
}

2.6如何使用其他Message

⚫ message 支持嵌套使用,作爲另一 message 中的字段類型
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}

2.7Message 嵌套的使用

⚫ 支持嵌套消息,消息可以包含另一個消息作爲其字段。也可以在消息內定義一個新的消息
⚫ 內部聲明的 message 類型名稱只可在內部直接使用

message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}repeated Result results = 1;
} 
⚫ 另外,還可以多層嵌套
message Outer { // Level 0
message MiddleAA { // Level 1
message Inner { // Level 2
int64 ival = 1;
bool booly = 2;
} }
message MiddleBB { // Level 1
message Inner { // Level 2
int32 ival = 1;
bool booly = 2;
} } }

2.8proto3的Map類型

⚫ proto3 支持 map 類型聲明
map<key_type, value_type> map_field = N;
message Project {...}
map<string, Project> projects = 1;
⚫ 鍵、值類型可以是內置的類型,也可以是自定義 message 類型
⚫ 字段不支持 repeated 屬性

2.9.proto文件編譯

⚫ 通過定義好的.proto文件生成Java, Python, C++, Go, Ruby,JavaNano, Objective-C, or C# 代碼,需要安裝編譯器 protoc
⚫ 當使用 protocol buffer 編譯器運行.proto 文件時,編譯器將生成所選語言的代碼,用於使用在.proto 文件中定義的消息類型、服務接口約定等。不同語言生成的代碼格式不同:
◼ C++: 每個.proto 文件生成一個.h  的文件和一個.cc 文件,每個消息類型對應一個類 
◼ Java: 生成一個.java 文件,同樣每個消息對應一個類,同時還有一個特殊的Builder 類用於創建消息接口
◼ Python: 姿勢不太一樣,每個.proto 文件中的消息類型生成一個含有靜態描述符的模塊,該模塊與一個元類 metaclass 在運行時創建需要Python 數據訪問類 
◼ Go: 生成一個.pb.go 文件,每個消息類型對應一個結構體
◼ Ruby: 生成一個.rb 文件的 Ruby 模塊,包含所有消息類型
◼ JavaNano: 類似 Java,但不包含 Builder 類
◼ Objective-C: 每個.proto 文件生成一個 pbobjc.h 和一個 pbobjc.m 文件
◼ C#: 生成.cs 文件包含,每個消息類型對應一個類

2.10import導入定義

⚫ 可以使用 import 語句導入使用其它描述文件中聲明的類型
⚫ protobuf 接口文件可以像 C 語言的 h 的文件一個,分離爲多個,在需要的時候通過import 導入需要對文件。其行爲和 C 語言的#include 或java 的 import 的行爲大致相同,例如 import "others.proto";
⚫ protocol buffer 編譯器會在 -I / --proto_path 參數指定的目錄中查找導入的文件,如果沒有指定該參數,默認在當前目錄中查找

2.11包的使用

⚫ 在.proto 文件中使用 package 聲明包名,避免命名衝突
syntax = "proto3";
package foo.bar;
message Open {...}
⚫ 在其他的消息格式定義中可以使用包名+消息名的方式來使用類型,如
message Foo {
...
foo.bar.Open open = 1;
...
} 
⚫ 在不同的語言中,包名定義對編譯後生成的代碼的影響不同
◼ C++ 中:對應 C++命名空間,例如 Open 會在命名空間 foo::bar 中 ◼ Java 中:package 會作爲 Java 包名,除非指定了 option jave_package 選項
◼ Python 中:package 被忽略
◼ Go 中:默認使用 package 名作爲包名,除非指定了 option go_package 選項
◼ JavaNano 中:同 Java
◼ C# 中:package 會轉換爲駝峯式命名空間,如 Foo.Bar,除非指定了 option csharp_namespace 選項

3.使用gRPC構建微服務

做一個處理用戶信息的微服務
客戶端通過用戶名,可以從服務端查詢用戶的基本信息
gRPC
proto
    user.proto 定義客戶端請求、服務端響應的數據格式
    user.pb.go 自動生成的,爲數據交互提供的函數
    server.go 微服務服務端
    client.go 微服務客戶端

3.1編寫proto文件-user.proto

//版本號
syntax = "proto3";

//指定包名
package proto;

//定義結構體
message UserRequest {
    //定義用戶名
    string name = 1;
}

//響應結構體
message UserResponse{
    int32 id = 1;
    string name = 2;
    int32 age = 3;
    //repeated修飾符是可變數組,go轉切片
    repeated string hobby = 4;
}

//service定義方法
service UserInfoService {
    rpc GetUserInfo (UserRequest) returns (UserResponse){}
}

3.2生成.go文件

⚫ pycharm 中打開命令行,輸入命令生成接口文件:
⚫ protoc -I . --go_out=plugins=grpc:. ./user.proto

3.微服務--GRPC

3.3編寫服務端-server.go

package main

import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "net"
    pb "test/gRPC/proto"
)

//1.需要監聽
//2.需要實例化gRPC服務端
//3.在gRPC上註冊微服務
//4.啓動服務端

//定義空接口
type UserInfoService struct{}

var u = UserInfoService{}

//實現方法
func (s *UserInfoService) GetUserInfo(ctx context.Context, req *pb.UserRequest) (resp *pb.UserResponse, err error)  {
    //通過用戶名查詢用戶信息
    name := req.Name
    //數據庫中查詢用戶信息
    if name == "zs" {
        resp = &pb.UserResponse{
            Id:                   1,
            Name:                 name,
            Age:                  0,
            Hobby:                []string{"Sing", "Run"},

        }
    }
    return
}

func main()  {
    //地址
    addr := "127.0.0.1:8080"
    //1.監聽
    listener ,err := net.Listen("tcp", addr)
    if err != nil {
        fmt.Printf("監聽異常:%s\n", err)
    }
    fmt.Printf("監聽端口:%s\n",addr)
    //2.實例化gRPC
    s := grpc.NewServer()
    //3.在gRPC上註冊微服務
    pb.RegisterUserInfoServiceServer(s, &u)
    //4.啓動服務端
    s.Serve(listener)
}

3.4編寫客戶端-client.go

package main
import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    pb "test/gRPC/proto"
)

//1.連接服務端
//2.實例gRPC
//3.調用

func main()  {
    //1.連接
    conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())
    if err != nil {
        fmt.Printf("連接異常:%s\n",err)
    }
    defer conn.Close()
    //2.實例化gRPC客戶端
    client := pb.NewUserInfoServiceClient(conn)
    //3.組裝請求參數
    req := new(pb.UserRequest)
    req.Name = "zs"
    //4.調用接口
    response, err := client.GetUserInfo(context.Background(),req)
    if err != nil {
        fmt.Printf("連接異常:%s\n",err)
    }
    fmt.Printf("響應結果:%v\n", response)
}

3.微服務--GRPC
3.微服務--GRPC

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章