基於Golang的微服務——Micro實踐(一)

winyh發表於2019-07-13

這篇文章主要是微服務服務端的實踐。

開始開發前需要先配置好Go的開發環境,可以看我寫的基於Golang的微服務——上手篇

在 GOPATH目錄下的src目錄下建立我們的實戰專案目錄 tech,切換到這個目錄

go get github.com/micro/go-micro //用於開發的微服務的RPC框架,是micro架構的基礎

go get github.com/micro/protoc-gen-micro // 用於生成Protobuf的程式碼

go get github.com/micro/micro // 工具集安裝,會自動將 micro加入環境變數
複製程式碼

Go Micro

Go Micro提供分散式系統開發的核心庫,包含RPC與事件驅動的通訊機制。micro的設計哲學是可插拔的架構理念,她提供可快速構建系統的元件,並且可以根據自身的需求剝離預設實現並自行定製。

Go Micro主要特性

Go Micro 把分散式系統的各種細節抽象出來

  • 服務發現(Service Discovery) - 自動服務註冊與名稱解析。服務發現是微服務開發中的核心。當服務A要與服務B協作時,它得知道B在哪裡。預設的服務發現系統是Consul,而multicast DNS (mdns,組播)機制作為本地解決方案,或者零依賴的P2P網路中的SWIM協議(gossip)
  • 負載均衡(Load Balancing) - 在服務發現之上構建了負載均衡機制。當我們得到一個服務的任意多個的例項節點時,我們要一個機制去決定要路由到哪一個節點。我們使用隨機處理過的雜湊負載均衡機制來保證對服務請求頒發的均勻分佈,並且在發生問題時進行重試。
  • 訊息編碼(Message Encoding) - 支援基於內容型別(content-type)動態編碼訊息。客戶端和服務端會一起使用content-type的格式來對Go進行無縫編/解碼。各種各樣的訊息被編碼會傳送到不同的客戶端,客戶端服服務端預設會處理這些訊息。content-type預設包含proto-rpc和json-rpc。
  • Request/Response - RPC通訊基於支援雙向流的請求/響應方式,我們提供有抽象的同步通訊機制。請求傳送到服務時,會自動解析、負載均衡、撥號、轉成位元組流,預設的傳輸協議是http/1.1,而tls下使用http2協議。
  • 非同步訊息(Async Messaging) - 釋出訂閱(PubSub)頭等功能內建在非同步通訊與事件驅動架構中。事件通知在微服務開發中處於核心位置。預設的訊息傳送使用點到點http/1.1,啟用tls時則使用http2。
  • 可插拔介面(Pluggable Interfaces) - Go Micro為每個分散式系統抽象出介面。因此,Go Micro的介面都是可插拔的,允許其在執行時不可知的情況下仍可支援。所以只要實現介面,可以在內部使用任何的技術

Go micro 組成包

  • transport 用於同步訊息
  • broker 用於非同步訊息
  • codec 用於訊息編碼
  • registry 用於服務發現
  • selector 用於負載均衡
  • client 用於傳送請求
  • server 用於處理請求

註冊(Registry) 註冊提供了服務發現機制來解析服務名到地址上。它可以使用Consul、etcd、zookeeper、dns、gossip等等提供支援。服務使用啟動註冊關機解除安裝的方式註冊。服務可以選擇性提供過期TTL和定時重註冊來保證服務線上,以及在服務不線上時把它清理掉。

選擇器(Selector) 選擇器是構建在註冊這上的負載均衡抽象。它允許服務被過濾函式過濾掉不提供服務,也可以通過選擇適當的演算法來被選中提供服務,演算法可以是隨機、輪詢(客戶端均衡)、最少連結(leastconn)等等。選擇器通過客戶端建立語法時發生作用。客戶端會使用選擇器而不是登錄檔,因為它提供內建的負載均衡機制。

傳輸(Transport) Transport是服務與服務之間同步請求/響應的通訊介面。和Golang的net包類似,但是提供更高階的抽象,請允許我們可以切換通訊機制,比如http、rabbitmq、websockets、NATs。傳輸也支援雙向流,這一強大的功能使得客戶端可以向服務端推送資料。

代理(Broker) Broker提供非同步通訊的訊息釋出/訂閱介面。對於微服務系統及事件驅動型的架構來說,釋出/訂閱是基礎。一開始,預設我們使用收件箱方式的點到點HTTP系統來最小化依賴的數量。但是,在go-plugins是提供有訊息代理實現的,比如RabbitMQ、NATS、NSQ、Google Cloud Pub Sub等等。

編碼(Codec) 編碼包用於在訊息傳輸到兩端時進行編碼與解碼,可以是json、protobuf、bson、msgpack等等。與其它編碼方式不同,我們支援RPC格式。所以我們有JSON-RPC、PROTO-RPC、BSON-RPC等格式。

編碼包把客戶端與服務端的編碼隔離開來,並提供強大的方法來整合其它系統,比如gRPC、Vanadium等等。

Server(服務端) Server包是使用編寫服務的構建包,可以命名服務,註冊請求處理器,增加中介軟體等等。服務構建在以上說的包之上,提供獨立的介面來服務請求。現在服務的構建是RPC系統,在未來可能還會有其它的實現。服務端允許定義多個不同的編碼來服務不同的編碼訊息。

Client(客戶端) 客戶端提供介面來建立向服務端的請求。與服務端類似,它構建在其它包之上,它提供獨立的介面,通過註冊中心來基於名稱發現服務,基於選擇器(selector)來負載均衡,使用transport、broker處理同步、非同步訊息。

上面的這些元件都可以在micro中,從更高的角度看成是服務(Service)

官方的Greeter示例

