SOFAMosn 無損重啓/升級

說明,本文檔基於 SOFAMosn 0.4.0 版本編寫html

前言

SOFAMosn 是一款採用 GoLang 開發的 Service Mesh 數據平面代理,由螞蟻金服系統部網絡團隊、螞蟻金服中間件團隊、UC 大文娛團隊共同開發,功能和定位相似 Envoy,旨在提供分佈式,模塊化,可觀察,智能化的代理能力;她經過模塊化,分層解耦的設計,提供了可編程,事件機制,擴展性,高吞吐量的能力。git

——摘自《 SOFAMosn 的誕生和特性》github

概述

整體上看,鏈接遷移的流程以下圖:編程

圖片描述

  • MOSN 經過 forkexec 生成 New MOSN
  • MOSN 經過 domain socket 把 TCP fd 和請求數據發送給 New MOSN
  • New MOSN 轉發請求到後端(PUB2)
  • 後端 回覆響應到 New MOSN
  • New MOSN 經過 MOSN 傳遞來的 TCP fd,回覆響應到 client

此後:後端

  • mosn 退出 readloop,再也不接受該 TCP 鏈接上的數據
  • New mosn 開始 readloop,接受該 TCP 鏈接上的數據

——摘自《SOFAMosn Introduction網絡

具體實現

觸發

在 MOSN 啓動的時候,會加載包數據結構

github.com/alipay/sofa-mosn/pkg/server

在這個包加載的時候,該裏面的 serverkeeper.go 這個文件中的 init() 函數被執行。這個函數會起一個協程在捕獲 HUP 信號。dom

當 Mosn 接收到來自系統的 HUP 信號時,MOSN 首先會調用 stopStoppable 函數先讓 Admin Server 中的全部 Listener 都關閉 。而後調用 reconfigure 函數來進行配置從新加載。socket

遷移過程

舊進程的退出

觸發了 reconfigure 函數後,首先 MOSN 會設置兩個環境變量分佈式

_MOSN_GRACEFUL_RESTART=true
_MOSN_INHERITFD_FD=<number>
  1. _MOSN_GRACEFUL_RESTART 對應的包 github.com/alipay/sofa-mosn/pkg/types 中的 GracefulRestart 常量,用於告訴新啓動的 MOSN(下簡稱 New MOSN) 這個是一個優雅重啓。
  2. _MOSN_INHERITFD_FD 對應包 github.com/alipay/sofa-mosn/pkg/types 中的 InheritFd 常量,裏面存儲的是 ListenerFD 的數量(ListenerFD 就是每一個 Listener 調用 listen() 返回的 fd)。

準備好環境變量後,就調用 syscall 包的 ForkExec 按照當前 MOSN 的啓動參數進行啓動,並將環境變量和標準輸入輸出錯誤和 ListenerFD 都和 New MOSN 共享。而後,MOSN 會等 3 秒,讓 New MOSN 啓動起來。認爲 New MOSN 啓動完成後,它就會調用 StopAccept() 讓全部的 Listener 中止 Accept 新的請求(已經 Accept 的請求不會結束,socket 的監聽也不會斷),而後調用 WaitConnectionsDone 函數根據 GracefulTimeout(默認是 30秒) 設置的優雅重啓的超時時間讓全部的鏈接都完畢。接着 MOSN 就進行 Metrics 的遷移,完成後就會退出進程。

在 WaitConnectionsDone 中,MOSN 設置了一個時間長度爲 2 個 GracefulTimeout + 10秒 的時間的定時器。而後首先會 sleep 一個 GracefulTimeout 的時間,等待全部的鏈接主動關閉。而後關閉全部 server 中 connHandler 的 listeners 成員的 stopChan. 而後再 sleep 一個 GracefulTimeout + 10秒的時間,等待全部鏈接的遷移。時間過了以後,函數就會返回。此後,上層會調用 TransferMetrics 進行 Metrics 的調用 Exit 進行進程退出。

新進程的啓動

繼承 Listener 的獲取

在 New MOSN 啓動的過程當中,首先會調用 getInheritListeners。這個函數會從讀取 Old MOSN 設置的環境變量 _MOSN_GRACEFUL_RESTART,若是爲 true, 說明這是一個優雅重啓,就會讀取環境變量 _MOSN_INHERITFD_FD。因爲 Listener 是最早使用 fd 的,因此 fd 老是從3 開始,那麼全部 Listener fd 就是: 3, 4, ... , 3 + _MOSN_INHERITFD_FD。而後利用這些 fd 將 Old MOSN 的 Listener 恢復出來。從而獲取到繼承過來的 Listener。獲取完以後,會對獲取的 Listener 和配置文件進行比對,判斷其合法性。若是不合法的,或者不能新的配置裏面沒有以至繼承過來的 Listener 不須要複用,就會將其關閉。

完成了全部的初始化以後,就會啓動兩個 Unix Sock 的Server, 分別用與進行鏈接的遷移和 metrics 的遷移。用於鏈接遷移的 Unix Sock Server 會在 2 個 GracefulTimeout + 10 秒後自動關閉。

遷移過程當中,New MOSN 對每個 Unix Sock 請求都會分配一個協程去處理。

鏈接的遷移

當一個請求進來的時候,若是請求使用的協議不是 HTTP1 且不使用系統提供的事件循環的時候,MOSN 會啓動本身的 ConnIo, 調用 startReadLoop 和 startWriteLoop 來開啓針對這個請求的的讀寫循環。

讀寫數據遷移的協議

在發送請求的過程當中,首先會發送一個字節的數據, 這個字節表明了傳輸的是讀數據遷移仍是寫數據遷移。0 表明是使用讀數據遷移協議。1 表明是使用寫數據協議。若是是 0, 還會將該鏈接的 fd 以 out-of-band 的方式也發送出去。

讀數據遷移協議

圖片描述

首先是頭部分:包括 8 個字節,前 4 個字節是 data 部分的長度,後 4 個字節是 TLS 部分的長度。body 部分:接下來 data length 個字節存儲的是 readBuffer 數據。最後 TLS length 個字節存儲的是 TLS 的數據。

寫數據遷移協議

圖片描述

頭部分也是 8 個字節, 前 4 個字節存儲了 data 部分長度,後 4 個字節存儲的是 connection ID。body 部分:接下來的 data length 本身存儲的是 writeBuffer 數據。

讀數據的遷移

Old MOSN 發送

在 startReadLoop 中,MOSN 會捕獲以前提到的 stopChan 被關閉的事件。捕獲到這個事件以後,MOSN 會讓這個連接等待一個隨機的時間,而後開啓鏈接遷移的過程。

首先 MOSN 會往鏈接中的 transferChan 發一個 transferNotify(值爲1) 消息,告訴這個鏈接對應的寫循環:要開始遷移鏈接了。而後調用 transferRead 開始遷移讀鏈接,並返回一個connection ID,最後將這個 ID 再次發送給 transferChan。

在函數 transferRead 中:

  1. 和 New MOSN 先前提到的負責鏈接遷移的 transferServer 創建 unix socket 鏈接。
  2. 獲取該鏈接對應的 fd
  3. 調用 transferSendType 將傳輸數據使用的協議類型(讀數據遷移協議仍是寫數據遷移協議)和鏈接的 fd 發送給 New MOSN。
  4. 調用 transferReadSendData 將 header 部分和 body 部分傳輸給 New MOSN。若是 TLS 握手還沒結束,則 TLS length 爲 0。
  5. 接收 New MOSN 處理完這些數據以後返回的 connection ID, 並返回

New MOSN 的接收

