grpc文檔與grpc-go的實現-串連語義和API

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。> 查看原文:[blog.keyboardman.me](http://blog.keyboardman.me/2018/02/08/grpc-doc-with-grpc-connectivity-semantics-and-api/)> 文檔地址:[gRPC Connectivity Semantics and API](https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md)> 譯文:[gitbook](https://0x5010.gitbooks.io/grpc-doc-zh/content/connectivity-semantics-and-api.html)> 譯者:[0x5010](https://github.com/0x5010)# doc本文檔描述了gRPC通道的串連語義以及對RPC的相應影響。然後我們淺談下API。## 串連狀態gRPC抽象了用戶端與伺服器進行通訊的方式。用戶端通道對象可以使用多於一個DNS名稱來建立。通道封裝了一系列功能,包括名稱解析,建立TCP串連(帶有retry和backoff)以及TLS握手。通道還可以處理已建立的串連上的錯誤並重新串連,或者在HTTP/2 `GO_AWAY`的情況下,重新解析並重新串連。為了對使用者隱藏gRPC API(即程式碼)的這些活動的細節,同時暴露有關通道狀態的有意義的資訊,用具有五種狀態的狀態機器表示,定義如下:CONNECTING: 該通道正在嘗試建立串連,正在等待名稱解析,TCP串連建立或TLS握手所涉及的其中一個步驟。這可以被用作建立時的通道的初始狀態。READY: 通道已經通過TLS握手(或相當的操作)後一直成功地建立串連,並且所有後續的通訊嘗試都成功(或者正在等待而沒有任何已知的故障)。TRANSIENT_FAILURE: 出現了一些暫時的故障(如TCP三向交握逾時或socket錯誤)。此狀態下的通道最終將切換到`CONNECTING`狀態,並嘗試再次建立串連。由於重試是以指數backoff的方式完成的,所以不能串連的通道將在這個狀態下花費很少的時間,但是由於嘗試重複失敗,通道將花費越來越多的時間在這個狀態。對於許多非致命故障(例如,由於伺服器尚不可用而導致TCP串連嘗試逾時),通道可能在此狀態下花費越來越多的時間。IDLE: 這是由於缺乏新的或待處理的RPC,通道甚至不嘗試建立串連的狀態。新的RPC可以在這個狀態下建立。任何嘗試在通道上啟動RPC都會將通道的狀態變更為CONNECTING。當一個指定`IDLE_TIMEOUT`的通道上沒有RPC活動時,即在此期間沒有新的或掛起的(活動)RPC時,`READY`或`CONNECTING`通道狀態變更為`IDLE`。另外,當沒有活動或待處理的RPC時,接收`GOAWAY`的通道也應變更到IDLE狀態,以避免試圖中斷連線的伺服器的串連超載。我們將使用300秒(5分鐘)的預設`IDLE_TIMEOUT`。SHUTDOWN: 這個通道已經開始關閉了。任何新的RPC應該立即失敗。待處理的RPC可能會繼續運行,直到程式取消它們。通道可能會進入此狀態,因為程式明確要求關閉或在嘗試串連通訊期間發生了不可恢複的錯誤(截至2015年12月6日,沒有已知的錯誤(串連或通訊中)被歸類為不可恢複)。 進入此狀態的通道永遠不會改變這個狀態。下表列出了從一個狀態到另一個狀態的轉換規則以及相應的原因。`-`儲存格表示不允許的轉換。|From/To|CONNECTING|READY|TRANSIENT_FAILURE|IDLE|SHUTDOWN||-|:-:|:-:|:-:|:-:|:-:||CONNECTING |在串連建立期間增量|建立串連所需的所有步驟都成功了|在建立串連所需的任何步驟中出現任何故障|通道上沒有RPC活動直到`IDLE_TIMEOUT`|程式觸發shutdown||READY |\-|在已建立的通道上增加成功的通話|預期在已建立的通道上成功通訊時遇到任何故障|沒有活動或待處理的RPC時接收`GOAWAY`或沒有待處理的RPC直到`IDLE_TIMEOUT`|程式觸發shutdown||TRANSIENT_FAILURE|指數backoff重試等待時間結束|\-|\-|\-|程式觸發shutdown||IDLE |頻道上的任何新的RPC活動|\-|\-|\-|程式觸發shutdown||SHUTDOWN |\-|\-|\-|\-|\-|## 通道狀態API所有的gRPC庫都會公開一個通道層級的API方法來輪詢當前的通道狀態。在C++中,這種方法稱為`GetState`,並返回五個合法狀態之一的枚舉。如果通道當前是IDLE的,它也接受布爾`try_to_connect`轉換到CONNECTING,他的行為像一個RPC發生,所以它也應該重設`IDLE_TIMEOUT`。```c++grpc_connectivity_state GetState(bool try_to_connect);```所有的庫都應該公開一個API,使得程式(gRPC API的使用者)在通道狀態改變時得到通知。由於狀態變化可以很快並且與任何這樣的通知競爭,所以通知應該只是通知使用者已經發生了一些狀態改變,留給使用者輪詢目前狀態。這個API的同步版本是:```c++bool WaitForStateChange(grpc_connectivity_state source_state, gpr_timespec deadline);```當狀態是`source_state`以外的狀態時返回true,如果截止時間到期則返回false。基於非同步和期貨的API應該有一個相應的方法,允許在通道狀態改變時通知程式。請注意,每次從任何狀態轉換到其他任何狀態時都會發送通知。另一方面,合法狀態轉換的規則,即使相應的指數回退在重試之前不需要等待,也需要從串連轉換到`TRANSIENT_FAILURE`,並返回串連到每個可恢複故障。綜合的影響是應用程式可能會收到虛假的狀態更改通知。例如,在`CONNECTING`狀態的通道上等待狀態改變的應用程式可以接收狀態改變通知,但是在輪詢目前狀態時找到處於還是`CONNECTING`狀態的通道,因為該通道可能在`TRANSIENT_FAILURE`狀態中花費了無限小的時間量。---# grpc-gogo的實現基本同上,除了去掉了`try_to_connect`的功能。```gofunc (cc *ClientConn) GetState() connectivity.State {return cc.csMgr.getState()}func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connectivity.State) bool {ch := cc.csMgr.getNotifyChan()if cc.csMgr.getState() != sourceState {return true}select {case <-ctx.Done():return falsecase <-ch:return true}}```真正的功能實現在`connectivityStateManager`中。可以看到`ClientConn.WaitForStateChange`是通過建立或監聽已有`notifyChan`來感知狀態變化。而修改狀態的函數`updateState`在修改狀態後,關閉`notifyChan`來通知所有監聽goroutine狀態變更。```gotype connectivityStateManager struct {mu sync.Mutexstate connectivity.StatenotifyChan chan struct{}}func (csm *connectivityStateManager) updateState(state connectivity.State) {csm.mu.Lock()defer csm.mu.Unlock()if csm.state == connectivity.Shutdown {return}if csm.state == state {return}csm.state = stateif csm.notifyChan != nil {close(csm.notifyChan)csm.notifyChan = nil}}func (csm *connectivityStateManager) getState() connectivity.State {csm.mu.Lock()defer csm.mu.Unlock()return csm.state}func (csm *connectivityStateManager) getNotifyChan() <-chan struct{} {csm.mu.Lock()defer csm.mu.Unlock()if csm.notifyChan == nil {csm.notifyChan = make(chan struct{})}return csm.notifyChan}```在串連`connect`時會建立一個goroutine去監控狀態變化,通過`handleSubConnStateChange`(最終調用到`connectivityStateManager.updateState`)去修改狀態。```gofunc (ac *addrConn) transportMonitor() {for {var timer *time.Timervar cdeadline <-chan time.Timeac.mu.Lock()t := ac.transport// 如果有設定截止時間則產生個定時器if !ac.connectDeadline.IsZero() {timer = time.NewTimer(ac.connectDeadline.Sub(time.Now()))cdeadline = timer.C}ac.mu.Unlock()// 阻塞,直到我們收到`GoAway`或發生錯誤select {case <-t.GoAway(): // GoAwaycase <-t.Error(): // 錯誤case <-cdeadline: // 逾時ac.mu.Lock()if ac.backoffDeadline.IsZero() {ac.mu.Unlock()continue}ac.mu.Unlock()timer = nilgrpclog.Warningf("grpc: addrConn.transportMonitor didn't get server preface after waiting. Closing the new transport now.")t.Close()}if timer != nil {timer.Stop()}// 如果GoAway發生了,不管錯誤如何,適當調整我們的Keepalive參數select {case <-t.GoAway():ac.adjustParams(t.GetGoAwayReason())default:}ac.mu.Lock()if ac.state == connectivity.Shutdown {ac.mu.Unlock()return}// 在調用resetTransport之前,將串連狀態設定為TransientFailure。因為無法從READY變成CONNECTING。ac.state = connectivity.TransientFailureac.cc.handleSubConnStateChange(ac.acbw, ac.state)ac.cc.resolveNow(resolver.ResolveNowOption{})ac.curAddr = resolver.Address{}ac.mu.Unlock()// resetTransport將重新建立串連,把狀態設定為Connectingif err := ac.resetTransport(); err != nil {ac.mu.Lock()ac.printf("transport exiting: %v", err)ac.mu.Unlock()grpclog.Warningf("grpc: addrConn.transportMonitor exits due to: %v", err)if err != errConnClosing {ac.tearDown(err)}return}}}```遇到錯誤或手動調用`Close`時,將狀態設定為`Shutdown`,相關goroutine感知到狀態為`Shutdown`或得到`errConnClosing`錯誤時自己退出。```gofunc (cc *ClientConn) Close() error```雖然有`Idle`狀態,但是卻沒有和doc`IDLE_TIMEOUT`和相關的實現。目前只作為負載平衡中一些串連狀態的標記。當我們想要判斷一個串連是不是可用(`Ready`)狀態時,可以:```gofor {s := cc.GetState()if s == connectivity.Ready {break}if !cc.WaitForStateChange(ctx, s) {// ctx got timeout or canceled.// handle timeout}}```296 次點擊  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.