「如何設計」:mysql
在定義什麼是高可用,能夠先定義下什麼是不可用,一個網站的內容最終呈如今用戶面前須要通過若干個環節,而其中只要任何一個環節出現了故障,均可能致使網站頁面不可訪問,這個也就是網站不可用的狀況。redis
參考維基百科,看看維基怎麼定義高可用算法
系統無中斷地執行其功能的能力,表明系統的可用性成都,是進行系統設計時的準則之一。sql
這個難點或是重點在於「無中斷」,要作到 7 x 24 小時任何人任什麼時候間任何地點去訪問任務服務都能獲得預期的結果。docker
剛說到要提供一套無中斷提供預計服務的系統,須要硬件,軟件相結合,可是咱們的硬件老是會出故障,軟件也總會有bug,硬件會慢慢老化,軟件會愈來愈複雜和龐大,除了硬件軟件在本質上沒法作到「無中斷」,外部環境也可能致使服務的中斷,例如斷電,地震,火災,光纖被挖掘機挖斷,這些影響的程度可能更大。數據庫
在業界有一套比較出名的評定網站可用性的指標,經常使用N個9來量化可用性,能夠直接映射到網站正常運行時間的百分比上緩存
描述 | N個9 | 可用性級別 | 年度停機時間 |
---|---|---|---|
基本可用 | 2個9 | 99% | 87.6小時 |
較高可用 | 3個9 | 99% | 8.8小時 |
具有故障自動恢復能力可用 | 4個9 | 99.99% | 53分鐘 |
極高可用 | 5個9 | 99.999% | 5分鐘 |
以前就任的一家大型互聯網公司也是按照這個指標去界定可用性,不過在執行的過程當中也碰到了一些問題,例如,有一些服務的升級或數據遷移明明能夠在深夜停機或停服務進行,然而考慮到之後的報告要顯示出咱們的系統達到了多少個9的高可用,而放棄停服務這種簡單的解決方案,例如停機2個小時,就永遠也達不到4個9。然而在一些高併發的場合,例如在秒殺或拼團,雖然服務中止了幾分鐘,可是這個對整個公司業務的影響多是很是重大的,分分鐘丟失的訂單多是一個龐大的數量。安全
很明顯若是隻是單單靠停機或不可用的時間來做爲高可用評價緯度也是很是的不靠譜,設置會讓技術人員爲了追求四個或五個9而採用一些更復雜但不必的方式。bash
咱們須要一種對高可用更爲科學的評估方式
咱們在深夜停機一個小時和白天高峯期停機一個小時出現的影響是徹底不同的,這個也是須要根據業務而定,通常來講 To C 業務領域形成不可用是對於用戶請求數的影響,換算成公式
一段時間的停機影響請求量佔比 = 停機時間影響請求量 / 總的請求量
複製代碼
這個緯度是從用戶影響的緯度去進行評估,能夠更加結合業務的緯度對業務形成多大影響去進行評估。固然作好這個評估的前提還須要對咱們的服務進行分級,只有結合服務分級才能更好的進行評估。
高可用是一個比較複雜的命題,基本上在全部的處理中都會涉及到高可用,全部在設計高可用方案也涉及到了方方面面,這中間將會出現的細節是多種多樣的,因此咱們須要對這樣一個微服務高可用方案進行一個頂層的設計。
每個訪問可能都會有多個服務組成而成,每一個機器每一個服務均可能出現問題,因此第一個考慮到的就是每一個服務必須不止一份能夠是多份,所謂多份一致的服務就是服務的冗餘,這裏說的服務泛指了機器的服務,容器的服務,還有微服務自己的服務。
在機器服務層面須要考慮,各個機器間的冗餘是否有在物理空間進行隔離冗餘 ,例如是否全部機器是否有分別部署在不一樣機房,若是在同一個機房是否作到了部署在不一樣的機櫃,若是是docker容器是否部署在分別不一樣的物理機上面。 採起的策略其實也仍是根據服務的業務而定,因此須要對服務進行分級評分,從而採起不一樣的策略,不一樣的策略安全程度不一樣,伴隨這的成本也是不一樣,安全等級更高的服務可能還不止考慮不一樣機房,還須要把各個機房所處的區域考慮進行,例如,兩個機房不要處在同一個地震帶上等等
服務的冗餘會要求咱們能夠隨時對服務進行擴容或者縮容,有可能咱們會從2臺機器變成3臺機器,想要對服務進行隨時隨地的擴縮容,就要求咱們的服務是一個無狀態化,所謂無狀態化就是每一個服務的服務內容和數據都是一致的。
例如,從咱們的微服務架構來看,咱們總共分水平劃分了好幾個層,正由於咱們每一個層都作到了無狀態,因此在這個水平架構的擴張是很是的簡單。
假設,咱們須要對網關進行擴容,咱們只須要增長服務就能夠,怎麼作到服務無狀態呢,舉個例子,在網關這一層,咱們須要作鑑權的操做,須要把用戶的session進行存儲,這時候其實有幾種方案:
每一個網關單獨存一份,在上層經過用戶的IP一致性哈希來選擇網關,這樣一個用戶就只會落到一臺網關上。
這實際上不Ok,若是這臺網關宕機了,那麼這個用戶的session也會丟失
每一個網關都存全部的session數據,當一個網關接收到一個session,就會把這個session同步到全部的網關,那麼不管訪問到那一臺網關均可以獲取到session
這實際上也不Ok,同步session是須要時間,若是咱們追求的是CAP中的AP模型,那麼就會可能出現,網關還未完成同步,用戶就已經請求到無同步的網關。
網關不保存任何的session數據,不提供會形成一致性的服務,將不一致的數據進行幾種存儲,藉助更加擅長數據同步的中間件來完成。
這個是目前主流的方案,服務自己儘量提供邏輯的服務,將數據的一致性保證集中式處理,這樣就能夠把「狀態」抽取出來,讓網關保持一個「無狀態」
這裏僅僅是舉了網關的例子,在微服務只基本全部的服務,都應該按照這種思路去作,若是服務中有狀態,就應該把狀態抽取出來,讓更加擅長處理數據的組件來處理,而不是在微服務中去兼容有數據的狀態。
相信負載均衡這個話題基本已經深刻每一個作微服務開發或設計者的人心,從狹義上來講,負載均衡的實現有硬件和軟件,硬件有F5,A10等機器,軟件有LVS,nginx,HAProxy等等,負載均衡的算法有 random , RoundRobin , ConsistentHash等等。
上面說的基本能夠屬於狹義上的負載均衡,這裏更想說的是廣義上的負載均衡
廣義上的負載均衡包含了
接下來看看一個網關對業務邏輯層的調用怎麼去實現負載均衡
在網關這一種的負載均衡大部分是由註冊中心進行實現
在使用ZK做爲註冊中心時,故障的發現是由Zk去進行發現,業務邏輯層經過watch的心跳機制將本身註冊到zk上,網關對zk進行訂閱就能夠知道有多少能夠調用的列表。當業務邏輯層在重啓或者被關閉時就會跟zk斷了心跳,zk會更新可調用列表。
使用zk做爲負載均衡的協調器,最大的問題是zk對於服務是否可用是基於pingpong的方式,只要服務心跳存在,zk就認爲服務是處在於可用狀態,可是服務若是處在於假死的狀態,zk是無從得知的。這個時候,業務邏輯服務是否真正可用只可以由網關知道。
爲什麼會牽出冪等設計的問題,主要是由於負載均衡的failover策略,就是對失敗的服務會進行重試,通常來講,若是是讀操做的服務,重複執行也不會出問題,但想象一下,若是是一個建立訂單減庫存的操做,第一次調用也【業務邏輯1】超時,再從新調用了【業務邏輯2】,這個時候咱們都不能確認超時調用的【業務邏輯1】是否真的被調用,有可能根本就調用不成功,有可能已經調用成功可是由於某些緣由返回超時而已,因此,很大程度這個接口會被調用2次。若是咱們沒有保證冪等性,就有可能一個訂單致使了減小2次的庫存。
所謂的冪等性,就是得保證在同一個業務中,一個接口被調用了屢次,其致使的結果都是同樣的。
怎麼作冪等性設計 : 看下 《「如何設計」具有冪等性的服務》
在每一次調用,時間越長存在超時的風險就越大,邏輯越複雜執行的步驟越多存在失敗的風險也就越大,若是在業務容許的狀況下,用戶調用只給用戶必需要的結果,而不是須要同步的結果能夠放在另外的地方異步去操做,這就減小了超時的風險也把複雜業務進行拆分減低複雜度。
固然異步化的好處是很是多,例如削封解耦等等,這裏只是從可用的角度出發。
異步化大體有這三種的實現方式:
什麼是柔性化,想象一個場景,咱們的系統會給每一個下單的用戶增長他們下單金額對應的積分,當一個用戶下單完畢後,咱們給他增長積分的服務出現了問題,這個時候,咱們是要取消掉這個訂單仍是先讓訂單經過,積分的問題經過從新或者報警來處理呢?
所謂的柔性化,就是在咱們業務中容許的狀況下,作不到給予用戶百分百可用的經過降級的手段給到用戶儘量多的服務,而不是非得每次都交出去要麼100分或0分的答卷。
怎麼去作柔性化,更多實際上是對業務的理解和判斷,柔性化更可能是一種思惟,須要對業務場景有深刻的瞭解。
在電商訂單的場景中,下單,扣庫存,支付是必定要執行的步驟,若是失敗則訂單失敗,可是加積分,發貨,售後是能夠柔性處理,就算出錯也能夠經過日誌報警讓人工去檢查,不必爲加積分損失整個下單的可用性
先來說講微服務中限流/熔斷的目的是什麼,微服務後,系統分佈式部署,系統之間經過rpc框架通訊,整個系統發生故障的機率隨着系統規模的增加而增加,一個小的故障通過鏈路的傳遞放大,有可能會形成更大的故障。
限流跟高可用的關係是什麼,假定咱們的系統最多隻能承受500我的的併發訪問,但整個時候忽然增長到1000我的進來,一會兒就把整個系統給壓垮了,原本還有500我的能享受到咱們系統的服務,忽然間變成了全部人都沒法獲得服務,與其讓1000人都不法獲得服務,不如就讓500我的獲得服務,拒絕掉另外500我的。限流是對訪問的隔離,是保證了部門系統承受範圍內用戶的可用性。
熔斷跟高可用的關係是什麼,上面說了微服務是一個錯綜複雜的調用鏈關係,假設 模塊A 調用 模塊B , 模塊B 又調用了 模塊C , 模塊C 調用了 模塊D,這個時候,模塊D 出了問題出現嚴重的時延,這個時候,整個調用鏈就會被 模塊D 給拖垮,A 等B,B等C,C等D,並且A B C D的資源被鎖死得不到釋放,若是流量大的話還容易引發雪崩。熔斷,主動丟棄 模塊D 的調用,並在功能上做出一些降級才能保證到咱們系統的健壯性。 熔斷是對模塊的隔離,是保證了最大功能的可用性。
以前上面說的服務冗餘,能夠簡單的理解爲計算的高可用,計算高可用只須要作到無狀態既可簡單的擴容縮容,可是對於須要存儲數據的系統來講,數據自己就是有狀態。
跟存儲與計算相比,有一個本質的差異:
將數據從一臺機器搬到另外一臺機器,須要通過線路進行傳輸
網絡是不穩定的,特別是跨機房的網絡,ping的延時多是幾十幾百毫秒,雖然毫秒對於人來講幾乎沒有什麼感受,可是對於高可用系統來講,就是本質上的不一樣,這意味着整個系統在某個時間點上,數據確定是不一致的。按照「數據+邏輯=業務」的公式來看,數據不一致,邏輯一致,最後的業務表現也會不一致。舉個例子
不管是正常狀況下的傳輸延時,仍是異常狀況下的傳輸中斷,都會致使系統的數據在某個時間點出現不一致,而數據的不一致又會致使業務出現問題,可是若是數據不作冗餘,系統的高可用沒法保證
因此,存儲高可用的難點不在於怎麼備份數據,而在於如何減小或者規避數據不一致對業務形成的影響
分佈式領域中有一個著名的CAP定理,從理論上論證了存儲高可用的複雜度,也就是說,存儲高可用不可能同時知足「一致性,可用性,分區容錯性」,最多隻能知足2個,其中分區容錯在分佈式中是必須的,就意味着,咱們在作架構設計時必須結合業務對一致性和可用性進行取捨。
存儲高可用方案的本質是將數據複製到多個存儲設備中,經過數據冗餘的方式來現實高可用,其複雜度主要呈如今數據複製的延遲或中斷致使數據的不一致性,咱們在設計存儲架構時必須考慮到一下幾個方面:
主從複製是最多見的也是最簡單的存儲高可用方案,例如Mysql,redis等等
其架構的優勢就是簡單,主機複製寫和讀,而從機只負責讀操做,在讀併發高時候可用擴張從庫的數量減低壓力,主機出現故障,讀操做也能夠保證讀業務的順利進行。
缺點就是客戶端必須感知主從關係的存在,將不一樣的操做發送給不一樣的機器進行處理,並且主從複製中,從機器負責讀操做,可能由於主從複製時延大,出現數據不一致性的問題。
剛說了主從切換存在兩個問題: 1.主機故障寫操做沒法進行 2.須要人工將其中一臺從機器升級爲主機
爲了解決這個兩個問題,咱們能夠設計一套主從自動切換的方案,其中設計到對主機的狀態檢測,切換的決策,數據丟失和衝突的問題。
1.主機狀態檢測
須要多個檢查點來檢測主機的機器是否正常,進程是否存在,是否出現超時,是否寫操做不可執行,讀操做是否不可執行,將其進行彙總,交給切換決策
2.切換決策
肯定切換的時間決策,什麼狀況下從機就應該升級爲主機,是進程不存在,是寫操做不可這行,連續檢測多少失敗次就進行切換。應該選擇哪個從節點升級爲主節點,通常來講或應該選同步步驟最大的從節點來進行升級。切換是自動切換仍是半自動切換,經過報警方式,讓人工作一次確認。
3.數據丟失和數據衝突 數據寫到主機,尚未複製到從機主機就掛了,這個時候怎麼處理,這個也得考慮業務的方式,是要確保CP或AP
還要考慮一個數據衝突的問題,這個問題在mysql中大部分是由自增主鍵引發,就算不考慮自增主鍵會引發數據衝突的問題,其實自增主鍵還要引發不少的問題,這裏不細說,避免使用自增主鍵。
上述的數據冗餘能夠經過數據的複製來進行解決,可是數據的擴張須要經過數據的分片來進行解決(若是在關係型數據庫是分表)。
HDFS , mongoDB 的sharding 模式也基本是基於這種分片的模式去實現,咱們在設計分片主要考慮到的點是: