go-micro使用Consul做服務發現的方法和原理

波斯馬發表於2022-04-25

go-micro v4預設使用mdns做服務發現。不過也支援採用其它的服務發現中介軟體,因為多年來一直使用Consul做服務發現,為了方便和其它服務整合,所以還是選擇了Consul。這篇文章將介紹go-micro使用Consul做服務發現的方法。關於Consul的使用方式請參考我的另一篇文章:使用Consul做服務發現的若干姿勢

安裝Consul

如果你已經安裝Consul,或者對Consul很熟悉了,按照自己的方式處理Consul就行了。

這裡提供一個通過docker快速安裝Consul的方式,當然前提是你得安裝了docker。

執行命令:

docker run --name consul1 -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302  -d consul:latest

這會在docker容器中啟動一個最新版本的Consul服務,並將相關埠開放給主機。

安裝Consul外掛

使用Consul作為服務註冊和服務發現,需要先安裝go-micro的consul外掛:

go get github.com/go-micro/plugins/v4/registry/consul

服務端使用Consul

服務註冊

為了使用Consul做服務註冊,需要為go-micro server顯式的指定Consul Registry。直接看程式碼吧:

func main() {

	registry := consul.NewRegistry()

	rpcServer := server.NewServer(
		server.Name("registry-consul.service"),
		server.Address("0.0.0.0:8001"),
		server.Registry(registry),
	)

	proto.RegisterHelloHandler(rpcServer, &Hello{})

	service := micro.NewService(
		micro.Server(rpcServer),
	)

	service.Init()

	// Run server
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}
}

通過 consul.NewRegistry() 建立一個Consul 註冊中心,然後使用 server.NewServer 建立Server的時候把它設定進去;同時我們需要指定服務的名稱,這裡設定的是 registry-consul.service;另外這裡不使用隨機埠,指定了一個服務的監聽地址。這樣基本就OK了。

img

這裡並沒有指定Consul的連線地址,因為按照推薦的Consul部署方式,服務所在機器或者容器中應該部署一個Consul的客戶端,程式可以直接通過 127.0.0.1:8500 訪問到它。如果要顯示指定,可以在NewRegistry時設定:

	registry := consul.NewRegistry(
		registry.Addrs("127.0.0.1:8500"),
	)

註冊過程

通過一張圖來看一下,go-micro註冊服務到Consul時都做了什麼。

img

服務註冊關鍵是實現兩個動作:

1、註冊: rpcServer啟動的時候,會呼叫到自身的Register方法,Register方法會呼叫consul外掛的Register方法,然後呼叫到consul自身SDK提供的Agent.ServiceRegister方法,將服務註冊到Consul中。註冊的服務名稱就是NewServer時的server.Name。

2、健康上報: 即重新整理TTL,服務註冊成功後,會啟動一個定時器定時呼叫consul外掛的Register方法,這個方法內部判斷服務註冊過,則會呼叫consul自身SDK提供的Agent.PassTTL方法,重新整理Consul中對應服務的TTL。

健康檢查

go-micro服務的健康狀態是通過TTL維護的,服務需要定時去重新整理TTL,如果TTL超過指定的時間沒有被重新整理,則服務會被認為是不健康的。預設情況下有三個設定會涉及到TTL,還是先來看程式碼:

registry := consul.NewRegistry()

	regCheckFunc := func(ctx context.Context) error {
		fmt.Println(time.Now().Format("2006-01-02 15:04:05") + " do register check")
		if 1+1 == 2 {
			return nil
		}
		return errors.New("this not earth")
	}

	rpcServer := server.NewServer(
		server.Name("registry-consul.service"),
		server.Address("0.0.0.0:8001"),
		server.Registry(registry),
		server.RegisterCheck(regCheckFunc),
		server.RegisterInterval(10*time.Second),
		server.RegisterTTL(20*time.Second),
	)

	proto.RegisterHelloHandler(rpcServer, &Hello{})

	service := micro.NewService(
		micro.Server(rpcServer),
	)

	//service.Init()

	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

