本文以roundRobin為例介紹gRPC負載平衡實現。
代碼
https://github.com/messixukej...
在liangzhiyang/annotate-grpc-go基礎上補充了部分注釋
關鍵interface
負載平衡器:type Balancer interface{ //啟動負載平衡器,dialing的時候調用。 Start(target string, config BalancerConfig) error //通知負載平衡器由新地址串連ok Up(addr Address) (down func(error)) //擷取下一個有效負載平衡地址 Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) //地址更新通知channel,這裡返回的是全量地址 Notify() <-chan[]Address Close() error}//位址解析器介面type Resolver interface{ //解析器Resolver 用於建立 服務地址watcher。 Resolve(target string) (Watcher, error)}//服務地址發現器介面//watcher用於觀察服務地址的變化type Watcher interface{ //阻塞介面,知道有服務地址變化或者錯誤發生 Next() ([]*Update, error) Close()}
關鍵資料結構
type testWatcher struct{//用於接收地址更新update chan*naming.Update//用於表示多少個更新發生side chan int//用於通知地址注入者更新讀已結束readDone chanint}
實現流程(以RoundRobin為例)
1、注入負載平衡規則
使用Dial時,使用WithBalance注入負載平衡規則func WithBalancer(b Balancer) DialOption { return func(o *dialOptions) { o.balancer = b }}例如,這裡注入了RoundRobin 輪尋規則。cc, err := Dial("foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
2、 啟動負載平衡器
roundRobin.Start -> testNameResolver.Resolve 產生服務地址發現器 -> 建立服務地址發現任務 watchAddrUpdates(新的goroutine迴圈監控地址)
3、服務地址發現器(開啟負載平衡後,不管是初始化還是過程中的變化,都是通過服務地址發現器來擷取服務端地址)
roundRobin.watchAddrUpdates ->阻塞在watcher.Next擷取地址變更動作 -> 更新roundRobin.addrs -> 寫入roundRobin.addrCh
3.1、開工過程,根據balancer.Notify擷取服務端地址 -> 根據地址清單建立串連resetAddrConn
地址注入方式:Resolve中將服務端初始地址注入
3.2、動態變更地址 ClientConn.lbWatcher,根據balancer.Notify擷取服務端地址 -> 新增地址resetAddrConn,刪除地址tearDown。
地址注入方式:testWatcher.inject
3.3 建立串連細化resetAddrConn->resetTransport->roundRobin.Up
3.4 刪除串連細化tearDown->roundRobin.down
4、提交記錄
Invoke->getTransport->balancer.Get擷取有效地址(策略:根據connected狀態,迴圈找到有效地址。如果找不到則阻塞在waitCh)waitCh阻塞解除由3.3觸發