微服務是一種開發軟體的架構和組織方法,其中軟體由通過明確定義的API 進行通訊的小型獨立服務組成。 這些服務由各個小型獨立團隊負責。 微服務架構使應用程式更易於擴充套件和更快地開發,從而加速創新並縮短新功能的上市時間。
將軟體應用程式構建為一組獨立、自治(獨立開發、部署和擴充套件)、鬆耦合、面向業務能力(強調能力,而不是完成任務)的服務。
為什麼微服務軟體系統需要藉助程序間(服務間,應用程式間)通訊技術?
傳統軟體系統被進一步拆分為一組細粒度,自治和麵向業務能力的實體,也就是微服務。
服務API介面有強、弱型別之分。
強型別介面
傳統的RPC服務(客製化二進位制協定 ,對訊息進行編碼和解碼),採用TCP傳輸訊息。RPC服務通常有嚴格的契約,開發服務前需要使用IDL(Interface description language)定義契約,最終通過契約自動生成強型別的伺服器端、使用者端的介面。服務呼叫直接使用強型別的使用者端。(GRPC、Thrift)
弱型別介面
Restful服務通常採用JSON作為傳輸訊息,使用HTTP作為傳輸協定,沒有嚴格契約的概念,使用普通的HTTP Client即可呼叫。呼叫方需要對JSON訊息進行手動的編碼和解碼工作。(Springboot)
描述性狀態轉移架構,是面向資源架構的基礎。將分散式應用程式建模為資源集合,存取這些資源的使用者端可以變更這些資源的狀態。有三大侷限性。
基於文字的低效訊息協定
應用程式之間缺乏強型別介面
架構風格難以強制實施
gRPC 是一項程序間通訊技術,用來連線、呼叫、操作和偵錯分散式異構應用程式。
開發gRPC應用需要先定義服務介面,使用的語言叫做 介面定義語言
建立 client、service 目錄,分別用指令生成 go.mod 檔案
go mod init productinfo/client
go mod init productinfo/service
PS C:\Users\小能喵喵喵\Desktop\Go\gRPC\chapter2\productinfo> tree /f
├─client
│ │ client.go
│ │ go.mod
│ │ go.sum
│ │
│ ├─bin
│ │ client.exe
│ │
│ └─ecommerce
│ product_info.pb.go
│ product_info.proto
│ product_info_grpc.pb.go
│
└─service
│ go.mod
│ go.sum
│ server.go
│ service.go
│
├─bin
│ server.exe
│
└─ecommerce
product_info.pb.go
product_info.proto
product_info_grpc.pb.go
syntax = "proto3";
package ecommerce;
option go_package = ".";
service ProductInfo {
rpc addProduct (Product) returns (ProductID);
rpc getProduct (ProductID) returns (Product);
}
message Product{
string id = 1;
string name = 2;
string description = 3;
float price = 4;
}
message ProductID {
string value = 1;
}
安裝:Release Protocol Buffers v21.6 · protocolbuffers/protobuf (github.com)
教學:Go Generated Code | Protocol Buffers | Google Developers
注: $GOPATH/bin
要新增到系統環境變數裡
protoc-gen-go-grpc: program not found or is not executable 解決方案
go get -u google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go
go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
Please specify either:
• a "go_package" option in the .proto source file, or
• a "M" argument on the command line.
解決方案 在syntax=」proto3″;下一行增加option go_package設定項。
protoc [opt...] file.proto
/* 例如 */
protoc --proto_path=src --go_out=out --go_opt=paths=source_relative foo.proto
protoc --go_out=plugins=grpc:. *.proto
不再使用,轉而用protoc --go_out=. --go-grpc_out=. ./hello.proto
代替。go_out=.
指定生成的pb.go
檔案所在目錄(如果沒有該目錄,需要手動提前建立),.
代表當前protoc執行目錄,結合.proto
檔案中的option go_package
,其最終的生成檔案目錄為go_out指定目錄/go_package指定目錄
,go-grpc_out
針對_grpc.pb.go
檔案,同理。--go_opt=paths=source_relative
,其含義代表生成的.pb.go
檔案路徑不依賴於.proto
檔案中的option go_package
設定項,直接在go_out
指定的目錄下生成.pb.go
檔案(.pb.go
檔案的package名還是由option go_package
決定)。--go-grpc_opt=paths=source_relative
,針對_grpc.pb.go
檔案,同理。PS C:\Users\小能喵喵喵\Desktop\Go\gRPC\chapter2\productinfo\service\ecommerce>
protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative ./product_info.proto
Product和ProductID結構體定義在product_info.pb.go檔案中,通過product_info.proto自動生成。
package main
import (
"context"
pb "productinfo/service/ecommerce" // ^ 匯入 protobuf編譯器生成程式碼的包
"github.com/google/uuid"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// ^ 實現 service/ecommerce 服務
type service struct {
pb.UnimplementedProductInfoServer
productMap map[string]*pb.Product
}
// ^ 實現方法邏輯、新增商品
// ^ ctx 物件包含一些後設資料,比如終端使用者授權令牌標識和請求的截止時間
func (s *service) AddProduct(ctx context.Context, in *pb.Product) (*pb.ProductID, error) {
out, err := uuid.NewUUID() // ^ 通用唯一標示符
if err != nil {
return nil, status.Errorf(codes.Internal, "生成產品編碼時出錯", err)
}
in.Id = out.String()
if s.productMap == nil {
s.productMap = make(map[string]*pb.Product)
}
s.productMap[in.Id] = in
return &pb.ProductID{Value: in.Id}, status.New(codes.OK, "").Err()
}
func (s *service) GetProduct(ctx context.Context, in *pb.ProductID) (*pb.Product, error) {
value, exists := s.productMap[in.Value]
if exists {
return value, status.New(codes.OK, "").Err()
}
return nil, status.Errorf(codes.NotFound, "商品條目不存在", in.Value)
}
前向相容一般指向上相容。 向上相容(Upward Compatible)又稱向前相容(Forward Compatible),在某一平臺的較低版本環境中編寫的程式可以在較高版本的環境中執行。
新版protoc-gen-go不支援grpc服務生成,需要通過protoc-gen-go-grpc生成grpc服務介面,但是生成的Server端介面中會出現一個mustEmbedUnimplemented***方法,為了解決前向相容問題(現在的相容未來的),如果不解決,就無法傳遞給RegisterXXXService方法。
package main
import (
"fmt"
"log"
"net"
pb "productinfo/service/ecommerce"
"google.golang.org/grpc"
)
const (
ip = "127.0.0.1"
port = "23333"
)
func main() {
lis, err := net.Listen("tcp", fmt.Sprintf("%v:%v", ip, port))
if err != nil {
log.Fatalf("無法監聽埠 %v %v", port, err)
}
s := grpc.NewServer()
pb.RegisterProductInfoServer(s, &service{})
log.Println("gRPC伺服器開始監聽", port)
if err := s.Serve(lis); err != nil {
log.Fatalf("提供服務失敗: %v", err)
}
}
2022/09/18 17:17:30 gRPC伺服器開始監聽 23333
建立一個client目錄,並重新之前mod init、編譯proto的操作。
PS C:\Users\小能喵喵喵\Desktop\Go\gRPC\chapter2\productinfo\client\ecommerce>
protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative ./product_info.proto
grpc.WithInsecure is Deprecated: use WithTransportCredentials and insecure.NewCredentials() instead. Will be supported throughout 1.x.
The function insecure.NewCredentials
returns an implementation of credentials.TransportCredentials
.
grpc.Dial(":9950", grpc.WithTransportCredentials(insecure.NewCredentials()))
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "productinfo/client/ecommerce"
)
const (
address = "localhost:23333"
)
func main() {
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials())) // ^ 不安全地建立端到端連線
if err != nil {
log.Fatalf("did not connect: %v", err)
}
c := pb.NewProductInfoClient(conn) // ^ 傳遞連線並建立存根範例,包含所有遠端呼叫方法
name := "小米 10 Pro"
description := "雷軍說:Are you ok?"
price := float32(2000.0)
ctx, cancel := context.WithTimeout(context.Background(), time.Second) // ^ 用於傳遞後設資料:使用者標識,授權令牌,請求截止時間
defer cancel()
r, err := c.AddProduct(ctx, &pb.Product{
Name: name, Price: price, Description: description,
})
if err != nil {
log.Fatalf("無法新增商品 %v", err)
}
log.Printf("新增商品成功 %v", r.Value)
/* -------------------------------------------------------------------------- */
product, err := c.GetProduct(ctx, &pb.ProductID{Value: r.Value})
if err != nil {
log.Fatalf("獲取不到商品 %v", err)
}
log.Println("Product: ", product.String())
}
分別進入service,client資料夾執行如下命令。構建二進位制檔案並執行(可以交叉編譯執行在其他作業系統上)。
PS C:\Users\小能喵喵喵\Desktop\Go\gRPC\chapter2\productinfo\service>
go build -o bin/server.exe
PS C:\Users\小能喵喵喵\Desktop\Go\gRPC\chapter2\productinfo\client>
go build -o bin/client.exe
《Go語言並行之道》Katherine CoxBuday
《Go語言核心程式設計》李文塔
《Go語言高階程式設計》柴樹彬、曹春輝
《Grpc 與雲原生應用開發》卡山·因德拉西里、丹尼什·庫魯普