關於這三個設定,這裡簡單介紹下:

1、server.RegisterCheck(regCheckFunc) 服務重新整理TTL之前,會呼叫一個函式檢查服務的狀態,這個函式的返回值是error型別。預設的函式不進行任何檢查,直接返回nil,代表服務狀態正常;我們可以自己寫一個函式,進行一些檢查邏輯,比如是否要下線維護。如果返回的error不是nil,go-micro會嘗試在Consul中登出服務,則呼叫方將不會再訪問到這個服務節點。

2、server.RegisterInterval(10*time.Second) 這個設定指定程式去重新整理TLL的頻率。

3、server.RegisterTTL(20*time.Second) 這個設定指定TTL的生存週期,如果超過這個時間沒有重新整理TTL,則Consul會認為服務是不健康。

另外需要注意不要使用service.Init(),因為這裡邊會覆蓋 RegisterIntervalRegisterTTL 的設定,除非你不關心這兩個引數。關於這個問題可以參考:https://github.com/asim/go-micro/issues/2488

客戶端使用Consul

呼叫服務

為了使用Consul做服務發現,需要為go-micro service顯式的指定Consul Registry。還是直接看程式碼:

	registry := consul.NewRegistry()

	service := micro.NewService(
		micro.Client(client.NewClient()),
		micro.Registry(registry),
	)

	service.Init()
	client := proto.NewHelloService("registry-consul.service", service.Client())

	rsp, err := client.Say(context.TODO(), &proto.SayRequest{Name: "BOSSMA"})
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(rsp)

程式碼很簡單,指定consul作為服務發現元件後,呼叫服務的時候傳遞的服務名稱就會使用consul進行解析,獲取到IP、埠後,再進行實際呼叫。

發現過程

還是先來看張圖,從呼叫 XXXService 的方法 YYY 開始:

consul發現過程

整個過程分為兩個大的步驟,第一步獲取要呼叫服務的地址,第二步通過http請求呼叫服務。我們重點看第一步通過Consul獲取服務這塊。

首先進入一個Selector,就是選擇器的意思。Selector首先看快取中有沒有快取請求服務的地址資訊,如果沒有就去Consul查詢,查詢到之後再通過指定的選擇策略選出來一個地址,用於後續http請求。這裡預設的選擇策略是隨機選擇,比如查詢到這個服務有三個部署節點,隨機策略會隨機返回其中某一個地址。

除了隨機策略,go-micro還提供了一個輪詢策略,這時候需要自己建立一個Selector:

	registry := consul.NewRegistry()
	selector := selector.NewSelector(
		selector.SetStrategy(selector.RoundRobin),
		selector.Registry(registry),
	)

	service := micro.NewService(
		micro.Client(client.NewClient()),
		micro.Selector(selector),
		//micro.Registry(registry),
	)

另外從上面的示意圖中,我們可以看到Selector呼叫了Registry,所以這裡建立Selector的時候,我們把Registry設定了進去,然後再把建立的Selector設定到Service中。其實還有另一種方式,把設定Registry放到設定Selector後邊,同樣可以把Registry註冊到Selector中,但是這樣比較隱晦,還需要注意設定順序,我不推薦。不過如果在Selector之外需要使用Registry的時候,還是需要使用micro.Registry(registry)進行註冊,這個例子中並沒有相關場景。

效果展示

先啟動服務端,然後啟動客戶端,截圖如下:

img


以上就是本文的主要內容,如有錯漏歡迎反饋。

演示程式碼已上傳到Github:https://github.com/bosima/go-demo/tree/main/go-micro-registry-consul

收穫更多架構知識,請關注微信公眾號 螢火架構。原創內容,轉載請註明出處。
掃描二維碼關注公眾號

相關文章