如果按照文章開頭執行的那三個命令列,用的GOPROXY 代理下載的依賴包,會有一個問題,獲取到的examples不是最新的。會有很多報錯,我自己的解決辦法是直接將目錄切換到

cd  $GOPATH/pkg/mod/github/micro

git clone github.com/micro/examples  examples

cd examples/greeter
複製程式碼

用 git clone 直接下載最新的官網示例包 greeter示例 按照這個示例的說明文件執行 最後發現除錯 API 時出現報錯

{"id":"go.micro.client","code":500,"detail":"error selecting go.micro.srv.greeter node: not found","status":"Internal Server Error"}
複製程式碼

剛開始找不到原因,去github用蹩腳英語提問,丟人現眼了一回。哈哈,但是學到東西就行,我是這麼安慰自己的。 我在執行示例的時候,只執行了三個指令碼:

go run api/api.go 

micro api --handler=api

curl http://localhost:8080/greeter/say/hello?name=John
複製程式碼

前兩個命令列正常,第三個報錯。去提issue,有個老外很快回復我了,但我還是一臉懵逼。最後嘗試先執行

go run srv/main.go
複製程式碼

就正常了,目前程式碼裡的邏輯理解的不深入,以後回過頭來探個究竟吧.最新版本的註冊中心沒有指定consul了,執行micro web,可以啟動 micro web工具集合,訪問localhost:8082就可以看到註冊的微服務名稱了。

基於Golang的微服務——Micro實踐(一)
也可以本地啟動 consul,然後將consul設定為註冊中心,執行命令:

go run main.go --registry=consul 
複製程式碼

就可以在consul自帶的UI控制皮膚裡找到對應註冊的服務(go.micro.srv.greeter)

基於Golang的微服務——Micro實踐(一)

題外話:個人經驗,日常開發過程中如果遇到問題跨不過去,可以多查查資料,請教別人,一時解決不了的暫時放下,隔段時間回頭在看,很多時候都會有新的思路和解決辦法,不要死懟著問題不放。還有就是程式碼如果邏輯實現太複雜了,請停下來,想下是不是自己的實現思路有問題,多半就是實現方式有問題,不會有很複雜的程式碼的。血淚教訓,以前自己遇到問題了就是猛懟,身邊也沒有人可以請教。其實那是不對的,很浪費時間。

編寫自己的服務

官方示例執行正常了就來嘗試下寫自己的服務吧。根據之前提到的

如何才能使用micro

  • 使用go-micro編寫服務。
  • 使用micro工具集來訪問這些服務

來試試按照這兩步走會遇到什麼妖魔鬼怪吧

服務原型

微服務中有個關鍵需求點,就是介面的強定義。Micro使用protobuf來完成這個需求。 GOPATH 資料夾下新建資料夾 popular/proto

touch popular.proto
複製程式碼

建立好原型檔案後,編輯檔案內容

syntax = "proto3";

service Popular {
	rpc Ping(PingRequest) returns (PingResponse) {}
}

message PingRequest {
	string name = 1;
}

message PingResponse {
	string popularing = 2;
}
複製程式碼

檔案的第一行指定了你使用的是proto3的語法. 我們定義Popular處理器,它有一個Ping方法。它有PingRequest入參物件及PingResponse出參物件,兩個物件都有一個字串型別的引數。

生成原型

在定義好原型後我們得使用protoc及micro的外掛編譯它,micro外掛可以幫助生成go micro需要的原型檔案。切換到$GOPATH/src 目錄,執行命令:

protoc --proto_path=$GOPATH/src/popular/proto:. --micro_out=. --go_out=. popular/proto/popular.proto
複製程式碼

執行這個命令時可能會報錯

-bash: protoc: command not found
複製程式碼

那是需要下面這個工具來生成protobuf程式碼檔案,它們負責生成定義的go程式碼實現

  • protoc
  • protoc-gen-go
  • protoc-gen-micro
brew install protobuf // 如果失敗就下載原始碼包自己編譯,加入到環境變數,我是這麼整的

go get github.com/golang/protobuf/{proto,protoc-gen-go}

go get github.com/micro/protoc-gen-micro
複製程式碼

protobuf 原始碼編譯安裝會有一些報錯,忽略就行。安裝參考

一切正常後會在 proto 資料夾下生成

popular.micro.go	popular.pb.go
複製程式碼

編寫服務

需要實現幾個需求:

  • 實現在 Popular Handler中定義的介面。
  • 初始化 micro.Service
  • 註冊 Popular handler
  • 執行服務

切換到 popular資料夾下,建立檔案 main.go

cd popular && touch main.go
複製程式碼

編輯main.go 檔案

package main

import (
	"context"
	"fmt"
	micro "github.com/micro/go-micro"
	proto "popular/proto"
)

type Popular struct{}

func (g *Popular) Ping(ctx context.Context, req *proto.PingRequest, rsp *proto.PingResponse) error {
	rsp.Popularing = "Ping " + req.Name // (Popularing, Name 首字母都要大寫 )
	return nil
}

func main() {

	// 建立新的服務,這裡可以傳入其它選項。
	service := micro.NewService(
		micro.Name("popular"),
	)

	// 初始化方法會解析命令列標識
	service.Init()

	// 註冊處理器
	proto.RegisterPopularHandler(service.Server(), new(Popular))

	// 執行服務
	if err := service.Run(); err != nil {
		fmt.Println(err)
	}
}
複製程式碼

[卡在這裡不能動了,原因不說。。。] 幹了一整天,終於把微服務搭建起來了,有時間再整理文章吧,得休息下了,頭暈眼花。。 貼一張圖:服務發現與註冊用的是Consul

基於Golang的微服務——Micro實踐(一)

這篇關於服務端的文章到這裡暫告一段落

想繼續深入可以看我下一篇文章:基於Golang的微服務——Micro實踐(二)

相關文章