springCloud去中心化機制
1. 閑聊注冊中心——ZK、Eureka、Sofa-Registry
最開始服務之間的調用藉助的是域名,域名其實是個好東西,使用起來很方便,但所有調用請求都得走域名解析和負載均衡,相對來說性能就差一點,而且這些夾在中間的零部件會演變成性能瓶頸和潛在的單點風險,後來大家一比較發現還不如直接端到端調用,那麼就需要一個東西把調用鏈的兩端連起來,這就是注冊中心。
注冊中心提供服務的注冊發現,用來連接調用鏈路的 Provider 和 Consumer 這兩個端點。一個注冊中心得管理好服務的擴縮容、故障轉移、流量分配這類的核心功能,自身同樣需要高可用,妥善解決跨地域部署情況下注冊中心節點之間的數據一致性,因此我更傾向於認為注冊中心是一個偏向體系化的產品,它的價值依賴於與之相匹配的服務規模和治理體系,它的思想雖然產生得很早,但是規模化地落地並不容易,偏小的服務規模本身對於注冊中心的依賴並不明顯,實現的手段也沒有定式;大型服務又會對注冊中心的定位、功能提出更苛刻的需求。
究竟是選擇強一致性還是高可用性,當下的主流技術選型傾向於後者,但這並不是說明 CP 組件沒有存在的意義,而是 AP 組件更契合互聯網對於注冊中心的定位。
互聯網系統高可用永遠是擺在首位的東西,因為系統不可用的時間通常跟財產損失直接掛鉤。服務列表在短時間少了一兩個節點、多了一兩個節點,這點流量傾斜對於大多數服務來說感知並不明顯,只需要注冊中心在某個短暫的時間窗口內重新達到一致性的狀態就可以,注冊中心不應該在運行時影響到處於正常調用的兩個端點之間的連通性。
CP 的代表就是 zookeeper ,至今 zookeeper 也是非常流行的用於注冊中心的組件,這在一定程度上要感謝 bbo 。 bbo 在 zookeeper 中是用一個樹形結構的兩個節點,分別維護服務的 Provider 和 Comsumer 的,列表注冊在這兩個節點下面,服務的健康檢查依賴 zookeeper 提供的臨時節點特性,實際上跟 session 的生命周期綁定在一起,但臨時節點的問題就是數據易失,一旦集群故障節點重啟丟失服務列表,可能造成服務大面積癱瘓,電商新貴 PDD 就曾經出現過這個問題。
CP 最關鍵的問題在於無法支持機房容災,例如 ABC 三個機房,機房 C 和其他兩個機房產生了網路隔離,部署在機房 C 的 zookeeper 節點是無法提供寫入的,這意味著機房 C 部署的 Provider 不能擴縮容、不能重啟,最好在故障期間一直保持原樣,否則 Provider 任何操作都會是高危的,會直接影響到部署在機房 C 的 Comsumer 。
順帶說一句,我認為 zookeeper 真挺好的,它本身的定位就是個分布式協調服務,並不是專門為注冊中心設計的,你不能拿評價注冊中心的眼光去評價它,你要的功能 zookeeper 基本上都有,藉助 Curator 拿來就可以用,提供了 election ,提供了 ZAB , TPS 低了點(其實也可以了)但是也滿足大部分服務規模。
Eureka —— 奈飛的網紅組件, AP 注冊中心的代表。 Eureka 1.x 今天回頭看來是個很牽強的實現,因為它的架構從誕生之日起,就意味著將來數據規模增長之後大概率會出問題,集群整體可伸縮性很低,每擴容一台 Eureka 節點意味著整體點對點之間需要多一份數據流,而這種 Peer 點對點機制的數據流很容易打滿網卡,造成服務端壓力。根據公開的資料, Eureka 在實例規模五千左右時就會出現明顯的性能瓶頸,甚至是服務不可用的情況。
但是從另一個方面來說, Eureka 的架構清晰、運維部署都很方便,而且 Eureka 作為 SpringCloud 推薦的注冊中心實現,國內用戶數目也相當可觀,可見在服務規模恰當的情況下其性能並沒有問題,從攜程的分享資料也可以看出,其內部的注冊中心也是類似於 Eureka 的實現,可見沒有絕對完美的架構設計,適合自己、滿足業務需求才是最主要的。
Eureka 服務端多節點架構其實有點去中心化的意思,有意保持了每個節點的無狀態特性,但是代價就是每個節點都持有全量數據,新增的數據可以往任意一個節點寫入,然後由這個節點向其他節點廣播,最終達到一致性。
數據都沒有持久化,僅保存在內存中,帶來了更好的讀寫性能、更短的響應時間,但是無法處理數據瓶頸,大節點擴容造成的數據同步存在打滿網卡的風險,這點跟 redis 集群很像。客戶端 30s 定期上報心跳、客戶端緩存服務列表這些都沒什麼好談的, Eureka 有個很有意思的點是,它的集群節點實現了一個自我保護機制,用來預測到底是集群出問題還是客戶端出問題,實現很簡單但是思想挺實用的。
Eureka 2.0 的設計文檔上體現了讀寫分離集群的思想,本質上是為了提升集群性能和容量,但是非常遺憾,目前 2.0 的狀態是 Discontinued ,短時間應該不會有成熟的產品出現。
雖然沒有 Eureka 2.0 ,但是可以從螞蟻開源的 Sofa-Registry 中了解類似思想的實現,Sofa-Registry 脫胎於阿里的 ConfigServer ,從公開的資料顯示,國內阿里應該是最早做注冊中心讀寫分離實踐的。
這種架構帶來的直接好處就是可以支持海量數據,第一層的 session 集群可以無限水平擴展,彼此之間完全不需要通信, session 集群在內存中維護了注冊中心需要的一切拓撲關系,客戶端僅僅連接一部分 session 集群中的機器,如果某台 session 機器掛了客戶端會選擇另一台重連;背後的 data 集群則通過分片存儲所有的源數據,分片之間通過主從副本保證高可用,再將源數據推送到 session 集群保存下來,供客戶端讀取。
缺點也很明顯,這種架構人力投入、運維成本肯定高於 Eureka 。
這幾乎是注冊中心不可避免的問題,高可用、機房容災、網路延遲等都會催生出要求注冊中心節點能夠跨地域部署,這會引申出另一個問題就是如何做數據同步。
這種情況下已經不可能通過客戶端注冊或者是集群節點數據流復制來保證異地注冊中心集群之間的數據一致,而是會研發單獨的數據同步組件,通過跨地域專線實現地域間注冊中心的數據同步,如果專線斷開,此時各自地域的注冊中心依舊可以獨立服務於本地域內服務的調用,等到專線恢復,二者的數據繼續進行同步合並糾正,最終達到一致性。對這個知識點感興趣的可以了解下 Nacos-Sync 組件,是 Nacos 項目專門推出的開源數據同步服務。
文章略過了一些注冊中心通用的概念,例如數據模型的分層、服務列表本地緩存、健康檢查的方式、推拉權衡等,我認為敘述這些細節的意義並不大。
很多時候,理解某個組件我認為最重要的是理清它的架構演進過程,這是寶貴的經驗財富,你可以抓住並學習每次架構演進的方向和原因。
2. 深入理解Spring Cloud Security OAuth2及JWT
OAuth2是一個關於授權的開放標准,核心思路是通過各類認證手段(具體什麼手段OAuth2不關心)認證用戶身份,並頒發token(令牌),使得第三方應用可以使用該令牌在 限定時間 、 限定范圍 訪問指定資源。主要涉及的RFC規范有 RFC6749 (整體授權框架), RFC6750 (令牌使用), RFC6819 (威脅模型)這幾個,一般我們需要了解的就是 RFC6749 。獲取令牌的方式主要有四種,分別是 授權碼模式 , 簡單模式 , 密碼模式 和 客戶端模式 ,如何獲取token不在本篇文章的討論范圍,我們這里假定客戶端已經通過某種方式獲取到了access_token,想了解具體的oauth2授權步驟可以移步阮一峰老師的 理解OAuth 2.0 ,裡面有非常詳細的說明。
這里要先明確幾個OAuth2中的幾個重要概念:
明確概念後,就可以看OAuth2的協議握手流程,摘自RFC6749
Spring Security是一套安全框架,可以基於RBAC(基於角色的許可權控制)對用戶的訪問許可權進行控制,核心思想是通過一系列的filter chain來進行攔截過濾,以下是ss中默認的內置過濾器列表,當然你也可以通過 custom-filter 來自定義擴展filter chain列表
這裡面最核心的就是 FILTER_SECURITY_INTERCEPTOR ,通過 來進行資源許可權的匹配, AccessDecisionManager 來執行訪問策略。
一般意義來說的應用訪問安全性,都是圍繞認證(Authentication)和授權(Authorization)這兩個核心概念來展開的。即首先需要確定用戶身份,在確定這個用戶是否有訪問指定資源的許可權。認證這塊的解決方案很多,主流的有 CAS 、 SAML2 、 OAUTH2 等(不巧這幾個都用過-_-),我們常說的單點登錄方案(SSO)說的就是這塊,授權的話主流的就是spring security和shiro。shiro我沒用過,據說是比較輕量級,相比較而言spring security確實架構比較復雜。
將OAuth2和Spring Security集成,就可以得到一套完整的安全解決方案。
為了便於理解,現在假設有一個名叫「臉盆網」的社交網站,用戶在首次登陸時會要求導入用戶在facebook的好友列表,以便於快速建立社交關系。具體的授權流程如下:
不難看出,這個假設的場景中,臉盆網就是第三方應用(client),而facebook既充當了認證伺服器,又充當了資源伺服器。這個流程裡面有幾個比較重要的關鍵點,我需要重點說一下,而這也是其他的涉及spring security與OAuth2整合的文章中很少提及的,很容易雲里霧里的地方。
細心的同學應該發現了,其實在標準的OAuth2授權過程中,5、6、8這幾步都不是必須的,從上面貼的 RFC6749 規范來看,只要有1、2、3、4、7這幾步,就完成了被保護資源訪問的整個過程。事實上, RFC6749 協議規範本身也並不關心用戶身份的部分,它只關心token如何頒發,如何續簽,如何用token訪問被保護資源(facebook只要保證返回給臉盆網的就是當前用戶的好友,至於當前用戶是誰臉盆網不需要關心)。那為什麼spring security還要做5、6這兩步呢?這是因為spring security是一套完整的安全框架,它必須關心用戶身份!在實際的使用場景中,OAuth2一般不僅僅用來進行被保護資源的訪問,還會被用來做單點登陸(SSO)。在SSO的場景中,用戶身份無疑就是核心,而token本身是不攜帶用戶信息的,這樣client就沒法知道認證伺服器發的token到底對應的是哪個用戶。設想一下這個場景,臉盆網不想自建用戶體系了,想直接用facebook的用戶體系,facebook的用戶和臉盆網的用戶一一對應(其實在很多中小網站現在都是這種模式,可以選擇使用微信、QQ、微博等網站的用戶直接登陸),這種情況下,臉盆網在通過OAuth2的認證後,就希望拿到用戶信息了。所以現在一般主流的OAuth2認證實現,都會預留一個用戶信息獲取介面,就是上面提到的 https://api.facebook.com/user (雖然這不是OAuth2授權流程中必須的),這樣client在拿到token後,就可以攜帶token通過這個介面獲取用戶信息,完成SSO的整個過程。另外從用戶體驗的角度來說,如果獲取不到用戶信息,則意味者每次要從臉盆網訪問facebook的資源,都需要重定向一次進行認證,用戶體驗也不好。
首先要明確一點, OAuth2並不是一個SSO框架,但可以實現SSO功能 。以下是一個使用github作為OAuth2認證伺服器的配置文件
可以看到 accessTokenUri 和 userAuthorizationUri 都是為了完成OAuth2的授權流程所必須的配置,而 userInfoUri 則是spring security框架為了完成SSO所必須要的。所以總結一下就是: 通過將用戶信息這個資源設置為被保護資源,可以使用OAuth2技術實現單點登陸(SSO),而Spring Security OAuth2就是這種OAuth2 SSO方案的一個實現。
Spring Security在調用user介面成功後,會構造一個 OAuth2Authentication 對象,這個對象是我們通常使用的 對象的一個超集,裡面封裝了一個標準的 ,同時在 detail 中還攜帶了OAuth2認證中需要用到的一些關鍵信息(比如 tokenValue , tokenType 等),這時候就完成了SSO的登陸認證過程。後續用戶如果再想訪問被保護資源,spring security只需要從principal中取出這個用戶的token,再去訪問資源伺服器就行了,而不需要每次進行用戶授權。這里要注意的一點是 此時瀏覽器與client之間仍然是通過傳統的cookie-session機制來保持會話,而非通過token。實際上在SSO的過程中,使用到token訪問的只有client與resource server之間獲取user信息那一次,token的信息是保存在client的session中的,而不是在用戶本地 。這也是之前我沒搞清楚的地方,以為瀏覽器和client之間也是使用token,繞了不少彎路,對於Spring Security來說, 不管是用cas、saml2還是Oauth2來實現SSO,最後和用戶建立會話保持的方式都是一樣的 。
根據前面所說,大家不難看出,OAuth2的SSO方案和CAS、SAML2這樣的純SSO框架是有本質區別的。在CAS和SAML2中,沒有資源伺服器的概念,只有認證客戶端(需要驗證客戶信息的應用)和認證伺服器(提供認證服務的應用)的概念。在CAS中這叫做 cas-client 和 cas-server ,SAML2中這叫做 Service Providers 和 Identity Provider ,可以看出CAS、SAML2規范天生就是為SSO設計的,在報文結構上都考慮到了用戶信息的問題(SAML2規范甚至還帶了許可權信息),而OAuth2本身不是專門為SSO設計的,主要是為了解決資源第三方授權訪問的問題,所以在用戶信息方面,還需要額外提供一個介面。
臉盆網的這個例子中,我們看到資源伺服器和認證伺服器是在一起的(都是facebook),在互聯網場景下一般你很難找到一個獨立的、權威的、第三方的認證中心(你很難想像騰訊的QQ空間通過支付寶的認證中心去授權,也很難想像使用谷歌服務要通過亞馬遜去授權)。但是如果是在公司內部,這種場景其實是很多的,尤其在微服務架構下,有大量服務會對外提供資源訪問,他們都需要做許可權控制。那麼最合理的當然就是建立一個統一的認證中心,而不是每個服務都做一個認證中心。我們前面也介紹了,token本身是不攜帶用戶信息的,在分離後resouce server在收到請求後,如何檢驗token的真實性?又如何從token中獲取對應的用戶信息?這部分的介紹網上其實非常少,幸好我們可以直接從官方文檔獲取相關的蛛絲馬跡,官方文檔對於resouce server的配置是這樣描述的:
寥寥數語,但已經足夠我們分析了。從這個配置可以看出,client在訪問resource server的被保護資源時,如果沒有攜帶token,則資源伺服器直接返回一個401未認證的錯誤
如果攜帶了token,則資源伺服器會使用這個token向認證伺服器發起一個用戶查詢的請求,若token錯誤或已經失效,則會返回
若token驗證成功,則認證伺服器向資源伺服器返回對應的用戶信息,此時resource server的spring security安全框架就可以按照標準的授權流程進行訪問許可權控制了。
從這個流程中我們可以看出,通過OAuth2進行SSO認證,有一個好處是做到了 認證與授權的解耦 。從日常的使用場景來說,認證比較容易做到統一和抽象,畢竟你就是你,走到哪裡都是你,但是你在不同系統裡面的角色,卻可能千差萬別(家裡你是父親,單位里你是員工,父母那裡你是子女)。同時角色的設計,又是和資源伺服器的設計強相關的。從前面的配置中不難發現,如果希望獲得為不同資源伺服器設計的角色,你只需要替換 https://api.facebook.com/user 這個配置就行了,這為我們的許可權控制帶來了更大的靈活性,而這是傳統的比如SAML2這樣的SSO框架做不到的。
終於來到了著名的JWT部分了,JWT全稱為Json Web Token,最近隨著微服務架構的流行而越來越火,號稱新一代的認證技術。今天我們就來看一下,jwt的本質到底是什麼。
我們先來看一下OAuth2的token技術有沒有什麼痛點,相信從之前的介紹中你也發現了,token技術最大的問題是 不攜帶用戶信息 ,且資源伺服器無法進行本地驗證,每次對於資源的訪問,資源伺服器都需要向認證伺服器發起請求,一是驗證token的有效性,二是獲取token對應的用戶信息。如果有大量的此類請求,無疑處理效率是很低的,且認證伺服器會變成一個中心節點,對於SLA和處理性能等均有很高的要求,這在分布式架構下是很要命的。
JWT就是在這樣的背景下誕生的,從本質上來說,jwt就是一種 特殊格式 的token。普通的oauth2頒發的就是一串隨機hash字元串,本身無意義,而jwt格式的token是有特定含義的,分為三部分:
這三部分均用base64進行編碼,當中用 . 進行分隔,一個典型的jwt格式的token類似 xxxxx.yyyyy.zzzzz 。關於jwt格式的更多具體說明,不是本文討論的重點,大家可以直接去官網查看 官方文檔 ,這里不過多贅述。
相信看到簽名大家都很熟悉了,沒錯,jwt其實並不是什麼高深莫測的技術,相反非常簡單。認證伺服器通過對稱或非對稱的加密方式利用 payload 生成 signature ,並在 header 中申明簽名方式,僅此而已。通過這種本質上極其傳統的方式,jwt可以實現 分布式的token驗證功能 ,即資源伺服器通過事先維護好的對稱或者非對稱密鑰(非對稱的話就是認證伺服器提供的公鑰),直接在本地驗證token,這種去中心化的驗證機制無疑很對現在分布式架構的胃口。jwt相對於傳統的token來說,解決以下兩個痛點:
在上面的那個資源伺服器和認證伺服器分離的例子中,如果認證伺服器頒發的是jwt格式的token,那麼資源伺服器就可以直接自己驗證token的有效性並綁定用戶,這無疑大大提升了處理效率且減少了單點隱患。
就像布魯克斯在《人月神話》中所說的名言一樣:「沒有銀彈」。JWT的使用上現在也有一種誤區,認為傳統的認證方式都應該被jwt取代。事實上,jwt也不能解決一切問題,它也有適用場景和不適用場景。
適用場景:
這些場景能充分發揮jwt無狀態以及分布式驗證的優勢
不適用的場景:
不要試圖用jwt去代替session。 這種模式下其實傳統的session+cookie機制工作的更好,jwt因為其無狀態和分布式,事實上只要在有效期內,是無法作廢的,用戶的簽退更多是一個客戶端的簽退,服務端token仍然有效,你只要使用這個token,仍然可以登陸系統。另外一個問題是續簽問題,使用token,無疑令續簽變得十分麻煩,當然你也可以通過redis去記錄token狀態,並在用戶訪問後更新這個狀態,但這就是硬生生把jwt的無狀態搞成有狀態了,而這些在傳統的session+cookie機制中都是不需要去考慮的。這種場景下,考慮高可用,我更加推薦採用分布式的session機制,現在已經有很多的成熟框架可供選擇了(比如spring session)。
3. Spring Cloud筆記03: 服務注冊和服務發現的基本概念
上節在K8S集群中部署了Nacos集群,並將Nacos的Web控制台和API以Ingress (nacos.youcomany.com)的形式暴露到了k8s集群外部,便於從外部測試和訪問。 這里再次強調Nacos被設計為一個在IDC內部使用的應用組件,而非面向公網環境的產品,因此需要在內部隔離網路中使用,這里為了測試將其暴露到K8S集群外部,如果是生產環境必須做好網路安全策略。
接下來我們將學習如何將服務注冊到Nacos,在開始後邊的實戰之前,先看一下服務治理中關於服務注冊和服務發現的一些概念。
服務治理首先要解決的問題就是服務注冊於服務發現,解決了這兩個問題才可能實現微服務之間的調用問題。
服務注冊中心 : 每個服務實例會向注冊中心注冊自己的信息,一般包含地址、埠、協議、版本等信息。每種服務會有多個實例副本注冊到注冊中心,注冊中心維護每種服務的多個實例列表。同時,注冊中心會以某種機制去檢查各個服務實例是否可用,如果某個實例已經失效會將其剔除。在某個服務實例關閉時會自動向注冊中心注銷自己。
常見的服務注冊有三種實現方式:
服務發現 : 即服務客戶端在其網路上找到其要調用服務的具體連接信息的過程。例如通過查詢服務注冊中心得到其所調用服務的具體 IP地址和埠。 簡單的說,服務發現就是服務或者應用之間互相定位的過程。
使用服務發現後,客戶端對服務的調用不再和具體的服務實例地址耦合,而是基於服務發現機制。有以下4種常見的服務發現機制:
K8S中的一個Service資源對象對應微服務。每個Service有唯一的名字,一個ClusterIP,一個埠。 K8S中的Pod資源對象中運行的容器對應服務實例,通過Pod上的標簽Label和Service上定義的標簽選擇器Label Selector將Service與Pod關聯,通過Service內建的負載均衡機制,對Service的調用將轉發到Pod的容器中。 K8S中的服務注冊是在Pod創建時由調度者Kubernetes完成的。K8S中的服務發現採用的是服務端負載均衡器,服務注冊中心為Kubernetes(後端持久化存儲etcd)。
Spring Cloud對微服務提供了完整的解決方案和統一抽象,按照微服務的功能特性: 服務治理、負載均衡、服務間調用通信、服務配置中心、服務網關、分布式鏈路追蹤、消息匯流排、消息時間驅動、分布式事務等,提供了一系列組件,被稱為Spring Cloud全家桶。 全家桶中的功能組件還支持使用第三方實現的某個組件單獨替換,只要第三方組件是遵循Spring Cloud Common的抽象實現的。
Spring Cloud在服務治理的組件上有以下三種選擇:
當然由於"某些原因",在最新版本的Spring Cloud中Netflix組件庫已經逐漸被移除。
我們在這里對Spring Cloud服務注冊和服務發現的學習將使用Spring Cloud Alibaba組件的Nacos。
Nacos是Spring Cloud Alibaba提供的服務發現和配置管理的解決方案。Nacos是用Java開發的,通過Spring Cloud Alibaba可以很好的與Spring Cloud整合。 如果項目的所有微服務都是用Java開發的,那麼使用Nacos作為服務發現可能會使一個不錯的選擇。
Nacos的服務注冊採用的是由"服務進程內直接包含服務注冊模塊,由服務實例自己完成上線注冊和下線注銷。",這與K8S服務注冊方案中"由一個中間調度者K8S來幫助處理服務注冊"是不同的。
4. 【知識總結】6.服務注冊發現框架比較(Consul/Zookeeper/etcd/Eureka)
服務發現就是服務提供者將自己提供的地址post或者update到服務中介,服務消費者從服務中介那裡get自己想要的服務的地址。
但是有兩個問題:
第一個問題:如果有一個服務提供者宕機,那麼中介的key/value中會有一個不能訪問的地址,該怎麼辦?
心跳機制: 服務提供者需要每隔5秒左右向服務中介匯報存活,服務中介將服務地址和匯報時間記錄在zset數據結構的value和score中。服務中介需要每隔10秒左右檢查zset數據結構,踢掉匯報時間嚴重落後的地址。這樣就可以保證服務列表中地址的有效性。
第二個問題是服務地址變動時如何通知消費者。有兩種解決方案。
第一種是輪詢,消費者每隔幾秒查詢服務列表是否有改變。如果服務地址很多,查詢會很慢。這時候可以引入服務版本號機制,給每個服務提供一個版本號,在服務變動時,遞增這個版本號。消費者只需要輪詢這個版本號的變動即可知道服務列表是否發生了變化。
第二種是採用pubsub。這種方式及時性要明顯好於輪詢。缺點是每個pubsub都會佔用消費者一個線程和一個額外的連接。為了減少對線程和連接的浪費,我們使用單個pubsub廣播全局版本號的變動。所謂全局版本號就是任意服務列表發生了變動,這個版本號都會遞增。接收到版本變動的消費者再去檢查各自的依賴服務列表的版本號是否發生了變動。這種全局版本號也可以用於第一種輪詢方案。
CAP理論
CAP理論是分布式架構中重要理論
關於P的理解,我覺得是在整個系統中某個部分,掛掉了,或者宕機了,並不影響整個系統的運作或者說使用,而可用性是,某個系統的某個節點掛了,但是並不影響系統的接受或者發出請求,CAP 不可能都取,只能取其中2個。原因是
(1)如果C是第一需求的話,那麼會影響A的性能,因為要數據同步,不然請求結果會有差異,但是數據同步會消耗時間,期間可用性就會降低。
(2)如果A是第一需求,那麼只要有一個服務在,就能正常接受請求,但是對與返回結果變不能保證,原因是,在分布式部署的時候,數據一致的過程不可能想切線路那麼快。
(3)再如果,同事滿足一致性和可用性,那麼分區容錯就很難保證了,也就是單點,也是分布式的基本核心,好了,明白這些理論,就可以在相應的場景選取服務注冊與發現了。
平時經常用到的服務發現的產品進行下特性的對比,首先看下結論:
補充:
(1)運維和開發如果是 Java 更熟,也更多 Java 的應用,那毫無疑問應該用 ZK;如果是搞 Go 的,那麼還是 etcd 吧,畢竟有時候遇到問題還是要看源碼的。
(2)在創建一百萬個或更多鍵時,etcd可以比Zookeeper或Consul穩定地提供更好的吞吐量和延遲。此外,它實現了這一目標,只有一半的內存,顯示出更高的效率。但是,還有一些改進的餘地,Zookeeper設法通過etcd提供更好的最小延遲,代價是不可預測的平均延遲。
(3)
一致性協議: etcd 使用 Raft 協議,Zookeeper 使用 ZAB(類PAXOS協議),前者容易理解,方便工程實現;
運維方面:etcd 方便運維,Zookeeper 難以運維;
數據存儲:etcd 多版本並發控制(MVCC)數據模型 , 支持查詢先前版本的鍵值對
項目活躍度:etcd 社區與開發活躍,Zookeeper 感覺已經快死了;
API:etcd 提供 HTTP+JSON, gRPC 介面,跨平台跨語言,Zookeeper 需要使用其客戶端;
訪問安全方面:etcd 支持 HTTPS 訪問,Zookeeper 在這方面缺失;
與 Eureka 有所不同,Apache Zookeeper 在設計時就緊遵CP原則,即任何時候對 Zookeeper 的訪問請求能得到一致的數據結果,同時系統對網路分割具備容錯性,但是 Zookeeper 不能保證每次服務請求都是可達的。
從 Zookeeper 的實際應用情況來看,在使用 Zookeeper 獲取服務列表時,如果此時的 Zookeeper 集群中的 Leader 宕機了,該集群就要進行 Leader 的選舉,又或者 Zookeeper 集群中半數以上伺服器節點不可用(例如有三個節點,如果節點一檢測到節點三掛了 ,節點二也檢測到節點三掛了,那這個節點才算是真的掛了),那麼將無法處理該請求。所以說,Zookeeper 不能保證服務可用性。
當然,在大多數分布式環境中,尤其是涉及到數據存儲的場景,數據一致性應該是首先被保證的,這也是 Zookeeper 設計緊遵CP原則的另一個原因。
但是對於服務發現來說,情況就不太一樣了,針對同一個服務,即使注冊中心的不同節點保存的服務提供者信息不盡相同,也並不會造成災難性的後果。
因為對於服務消費者來說,能消費才是最重要的,消費者雖然拿到可能不正確的服務實例信息後嘗試消費一下,也要勝過因為無法獲取實例信息而不去消費,導致系統異常要好(淘寶的雙十一,京東的618就是緊遵AP的最好參照)。
當master節點因為網路故障與其他節點失去聯系時,剩餘節點會重新進行leader選舉。問題在於,選舉leader的時間太長,30~120s,而且選舉期間整個zk集群都是不可用的,這就導致在選舉期間注冊服務癱瘓。
在雲部署環境下, 因為網路問題使得zk集群失去master節點是大概率事件,雖然服務能最終恢復,但是漫長的選舉事件導致注冊長期不可用是不能容忍的。
Spring Cloud Netflix 在設計 Eureka 時就緊遵AP原則。Eureka是在Java語言上,基於Restful Api開發的服務注冊與發現組件,由Netflix開源。遺憾的是,目前Eureka僅開源到1.X版本,2.X版本已經宣布閉源。
Eureka Server 也可以運行多個實例來構建集群,解決單點問題,但不同於 ZooKeeper 的選舉 leader 的過程,Eureka Server 採用的是Peer to Peer 對等通信。這是一種去中心化的架構,無 master/slave 之分,每一個 Peer 都是對等的。在這種架構風格中,節點通過彼此互相注冊來提高可用性,每個節點需要添加一個或多個有效的 serviceUrl 指向其他節點。每個節點都可被視為其他節點的副本。
在集群環境中如果某台 Eureka Server 宕機,Eureka Client 的請求會自動切換到新的 Eureka Server 節點上,當宕機的伺服器重新恢復後,Eureka 會再次將其納入到伺服器集群管理之中。當節點開始接受客戶端請求時,所有的操作都會在節點間進行復制(replicate To Peer)操作,將請求復制到該 Eureka Server 當前所知的其它所有節點中。
當一個新的 Eureka Server 節點啟動後,會首先嘗試從鄰近節點獲取所有注冊列表信息,並完成初始化。Eureka Server 通過 getEurekaServiceUrls() 方法獲取所有的節點,並且會通過心跳契約的方式定期更新。
默認情況下,如果 Eureka Server 在一定時間內沒有接收到某個服務實例的心跳(默認周期為30秒),Eureka Server 將會注銷該實例(默認為90秒, eureka.instance.lease-expiration-ration-in-seconds 進行自定義配置)。
當 Eureka Server 節點在短時間內丟失過多的心跳時,那麼這個節點就會進入自我保護模式。
Eureka的集群中,只要有一台Eureka還在,就能保證注冊服務可用(保證可用性),只不過查到的信息可能不是最新的(不保證強一致性)。除此之外,Eureka還有一種自我保護機制,如果在15分鍾內超過85%的節點都沒有正常的心跳,那麼Eureka就認為客戶端與注冊中心出現了網路故障,此時會出現以下幾種情況:
Eureka不再從注冊表中移除因為長時間沒有收到心跳而過期的服務;
Eureka仍然能夠接受新服務注冊和查詢請求,但是不會被同步到其它節點上(即保證當前節點依然可用);
當網路穩定時,當前實例新注冊的信息會被同步到其它節點中;
因此,Eureka可以很好的應對因網路故障導致部分節點失去聯系的情況,而不會像zookeeper那樣使得整個注冊服務癱瘓。
Consul 是 HashiCorp 公司推出的開源工具,用於實現分布式系統的服務發現與配置。Consul 使用 Go 語言編寫,因此具有天然可移植性(支持Linux、windows和Mac OS X)。
Consul採用主從模式的設計,使得集群的數量可以大規模擴展,集群間通過RPC的方式調用(HTTP和DNS)。
Consul 內置了服務注冊與發現框架、分布一致性協議實現、健康檢查、Key/Value 存儲、多數據中心方案,不再需要依賴其他工具(比如 ZooKeeper 等),使用起來也較為簡單。
Consul 遵循CAP原理中的CP原則,保證了強一致性和分區容錯性,且使用的是Raft演算法,比zookeeper使用的Paxos演算法更加簡單。雖然保證了強一致性,但是可用性就相應下降了,例如服務注冊的時間會稍長一些,因為 Consul 的 raft 協議要求必須過半數的節點都寫入成功才認為注冊成功 ;在leader掛掉了之後,重新選舉出leader之前會導致Consul 服務不可用。
默認依賴於SDK
Consul本質上屬於應用外的注冊方式,但可以通過SDK簡化注冊流程。而服務發現恰好相反,默認依賴於SDK,但可以通過Consul Template(下文會提到)去除SDK依賴。
Consul Template
Consul,默認服務調用者需要依賴Consul SDK來發現服務,這就無法保證對應用的零侵入性。
所幸通過 Consul Template ,可以定時從Consul集群獲取最新的服務提供者列表並刷新LB配置(比如nginx的upstream),這樣對於服務調用者而言,只需要配置一個統一的服務調用地址即可。
Consul強一致性(C)帶來的是:
Eureka保證高可用(A)和最終一致性:
其他方面,eureka就是個servlet程序,跑在servlet容器中; Consul則是go編寫而成。
etcd是一個採用http協議的分布式鍵值對存儲系統,因其易用,簡單。很多系統都採用或支持etcd作為服務發現的一部分,比如kubernetes。但正事因為其只是一個存儲系統,如果想要提供完整的服務發現功能,必須搭配一些第三方的工具。
比如配合etcd、Registrator、confd組合,就能搭建一個非常簡單而強大的服務發現框架。但這種搭建操作就稍微麻煩了點,尤其是相對consul來說。所以etcd大部分場景都是被用來做kv存儲,比如kubernetes。
etcd 比較多的應用場景是用於服務發現,服務發現 (Service Discovery) 要解決的是分布式系統中最常見的問題之一,即在同一個分布式集群中的進程或服務如何才能找到對方並建立連接。和 Zookeeper 類似,etcd 有很多使用場景,包括:
配置管理
服務注冊發現
選主
應用調度
分布式隊列
分布式鎖
按照官網給出的數據, 在 2CPU,1.8G 內存,SSD 磁碟這樣的配置下,單節點的寫性能可以達到 16K QPS, 而先寫後讀也能達到12K QPS。這個性能還是相當可觀。
etcd 提供了 etcdctl 命令行工具 和 HTTP API 兩種交互方法。etcdctl命令行工具用 go 語言編寫,也是對 HTTP API 的封裝,日常使用起來也更容易。所以這里我們主要使用 etcdctl 命令行工具演示。
(1)注冊中心ZooKeeper、Eureka、Consul 、Nacos對比
https://zhuanlan.hu.com/p/165217227?utm_source=wechat_session
(2)常用的服務發現對比(Consul、zookeeper、etcd、eureka)
https://blog.csdn.net/gaohe7091/article/details/101197107
5. 如何理解中心化與去中心化概念
在一個分布有眾多節點的系統中,每個節點都具有高度自治的特徵。節點之間彼此可以自由連接,形成新的連接單元。任何一個節點都可能成為階段性的中心,但不具備強制性的中心控制功能。節點與節點之間的影響,會通過網路而形成非線性因果關系。這種開放式、扁平化、平等性的系統現象或結構,我們稱之為去中心化。
對於去中心化,很多人都有誤解,甚至進入一個困局——去中心化就是不要中心。去中心化,不是不要中心,而是由節點來自由選擇中心、自由決定中心。簡單地說,中心化的意思,是中心決定節點。節點必須依賴中心,節點離開了中心就無法生存。在去中心化系統中,任何人都是一個節點,任何人也都可以成為一個中心。任何中心都不是永久的,而是階段性的,任何中心對節點都不具有強制性。
舉例說明
中心化:SOA ESB中心化服務架構;去中心化:Dubbo、Spring Cloud去中心化架構。
中心化:上課;去中心化:學術交流
中心化:看電影;去中心化:玩手機
其他看法
通俗地講,中心化就是一個或幾個認證的嘉賓在講話,所有其他人還能參與聽,類似於上課的模式。而去中心化就是每個人都可以講話,都可以選擇聽還是選擇講,就像自由討論模式。以前的門戶網站是中心化,今天的博客,社交媒體都是去中心化。
當然在今天沒有完全的中心化和去中心化,就像大家普遍認為淘寶京東是中心化平台,但它們的用戶也可以通過社交渠道去分享發掘流量,淘寶有自己的「社區」,京東也有自己的「發現」,這些都是去中心化的形態
6. springcloud四個注冊中心的比較
springcloud是一個非常優秀的微服務框架,要管理眾多的服務,就需要對這些服務進行治理,也就是我們說的 服務治理 , 服務治理 的作用就是在傳統的rpc遠程調用框架中,管理每個服務與每個服務之間的依賴關系,可以實現服務調用、負載均衡、服務容錯、以及服務的注冊與發現。
如果微服務之間存在調用依賴,就需要得到目標服務的服務地址,也就是微服務治理的 服務發現 。要完成服務發現,就需要將服務信息存儲到某個載體,載體本身即是微服務治理的 服務注冊中心 ,而存儲到載體的動作即是 服務注冊 。
springcloud支持的注冊中心有 Eureka 、 Zookeeper 、 Consul 、 Nacos
Spring Cloud Netflix 在設計 Eureka 時就緊遵AP原則,Eureka Server 也可以運行多個實例來構建集群,解決單點問題,但不同於 ZooKeeper 的選舉 leader 的過程,Eureka Server 採用的是Peer to Peer 對等通信。這是一種去中心化的架構,無 master/slave 之分,每一個 Peer 都是對等的。在這種架構風格中,節點通過彼此互相注冊來提高可用性,每個節點需要添加一個或多個有效的 serviceUrl 指向其他節點。每個節點都可被視為其他節點的副本。
在集群環境中如果某台 Eureka Server 宕機,Eureka Client 的請求會自動切換到新的 Eureka Server 節點上,當宕機的伺服器重新恢復後,Eureka 會再次將其納入到伺服器集群管理之中。當節點開始接受客戶端請求時,所有的操作都會在節點間進行復制(replicate To Peer)操作,將請求復制到該 Eureka Server 當前所知的其它所有節點中。
Eureka的集群中,只要有一台Eureka還在,就能保證注冊服務可用(保證可用性),只不過查到的信息可能不是最新的(不保證強一致性)。除此之外,Eureka還有一種自我保護機制,如果在15分鍾內超過85%的節點都沒有正常的心跳,那麼Eureka就認為客戶端與注冊中心出現了網路故障,此時會出現以下幾種情況:
因此,Eureka可以很好的應對因網路故障導致部分節點失去聯系的情況,而不會像zookeeper那樣使得整個注冊服務癱瘓
與 Eureka 有所不同,Apache Zookeeper 在設計時就緊遵CP原則,即任何時候對 Zookeeper 的訪問請求能得到一致的數據結果,同時系統對網路分割具備容錯性,但是 Zookeeper 不能保證每次服務請求都是可達的。從 Zookeeper 的實際應用情況來看,在使用 Zookeeper 獲取服務列表時,如果此時的 Zookeeper 集群中的 Leader 宕機了,該集群就要進行 Leader 的選舉,又或者 Zookeeper 集群中半數以上伺服器節點不可用(例如有三個節點,如果節點一檢測到節點三掛了 ,節點二也檢測到節點三掛了,那這個節點才算是真的掛了),那麼將無法處理該請求。所以說,Zookeeper 不能保證服務可用性。
Consul 是 HashiCorp 公司推出的開源工具,用於實現分布式系統的服務發現與配置。Consul 使用 Go 語言編寫,因此具有天然可移植性(支持Linux、windows和Mac OS X)。Consul 內置了服務注冊與發現框架、分布一致性協議實現、健康檢查、Key/Value 存儲、多數據中心方案,不再需要依賴其他工具(比如 ZooKeeper 等),使用起來也較為簡單。
Consul 遵循CAP原理中的CP原則,保證了強一致性和分區容錯性,且使用的是Raft演算法,比zookeeper使用的Paxos演算法更加簡單。雖然保證了強一致性,但是可用性就相應下降了,例如服務注冊的時間會稍長一些,因為 Consul 的 raft 協議要求必須過半數的節點都寫入成功才認為注冊成功 ;在leader掛掉了之後,重新選舉出leader之前會導致Consul 服務不可用。
Nacos是阿里開源的,Nacos 支持基於 DNS 和基於 RPC 的服務發現。Nacos除了服務的注冊發現之外,還支持動態配置服務。動態配置服務可以讓您以中心化、外部化和動態化的方式管理所有環境的應用配置和服務配置。動態配置消除了配置變更時重新部署應用和服務的需要,讓配置管理變得更加高效和敏捷。配置中心化管理讓實現無狀態服務變得更簡單,讓服務按需彈性擴展變得更容易。 一句話概括就是Nacos = 注冊中心 + 配置中心。
這四個組件雖然都實現了注冊中心的功能,但是他們的功能和實現方式都有不同的地方,也各有各的優點,單從注冊中心方面來比價四個注冊中心(如果不了解 CAP定理 可先閱讀下一章節):
CAP原則的精髓就是要麼AP,要麼CP,要麼AC,但是不存在CAP。如果在某個分布式系統中數據無副本, 那麼系統必然滿足強一致性條件, 因為只有獨一數據,不會出現數據不一致的情況,此時C和P兩要素具備,但是如果系統發生了網路分區狀況或者宕機,必然導致某些數據不可以訪問,此時可用性條件就不能被滿足,即在此情況下獲得了CP系統,但是CAP不可同時滿足。
因此在進行分布式架構設計時,必須做出取捨。當前一般是通過分布式緩存中各節點的最終一致性來提高系統的性能,通過使用多節點之間的數據非同步復制技術來實現集群化的數據一致性。通常使用類似 memcached 之類的 NOSQL 作為實現手段。雖然 memcached 也可以是分布式集群環境的,但是對於一份數據來說,它總是存儲在某一台 memcached 伺服器上。如果發生網路故障或是伺服器死機,則存儲在這台伺服器上的所有數據都將不可訪問。由於數據是存儲在內存中的,重啟伺服器,將導致數據全部丟失。當然也可以自己實現一套機制,用來在分布式 memcached 之間進行數據的同步和持久化,但是實現難度是非常大的
例如,根據CAP定理將NoSql數據分成了滿足CA原則、滿足CP原則和滿足AP原則的三大類:
7. 微服務框架之Spring Cloud簡介
在了解 Spring Cloud 之前先了解一下微服務架構需要考量的核心關鍵點,如下圖:
對於以上等核心關鍵點的處理,不需要我們重復造車輪, Spring Cloud 已經幫我們集成了,它使用 Spring Boot 風格將一些比較成熟的微服務框架組合起來,屏蔽掉了復雜的配置和實現原理,為快速構建微服務架構的應用提供了一套基礎設施工具和開發支持。
Spring Cloud 所提供的核心功能包含:
Spring Cloud架構圖
Spring Cloud子項目
Spring Cloud 旗下的子項目大致可以分為兩類:
如下:
1. Spring Cloud 與 Spring Boot
Spring Boot 可以說是微服務架構的核心技術之一。通過在 Spring Boot 應用中添加 Spring MVC 依賴,就可以快速實現基於 REST 架構的服務介面,並且可以提供對 HTTP 標准動作的支持。而且 Spring Boot 默認提供 JackJson 序列化支持,可以讓服務介面輸入、輸出支持 JSON 等。因此,當使用 Spring Cloud 進行微服務架構開發時,使用 Spring Boot 是一條必經之路。
2. Spring Cloud 與服務治理( Eureka )
服務治理是 Spring Cloud 的核心,在實現上其提供了兩個選擇,即 Consul 和 Netflix 的 Eureka 。
Eureka 提供了服務注冊中心、服務發現客戶端,以及注冊服務的 UI 界面應用。
在 Eureka 的實現中,節點之間相互平等,有部分注冊中心「掛掉」也不會對整個應用造成影響,即使集群只剩一個節點存活,也可以正常地治理服務。即使所有服務注冊節點都宕機, Eureka 客戶端中所緩存的服務實例列表信息,也可讓服務消費者能夠正常工作,從而保障微服務之間互相調用的健壯性和應用的彈性。
3. Spring Cloud 與客戶端負載均衡( Ribbon )
Ribbon 默認與 Eureak 進行無縫整合,當客戶端啟動的時候,從 Eureka 伺服器中獲取一份服務注冊列表並維護在本地,當服務消費者需要調用服務時, Ribbon 就會根據負載均衡策略選擇一個合適的服務提供者實例並進行訪問。
Spring Cloud 通過集成 Netflix 的 Feign 項目,為開發者提供了聲明式服務調用,從而簡化了微服務之間的調用處理方式。並且默認 Feign 項目集成了 Ribbon ,使得聲明式調用也支持客戶端負載均衡功能。
4. Spring Cloud 與微服務容錯、降級( Hystrix )
為了給微服務架構提供更大的彈性,在 Spring Cloud 中,通過集成 Netflix 下子項目 Hystrix ,通過所提供的 @HystrixCommand 註解可以輕松為我們所開發的微服務提供容錯、回退、降級等功能。此外, Hystrix 也默認集成到 Feign 子項目中。
Hystrix 是根據「斷路器」模式而創建。當 Hystrix 監控到某服務單元發生故障之後,就會進入服務熔斷處理,並向調用方返回一個符合預期的服務降級處理( fallback ),而不是長時間的等待或者拋出調用異常,從而保障服務調用方的線程不會被長時間、不必要地佔用,避免故障在應用中的蔓延造成的雪崩效應。
而 Hystrix 的儀表盤項目( Dashboard )可以監控各個服務調用所消耗的時間、請求數、成功率等,通過這種近乎實時的監控和告警,可以及時發現系統中潛在問題並進行處理。
5. Spring Cloud 與服務網關( Zuul )
Spring Cloud 通過集成 Netflix 中的 Zuul 實現 API 服務網關功能,提供對請求的路由和過濾兩個功能
路由功能負責將外部請求轉發到具體的微服務實例上,是實現外部訪問統一入口的基礎。
過濾器功能則負責對請求的處理過程進行干預,是實現請求校驗、服務聚合等功能的基礎。
通過 Zuul ,可以將細粒度的服務組合起來提供一個粗粒度的服務,所有請求都導入一個統一的入口,對外整個服務只需要暴露一個 API 介面,屏蔽了服務端的實現細節。通過 Zuul 的反向代理功能,可以實現路由定址,將請求轉發到後端的粗粒度服務上,並做一些通用的邏輯處理。此外, Zuul 默認會與 Eureka 伺服器進行整合,自動從 Eureka 伺服器中獲取所有注冊的服務並進行路由映射,實現 API 服務網關自動配置。
6. Spring Cloud 與消息中間件( Stream )
Spring Cloud 為簡化基於消息的開發,提供了 Stream 子項目,通過建立消息應用抽象層,構建了消息收發、分組消費和消息分片等功能處理,將業務應用中的消息收發與具體消息中間件進行解耦,使微服務應用開發中可以非常方便地與 Kafka 和 RabbitMQ 等消息中間件進行集成。
Spring Cloud Bus 基於 Stream 進行擴展,可以作為微服務之間的事件、消息匯流排,用於服務集群中狀態變化的傳播。
比如 Spring Cloud Config 藉助 Bus ,可以實現配置的動態刷新處理。
7. Spring Cloud 與分布式配置中心( Config )
針對微服務架構下的配置文件管理需求, Spring Cloud 提供了一個 Config 子項目。 Spring Cloud Config 具有中心化、版本控制、支持動態更新和語言獨立等特性。
在 Config 子項目中將微服務應用分為兩種角色:配置伺服器( Config Server )和配置客戶端( Config Client )。使用配置伺服器集中地管理所有配置屬性文件,配置服務中心可以將配置屬性文件存儲到 Git 、 SVN 等具有版本管理倉庫中,也可以存放在文件系統中。默認採用 Git 的方式進行存儲,因此可以很容易地對配置文件進行修改,並實現版本控制。
8. Spring Cloud 與微服務鏈路追蹤( Sleuth )
Spring Cloud 中的 Sleuth 子項目為開發者提供了微服務之間調用的鏈路追蹤。
Sleuth 核心思想就是通過一個全局的 ID 將分布在各微服務服務節點上的請求處理串聯起來,還原了調用關系,並藉助數據埋點,實現對微服務調用鏈路上的性能數據的採集。
因此,通過 Sleuth 可以很清楚地了解到一個用戶請求經過了哪些服務、每個服務處理花費了多長時間,從而可以對用戶的請求進行分析。此外,通過將採集的數據發送給 Zipkin 進行存儲、統計和分析,從而可以實現可視化的分析和展示,幫助開發者對微服務實施優化處理。
9. Spring Cloud 與微服務安全( Security )
Spring Cloud Security 為我們提供了一個認證和鑒權的安全框架,實現了資源授權、令牌管理等功能,同時結合 Zuul 可以將認證信息在微服務調用過程中直接傳遞,簡化了我們進行安全管控的開發。
Spring Cloud Security 默認支持 OAuth 2.0 認證協議,因此單點登錄也可以非常容易實現,並且 OAuth2.0 所生成的令牌可以使用 JWT 的方式,進一步簡化了微服務中的安全管理。
10. Spring Cloud 的其他子項目
8. 90 SpringCloud 解決分布式事務--lcn解決分布式事務
1,分布式事務產生的背景。
分情況而定
1, 在單體的項目中,多個不同的業務邏輯都是在同一個數據源中實現事務管理,是不存在分布式事務的問題,因為同一個數據源的情況都是採用事務管理器,相當於每個事務管理器對應一個數據源。
[圖片上傳失敗...(image-810669-1618491127348)]
2,在單體的項目中,有多個不同的數據源,每個數據源都有自己獨立的事務管理器,互不影響,那麼這時候也會存在多數據源事務管理: 解決方案 jta+Atomikos
[圖片上傳失敗...(image-7df061-1618491220423)]
3,在分布式/微服務架構中,每個服務都有自己的本地事務,每個服務本地事務互不影響,那麼這時候也會存在分布式事務的問題。
事務的定義:
對我們的業務邏輯可以實現提交或者回滾,保證數據的一致性的情況。
所以要麼提交,要麼回滾
原子性a 要麼提交 要麼回滾
一致性c
隔離性i 多個事務在一起執行的時候,互不影響;
持久性d 事務一旦提交或者回滾後,不會在對該結果有任何影響
2,傳統分布式事務解決方案
3,2PC/3PC協議使用場景。
4,LCN為什麼不更新了?那些思想值得學習、
5,分布式事務解決方案有哪些?
6,強一致性/最終一致性區別。
7,LCn深度源碼解讀。
1 CAP定律和BASE理論
1.1 CAP定律#
這個定理的內容是指的是在一個分布式系統中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。
(一)一致性(C)
在分布式系統中的所有數據備份,在同一時刻是否同樣的值。(等同於所有節點訪問同一份最新的數據副本)
(二)可用性(A)
在集群中一部分節點故障後,集群整體是否還能響應客戶端的讀寫請求。(對數據更新具備高可用性)
(三)分區容錯性(P) 形成腦裂問題
以實際效果而言,分區相當於對通信的時限要求。系統如果不能在時限內達成數據一致性,就意味著發生了分區的情況,必須就當前操作在C和A之間做出選擇。
https://ke..com/item/%E5%AE%B9%E9%94%99%E7%8E%87/9967698?fr=aladdin
(四)總結一下
以上可以知道分區容錯性(P)主要代表網路波動產生的錯誤,這是不可避免的,且這個三個模式不可兼得,所以目前就只有兩種模式:CP和AP模式。
其中CP表示遵循一致性原則,但不能保證高可用性,其中zookeeper作為注冊中心就是採用CP模式,因為zookeeper有過半節點不可以的話整個zookeeper將不可用。
AP表示遵循於可用性原則,例如Eureka作為注冊中心用的是AP模式,因為其為去中心化,採用你中有我我中有你的相互注冊方式,只要集群中有一個節點可以使用,整個eureka服務就是可用的,但可能會出現短暫的數據不一致問題。
AP保證可用性:但是不能保證每個副本數據數據一致性;
CP保證數據一致性:如果有過半的zk節點宕機的情況下,不能保證可用性,但是必須保證每個副本節點之間數據一致性, 比如ZK;
BASE是Basically Available(基本可用)、Soft state(軟狀態)和 Eventually consistent(最終一致性)三個短語的縮寫。BASE理論是對CAP中一致性和可用性權衡的結果,其來源於對大規模互聯網系統分布式實踐的總結, 是基於CAP定理逐步演化而來的。BASE理論的核心思想是:即使無法做到強一致性,但每個應用都可以根據自身業務特點,採用適當的方式來使系統達到最終一致性。
(一)基本可用
基本可用是指分布式系統在出現不可預知故障的時候,允許損失部分可用性,注意,這絕不等價於系統不可用。
比如:響應時間上的損失。正常情況下,一個在線搜索引擎需要在0.5秒之內返回給用戶相應的查詢結果,但由於出現故障,查詢結果的響應時間增加了1~2秒。
系統功能上的損失:正常情況下,在一個電子商務網站上進行購物的時候,消費者幾乎能夠順利完成每一筆訂單,但是在一些節日大促購物高峰的時候,由於消費者的購物行為激增,為了保護購物系統的穩定性,部分消費者可能會被引導到一個降級頁面。
(二)軟狀態
軟狀態指允許系統中的數據存在中間狀態,並認為該中間狀態的存在不會影響系統的整體可用性, 即允許系統在不同節點的數據副本之間進行數據同步的過程存在延時 。
(三)最終一致性
最終一致性強調的是所有的數據副本,在經過一段時間的同步之後,最終都能夠達到一個一致的狀態。因此,最終一致性的本質是需要系統保證最終數據能夠達到一致,而不需要實時保證系統數據的強一致性。
目前主流分布式解決框架:
1,單體項目多數據源,可以jta+Atomikos
2,基於RabbitMQ的形式解決,最終一致性的思想。
3,基於RocketMQ解決分布式事務,採用事務消息。
4,LCn採用lcn模式,假關閉連接
5,Alibaba的Seata
6,跨語言的方式實現解決分布式事務問題,類似於支付寶回調。
倆階段提交協議基本概念:
2階段提交協議可以理解為2pc,也就是分為參與者和協調者,協調者會通過2次階段實現數據最終一致性。
2pc和3pc的區別就是解決參與者超時的問題和多加了一層詢問,保證數據的傳輸可靠性。
http://www.txlcn.org/zh-cn/ LCN並不生產事務,LCN只是本地事務的協調工
現在官網已經不維護呢,可以參考:GitEE
https://gitee.com/wangliang1991/tx-lcn?_from=gitee_search
默認密碼為:codingapi
lcn基本實現處理:
1,發起方與參與方都與我們的lcn管理器一直保持長連接;
2,發起方在調用介面前,先向lcn管理器中申請一個全局的事務分組id.
3,發起方調用介面的時候在請求頭中傳遞事務分組id
,4參與方獲取到請求頭中有事務分組的id的,則當前業務邏輯執行完實現假關閉,不會提交或者回滾事務『
5,發起方調用完介面後,如果出現異常的情況下,在通知給事務回滾事務,這時候事務協調則告訴參與方回滾當前的事務。
lcn解決分布式事務的原理:
角色劃分:
1,全局事務協調者(組長);
2,發起方--調用介面者
3,參與方--被別人調用介面。
訂單(發起方)調用派單(參與方)
1.發起方和參與方都會與我們的全局事務協調者保持長連接;
SpringBoot整合lcn5.0
Maven依賴
相關配置
參與方獲取全局id
1.SpringTracingApplier
攔截器 獲取feign客戶端請求頭中的參數全局事務分組id,緩存到threadlocal中。
9. 86 SpringCloud解決分布式事務
1,分布式事務產生的背景;
分情況而定。
1,在單體項目中,多個不同的業務邏輯都是在同一個數據源中心實現事務管理,是不存在分布式事務的問題。因為在同一個數據源的情況下都是採用事務管理器,相當於每個事務管理器對應一個數據源。
2,在單體項目中,有多個不同的數據源,每個數據源中都有自己獨立的事務管理器,互不影響,那麼這時候也會存在多數據源事務管理:解決方案jta+Atomikos
3,在分布式/微服務架構中。每個服務都有自己的本地的事務。每個服務本地事務互不影響,那麼這時候也會存在分布式事務的問題。
分布式事務產生的背景:訂單服務調用派單服務介面成功之後,可能會引發錯誤。
2pc3pc思想,實際上都是解決我們在分布式系統中,每個節點保證數據一致性問題。
事務的定義。
對我們業務邏輯可以實現提交或者是回滾,保證數據一致性的情況。所以,要麼提交,要麼回滾。
原子性a 要麼提交 要麼回滾。
一致性 c
持久性d 事務一旦提交或者回滾後,不會再對該結果有任何的影響。
Base 與 cap理論。
1,cap定律
這個定理的內容是指的是在一個分布式系統中,Consistency(一致性),Availability(可用性),Partition tolerance(分區容錯性),二者不可兼容。
1,一致性(C)
在分布式系統中的所有數據備份,是在同一時刻是否同樣的值,(等同於所有節點訪問同一份最新的數據副本)
2,可用性 A
在集群中一部分節點故障後,集群整體是否還能響應客戶端的讀寫請求(對數據更新具有高可用性)
3,分區容錯性(p) 形成腦裂問題:
以實際效果而言,分區相當於對通信的時限要求,系統如果不能再時限內達成數據一致性,就意味著發生了分區的情況,必須就當前操作在C和A之間做出選擇。
4,總結下
以上可以知道分區容錯性(P)主要代表網路波動產生的錯誤,這是不可以避免的,且這三個模式不可以兼得,所以目前就2種模式: cp和Ap模式。
其中cp表示遵循一致性的原則,但不能保證高可用性,其中zookeeper作為注冊中心就是採用cp模式,因為zookeeper有過半節點不可以的話整個zookeeper將不可用。
AP表示遵循於可用性原則,例如Eureka作為注冊中心用的是AP模式,因為其為去中心化,採用你中有我我中有你的相互注冊方式,只要集群中有一個節點可以使用,整個eureka服務就是可用的,但可能會出現短暫的數據不一致問題。
Ap保證可用性:但是不能保證每個副本數據數據一致性,
cp保證數據一致性;如果有過半的zk節點宕機的情況下,不能保證可用性,但是必須保證每個副本節點之間數據一致性,比如zk。
Base理論:
Base是 Basically Available(基本可用),Softstate(軟狀態)和Eventually consistent(最終一致性)三個短語的縮寫。Base理論是對CAP定理逐步演化而來的,base理論核心思想是:即使無法做到強一致性,但每個應用都可以根據自身業務特點,採用適當的方式達到最終一致性。
1基本可用性;
基本可用是指分布式系統在出現不可預知故障的時候,允許損失部分可用性,注意:這絕不等於系統不可用。
比如: 響應時間的損失,正常情況下,在一個電子商務網站上進行購物的時候,消費者幾乎能順利完成每一筆訂單,但是在一些介入大促銷購物高峰的時候,由於消費者的購物行為激增,為了保護購物系統的穩定性,部分消費者可能被引導在一個降級頁面。
2,軟狀態。
軟狀態指允許系統中的數據存在中間狀態,並認為該中間狀態的存在不會影響系統的整體可用性,既允許系統在不同節點的數據副本之間進行數據同步的過程存在延時。
3,最終一致性
最終一致性強調的是所有的數據副本,在經過一段時間的同步之後,最終都能夠達到一個一致的狀態,因此,最終一致性的本質需要系統保證數據能夠達成一致,而不需要時時保證系統數據的強一致性。
2pc 與3pC
通過2pc和3pc 思想可以實現保證每個節點的數據一致性問題。
目前主流分布式解決框架
1,單體項目多數據源,可以jta+Atomilos;
2,基於Rabbitmq的形式解決 最終一致性思想;
3,基於Rocketmq解決分布式事務 ,採用事務消息。
4,lcn採用lcn模式,假關閉連接
5,Alibaba的seata 背景強大,已經成為了主流。
以上適合於微服務架構中,不適合於和外部介面保證分布式事務問題。
6,跨語言的方式實現解決分布式事務問題。類似於支付寶回調方式。
2階段提交協議基本概念。
2階段提交協議基本概念:
倆階段提交協議可以理解為2pc,也就是分為參與者和協調者,協調者會通過2次階段實現數據最終一致性的
2pc和3pc 的區別就是解決參與者超時問題和多加了一層詢問。保證了數據傳輸的可靠性。
簡單的回顧下lcn解決分布式事務。
http://www.txlcn.org/zh-cn/ LCN並不生產事務,LCN只是本地事務的協調工
現在官網已經不維護呢,可以參考:GitEE
https://gitee.com/wangliang1991/tx-lcn?_from=gitee_search
默認密碼為:codingapi
lcn基本實現的原理:
1,發起方與參與方都與我們的lcn保持長連接;
2,發起方調用介面前,先向lcn管理器中申請一個全局的事務分組id;
3,發起方調用介面的時候在請求頭里傳遞事務分組id.
4,參與方獲取到請求頭中有事務分組的id的,則當前業務邏輯執行完實現假關閉,不會提交或者回滾當前事務,
5,發起方調用完介面後,如果出現異常的情況下,在通知事務協調者回滾事務,這時候事務協調則告訴給參與者回滾當前的事務。
lcn 解決分布式事務的原理:
角色劃分
1,全局事務協調者(組長);
2,發起方---調用介面者;
3,參與方---被別人調用介面
訂單(發起方)調用派單(參與方)
1.發起方和參與方都會與我們的全局事務協調者保持長連接;
整合和源碼解讀
spring-boot 2.1.6.RELEASE+spring-cloud Greenwich.RELEASE +seata 1.4
pom如下:
file.conf
registry.conf:
yml配置:
添加 DataSourceConfiguration
其他模塊參照此模塊配置,
seata的registry.conf 和file.conf配置和項目配置一致。
添加全局事務:
https://gitee.com/lttwj/wj1/tree/master/seata/springcloud-seata
1,如何學會分析框架的源碼?思想有哪些?
A: spring入口角度分析 springbean 生命周期ioc容器底層原理。
B. 報錯日誌法
2,seata 底層如何解決分布式事務的?
3,seata 如何生成全局xid
4,Seata如何生成前置和後置鏡像。
5,seata 如何傳遞xid的
6, Seata 如何實現逆向回滾
7,如果協調者宕機了,參與事務是回滾還是提交
8,如果協調者宕機發起方沒有通知協調者到底是提交還是回滾?
10. SpringCloud整體構架設計(一)
SpringClound整體核心架構只有一點:Rest服務,也就是說在整個SpringCloud配置過程之中,所有的配置處理都是圍繞著Rest完成的,在這個Rest處理之中,一定要有兩個端:服務的提供者(Provider)、服務的消費者(Consumer),所以對於整個SpringCloud基礎的結構就如下所示:
既然SpringCloud的核心是Restful結構,那麼如果要想更好的去使用Rest這些微服務還需要考慮如下幾個問題。
1、所有的微服務地址一定會非常的多,所以為了統一管理這些地址信息,也為了可以及時的告訴用戶哪些服務不可用,所以應該准備一個分布式的注冊中心,並且該注冊中心應該支持有HA機制,為了高速並且方便進行所有服務的注冊操作,在SpringCloud裡面提供有一個Eureka的注冊中心。
對於整個的WEB端的構架(SpringBoot實現)可以輕松方便的進行WEB程序的編寫,而後利用Nginx或Apache實現負載均衡處理,但是你WEB端出現了負載均衡,那麼業務端呢?應該也提供有多個業務端進行負載均衡。那麼這個時候就需要將所有需要參與到負載均衡的業務端在Eureka之中進行注冊。
在進行客戶端使用Rest架構調用的時候,往往都需要一個調用地址,即使現在使用了Eureka作為注冊中心,那麼它也需要有一個明確的調用地址,可是所有的操作如果都利用調用地址的方式來處理,程序的開發者最方便應用的工具是介面,所以現在就希望可以將所有的Rest服務的內容以介面的方式出現調用,所以它又提供了一個Feign技術,利用此技術可以偽造介面實現。
在進行整體的微架構設計的時候由於牽扯的問題還是屬於RPC,所以必須考慮熔斷處理機制,實際上所有的熔斷就好比生活之中使用保險絲一樣,有了保險絲在一些設備出現了故障之後依然可以保護家庭的電器可以正常使用,如果說現在有若乾的微服務,並且這些微服務之間可以相互調用,例如A微服務調用了B微服務,B微服務調用了C微服務。
如果在實際的項目設計過程之中沒有處理好熔斷機制,那麼就會產生雪崩效應,所以為了防止這樣的問題出現,SpringCloud裡面提供有一個Hystrix熔斷處理機制,以保證某一個微服務即使出現了問題之後依然可以正常使用。
通過Zuul的代理用戶只需要知道指定的路由的路徑就可以訪問指定的微服務的信息,這樣更好的提現了java中的「key=value」的設計思想,而且所有的微服務通過zuul進行代理之後也更加合理的進行名稱隱藏。
在SpringBoot學習的時候一直強調過一個問題:在SpringBoot裡面強調的是一個「零配置」的概念,本質在於不需要配置任何的配置文件,但是事實上這一點並沒有完全的實現,因為在整個在整體的實際裡面,依然會提供有application.yml配置文件,那麼如果在微服務的創建之中,那麼一定會有成百上千個微服務的信息出現,於是這些配置文件的管理就成為了問題。例如:現在你突然有一天你的主機要進行機房的變更,所有的服務的IP地址都可能發生改變,這樣對於程序的維護是非常不方便的,為了解決這樣的問題,在SpringCloud設計的時候提供有一個SpringCloudConfig的程序組件,利用這個組件就可以直接基於GIT或者SVN來進行配置文件的管理。
在整體設計上SpringCloud更好的實現了RPC的架構設計,而且使用Rest作為通訊的基礎,這一點是他的成功之處,由於大量的使用了netflix公司的產品技術,所以這些技術也有可靠的保證。