當 New MOSN 接受到來自 Old MOSN 的數據時:

  1. New MOSN 會調用 transferRecvType, 首先接受協議的類型(一個字節), 若是是讀數據遷移的協議,還會去接受 oob(out-of-band)中的 fd,並利用個這個 fd 重建出一個鏈接,恢復監聽。
  2. 調用 transferReadRecvData 得到本次請求中的 data 部分和 tls 部分的數據。
  3. 調用 transferNewConn, 首先根據重建出的鏈接,找到 NewMOSN 中對應的 Listener。若是這是一個 TLS 鏈接,還會利用 Old MOSN 傳過來的 TLS 信息將鏈接重建成一個 TLS 鏈接。
  4. 而後該 Listener 從觸發 Listener 的 OnAccept 事件開始,處理這個鏈接的請求。當 MOSN 用於封裝鏈接的 connection 結構體創建完畢後,就標誌着這個鏈接遷移完成,並將這個 connection 結構體存儲在一個叫 transferMap 的數據結構中。
  5. 利用重建的 connection 的 id 生成一個本次遷移鏈接的 ID,回傳給 Old MOSN。
寫數據的遷移

Old MOSN 的發送

當寫循環收到讀循環從 transferChan 發過來的 transferNotify 消息時,會再讀一次 transferChan, 獲取到這一次鏈接傳輸的 ID,若是 ID 合法,則會開始監聽兩個 channel:

  1. internalStopChan: 從這個 channel 收到信號(這個 channel 被 close 了),認爲這寫數據遷移完成了,會直接退出。
  2. writeBufferChan: 這個 channel 傳送過來的是須要寫的數據,也就是須要傳送的數據。收到後就會調用 transferWrite 開始遷移。當 writeLoop 要結束的時候,會 close 掉 internalStopChan 和 writeBufferChan。觸發條件1。

在 transferWrite 中:

  1. 和 New MOSN 先前提到的負責鏈接遷移的 transferServer 創建 unix socket 鏈接。
  2. 調用 transferSendType 將傳輸數據使用的協議類型(讀數據遷移協議仍是寫數據遷移協議,此處是寫數據遷移協議)
  3. 調用 tranferWriteSendData 將 writeBuffer 裏面的內容連同從 New MOSN 返回的鏈接傳輸 ID 一塊兒發送給 New MOSN

New MOSN 的接收

當 New MOSN 接受到來自 Old MOSN 的數據時:

  1. New MOSN 會調用 transferRecvType, 接受協議的類型(一個字節),判斷是寫數據遷移協議,進入接受寫數據遷移的數據流程。
  2. 從 unix sock 中讀出要寫的 buffer, 和鏈接傳輸的 ID
  3. 根據 ID 從 transferMap 中 取出對應的 connection 結構體。並讓將傳輸過來的數據扔到 connection 結構體中的 writeBufferChan 中,進入新的 writeLoop。

至此,鏈接遷移的過成就完成了。

Metrics 的遷移

Old MOSN 退出前的最後一件事,就是把 Metrics 數據託付給 New MOSN。

協議

Metrics 的傳輸協議很簡單,包括兩部分 header 和 body

  1. Header 長度爲 4 個字節 存放的是 body length
  2. Body 的長度爲 body length 個字節。存放着要傳輸的數據,即 Metrics 數據。
Old MOSN 的發送
  1. 首先他會調 makesTransferData, 將全部的 Metrics 數據都統一收集起來。
  2. 和 New MOSN 先前提到的負責 Metrics 遷移的 transferServer 創建 unix socket 鏈接。
  3. 前後把 header 和 body 發送給 New MOSN
  4. 若是設置了等待 New MOSN 的響應,會在一個超市時間內等待 New MOSN 1 個字節的返回。
New MOSN 的接收

當 New MOSN 接受到來自 Old MOSN 的數據時,會調 serveConn 函數去處理每個遷移請求:

  1. 讀取數據中的協議頭,並根據協議頭讀取出報文體。
  2. 將報文體的數據恢復成成 go-metrics 的數據,供 New MOSN 使用

至此,全部關於平滑重啓的操做就完成了。

原文地址:https://blog.coordinate35.cn/...

相關文章
相關標籤/搜索