今天主要討論一下,對於分布式服務,站點如何平滑的上下線問題。
在分布式服務下,我們會用nginx做負載均衡, 業務站點訪問某服務站點的時候, 統一走nginx, 然后nginx根據一定的輪詢策略,將請求路由到后端一台指定的服務器上。


這樣的架構是沒有問題的, 但是我們這里考慮幾個問題,
1. 網站上下線問題:我們網站平時更新站點的時候是直接覆蓋文件,然后重啟, 那這樣會造成一些請求中斷,如果是非核心邏輯那還好, 如果是核心邏輯,那請求中斷,會影響一些數據一致性,比如資金, 交易,訂單等。
2. 動態加減機器,比如某個站點訪問量大,要新增機器,那就需要修改nginx的配置,然后reload, 這樣會中斷連接。 雖然reload很快,但是還是會有一瞬間的請求中斷。
對於第一個問題,我們可以在請求量少的時候去更新, 但是這種在一些服務穩定的公司可用, 對於互聯網企業,可能2-3天就一個版本, 而且需要立刻上線, 如果每次都要等到凌晨4點去更新, 可能整個的開發節奏都被帶慢了。
對於第二個問題, 對於可以預見的流量,比如大促來臨,可以提前3天放在請求量少的時候更新。
最近幾年,隨着SOA的普及和微服務的出現,特別是dubbo的出現,服務治理的概念被提出來。 服務治理是一個很宏大的概念,包括
服務注冊,服務自動發現,服務路由,服務依賴,集群容錯,服務降級,服務監測,服務審批等,當然不是每個服務中心都必須實現這些東西, 公司可以根據自己的實際需求來定制實現。
基於以上這些情況, 我計划實現一個工具,這個工具首先解決站點上下線和動態擴容問題,也就是說在不需要重啟nginx的情況下,並且在保證請求不丟失的情況下來更新站點。 同時帶有部分服務治理功能。


服務上線
1. 在一個新服務上線的時候,一般會提前申請幾台機器, 運維會在nginx上新增server,並新增server對應的upstream ,正常情況下upstream應該配置是后端服務器的IP,但是這里不配置(如果允許,甚至這一步都可以省略)。
2. 服務部署好並啟動,在啟動的時候,向注冊中心注冊自身的服務信息,包括IP和端口。
3. 注冊中心收到請求后,會對服務進行健康檢測,確保提供的服務沒有問題,則將服務狀態標示為
預上線狀態。
4. 在后台管理中心,就可以將
預上線的服務設置為
上線,服務管理中心會調用nginx的上線接口,將服務IP新增或者更新到upstream中,服務就可以提供訪問。
服務更新
假如我們現在有一個服務需要更新,則執行以下步驟:
1. 在后台管理中心,將一個服務設為
下線,此時服務中心會調用nginx的下線接口,將指定服務器的IP設置為下線。
2. 在等待1分鍾后,確保沒有新連接連過來,則可以開始更新服務站點。
3. 更新完畢后,再手動設為
上線,此時服務中心會調用nginx的上線接口,將指定服務器的IP設置為上線。當然對於成熟的服務,這些都可以自動化,有些公司會有一些自動化發布工具, 與自動化發布工具集成,可以一鍵下線,更新並上線。
服務運行期間
在服務運行過程中,會有一個健康檢測的服務對所有提供服務的站點進行健康檢測,一旦檢測到有問題,就執行
下線邏輯。 直到問題被解決,最后執行
上線流程。
動態加減機器
在服務運行過程中,可能因為某些原因,服務請求飆高(前提是這些請求都是合法的),超過了當前集群的承載能力,當系統檢測到這些情況后,可以動態擴充機器,比如現在流行的docker,在啟動容器的時候,同時啟動應用,應用在啟動的時候,將自身信息注冊給注冊中心,注冊中心再將這些信息同步到nginx,應用就可以提供訪問,整體上就可以實現彈性計算。
為什么不實現服務動態發現?
這里可以看到圖中已經有一個服務注冊中心。 既然有了服務注冊中心了, 那可以讓業務站點連接服務注冊中心來獲取真實的服務IP,然后繞過nginx來連接服務,這里之所以沒有這樣做,是因為:
1. 實現服務動態發現,這個需要和RPC框架配合,而且需要做服務的軟負載,失敗重連,限流等,整個項目設計就上升了一個復雜度, 考慮到有些項目還未使用RPC,並且不想對原有的項目有過多的侵入, 所以這里不做實現。 但是並不意味沒有這些功能,服務的負載, 失敗重連, 限流,其實這些功能在nginx中同樣也有,可以直接使用,所以沒有必要重新再開發。
2. 實現服務動態發現,獲取到真實的服務IP,然后直連,這些一般是在流量特別大,nginx上出現短板的時候使用,但實際情況,一般很少會耗盡nginx的性能,即使有,也可以通過ngxin水平擴展來實現,所以這里依然使用nginx作為負載均衡。
這里講一下這個項目的關鍵點:
1. 服務的注冊和健康檢測這個沒有技術難點,這里不做解釋。
2. 關於操作nginx上下線,這里的確是一個難點,因為nginx本身並沒有提供這些上下線API,需要openresty並配合一些第三方擴展來實現。 這里主要用到了兩個擴展模塊:
ngx_http_dyups_module lua-upstream-nginx-module
ngx_http_dyups_module(
https://github.com/yzprofile/ngx_http_dyups_module)提供了粗粒度的upstream管理方法,可以對整個upstream進行新增,刪除。
lua-upstream-nginx-module(
https://github.com/openresty/lua-upstream-nginx-module) ,則提供了細粒度的管理方式,可以對某一個服務IP進行管理,其中提供的set_peer_down方法,可以對upstream中的某個ip進行上下線。
3. 也可以使用
ngx_dynamic_upstream(
https://github.com/cubicdaiya/ngx_dynamic_upstream)
這些插件有一個共同點,那就是在不需要重啟nginx的基礎上, 動態修改nginx的配置。
1. 最后我想請大伙討論一下,你們公司是怎么上下線的, 是直接覆蓋,還是有其他策略。 歡迎在評論區討論。