📝 代碼記錄
Consul(服務注冊與發現 + 分布式配置管理)
擁有服務治理功能,實現微服務之間的動態注冊與發現
?不在使用Eureka:1. 停更進維 2. 注冊中心獨立且和微服務功能解耦
Consul官網
Spring官方介紹
三個注冊中心區別
組件名 | 語言 | CAP | 服務健康檢查 | 對外暴露接口 | Spring Cloud集成 |
---|---|---|---|---|---|
Eureka | Java | AP | 可配支持 | HTTP | 已集成 |
Consul | Go | CP | 支持 | HTTP/DNS | 已集成 |
Zookeeper | Java | CP | 支持 | 客戶端 | 已集成 |
Nacos | Java | AP | 支持 | 客戶端 | 已集成 |
CAP
一個分布式系統最多只能同時滿足其中的兩個屬性。
- Consistency: 強一致性,每次讀取都能獲取到最近一次成功寫入的數據。
- Availablity: 可用性,每次請求都會在有限時間內返回結果,無論結果是否為最新。
- Partition tolerance: 分區容錯性,系統在遇到網絡分區(節點之間無法通信)時仍能繼續運作。(必須有)
經典CAP:
- AP(Eurake):當網絡分區出現后,為了保證可用性,系統B可以返回舊值,保證系統的可用性。
- CP:當網絡分區出現后,為了保證一致性,系統返回錯誤信息。
當數據出現不一致時,雖然A, B上的注冊信息不完全相同,但每個Eureka節點依然能夠正常對外提供服務,這會出現查詢服務信息時如果請求A查不到,但請求B就能查到。如此保證了可用性但犧牲了一致性結論:違背了一致性C的要求,只滿足可用性和分區容錯,即AP
當網絡分區出現后,為了保證一致性,就必須拒接請求,否則無法保證一致性,Consul 遵循CAP原理中的CP原則,保證了強一致性和分區容錯性,且使用的是Raft算法,比zookeeper使用的Paxos算法更加簡單。雖然保證了強一致性,但是可用性就相應下降了,例如服務注冊的時間會稍長一些,因為 Consul 的 raft 協議要求必須過半數的節點都寫入成功才認為注冊成功 ;在leader掛掉了之后,重新選舉出leader之前會導致Consul 服務不可用。結論:違背了可用性A的要求,只滿足一致性和分區容錯,即CP
使用
./consul --version # 查看版本號
./consul agent -dev # 開發模式啟動 http://localhost:8500
LoadBalance(服務調用復雜均衡)
負載均衡:平攤請求,減少服務器壓力
Spring-cloud-starter-loadbalancer:spring官方提供的客戶端負載均衡器,在SpringCloud-commons中,用來替代以前的Ribbon,支持RestTemplate、Web Flux。
客戶端與服務器端負載均衡區別:服務器端如Nginx,將客戶端發起的請求到通過服務器端部署的Nginx,轉發到各個服務器上。LoadBlance本地負載均衡,調用微服務接口時,在注冊中心上獲取信息服務列表之后緩存到JVM本地,從而在本地實現RPC遠程服務調用。
Spring官方介紹
負載均衡算法
實現ReactiveLoadBalancer接口,默認使用的是RoundRobinLoadBalancer。
官方對負載均衡算法的介紹
- RoundRobinLoadBalancer: 輪詢
- RandomLoadBalancer: 隨機
OpenFeign(服務調用復雜均衡)
Feign是一個聲明式web服務客戶端,用來替代RestTemplate,只需創建一個Rest接口并在該接口上添加注解@FeignClient。
OpenFeign基本上就是當前微服務之間調用的事實標準。
可以結合LoadBalancer實現負載均衡,結合Sentinel實現熔斷降級。
Spring官方介紹 Github
超時控制
超時控制說明
- connectTimeout: 連接超時時間
- readTimeout: 請求處理超時時間,默認超時時間是60s
重試機制
重試機制說明
默認情況下會創建Retry.NEVER_RETRY類型的Retry的bean,這將禁用重試,這種重試行為與Feign默認行為不同,他會自動重試IOExceptions,將它們視為網絡相關的瞬態異常,以及從ErrorDecoder拋出的任何RetryableException。
默認HTTPClient修改
默認HTTPClient修改說明
如果不做特殊配置,OpenFeign默認使用JDK自帶的HttpURLConnection發送HTTP請求,
由于默認HttpURLConnection沒有連接池、性能和效率比較低 ,如果采用默認,無法發揮最大性能,故使用Apach的HTTPClient 5替換默認HTTPURLConnection。
??注意:httpclient的版本對齊
請求/響應壓縮
請求/響應壓縮說明
Spring Cloud OpenFeign支持對請求和響應進行GZIP壓縮,以減少通信過程中的性能損耗。
日志打印功能
日志打印功能說明
Feign 提供了日志打印功能,我們可以通過配置來調整日志級別,
日志級別:
- NONE:默認的,不顯示任何日志;
- BASIC:僅記錄請求方法、URL、響應狀態碼及執行時間;
- HEADERS:除了 BASIC 中定義的信息之外,還有請求和響應的頭信息;
- FULL:除了 HEADERS 中定義的信息之外,還有請求和響應的正文及元數據
CircuitBreaker(斷路器)
??較為繁瑣,資料少,不適合自學,面試必考
解決服務雪崩問題,對于有問題的節點,快速熔斷(快速返回失敗處理或者返回默認兜底數據【服務降級】)
“斷路器”本身是一種開關裝置,當某個服務單元發生故障之后,通過斷路器的故障監控(類似熔斷保險絲),向調用方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者拋出調用方無法處理的異常,這樣就保證了服務調用方的線程不會被長時間、不必要地占用,從而避免了故障在分布式系統中的蔓延,乃至雪崩。
當一個組件或服務出現故障時,CircuitBreaker會迅速切換到開放OPEN狀態(保險絲跳閘斷電),阻止請求發送到該組件或服務從而避免更多的請求發送到該組件或服務。這可以減少對該組件或服務的負載,防止該組件或服務進一步崩潰,并使整個系統能夠繼續正常運行。同時,CircuitBreaker還可以提高系統的可用性和健壯性,因為它可以在分布式系統的各個組件之間自動切換,從而避免單點故障的問題。
Spring官方介紹
Spring Cloud提供的兩個實現類:Resilience4j、Spring Retry
Resilience4j
Resilience4j官網
Resilience4j 是一個輕量級的容錯庫,專為函數式編程設計。Resilience4j 提供了高階函數(裝飾器),可以增強任何函數式接口、lambda 表達式或方法引用,添加斷路器、速率限制器、重試或艙壁。您可以在任何函數式接口、lambda 表達式或方法引用上堆疊多個裝飾器。優點是可以自由選擇所需的裝飾器,而不需要其他任何東西。
斷路(Circuit Breaker)
斷路器狀態
- OPEN
- CLOSED
- HALF_OPEN
- DISABLED(特殊狀態)
- FORCED_OPEN(特殊狀態)
3大狀態之間的轉換
- 當熔斷器關閉時,所有請求都會通過熔斷器
- 失敗率超過設定的閾值,熔斷器就會從CLOSED轉到OPEN,這時所有的請求都會被拒絕
- 當經過一段時間后,熔斷器會從OPEN變為HALF_OPEN,這時有一定數量的請求會被放入,并重新計算失敗率
- 如果失敗率超過閾值,則變為OPEN狀態,如果失敗率低于閾值,則變為CLOSED狀態
- 斷路器使用滑動窗口來存儲和統計調用的結果
- 基于訪問數量的滑動窗口:統計了最近N次調用的返回結果
- 基于時間的滑動窗口:統計了最近N秒的調用返回結果
- 特殊狀態
- 這兩個狀態不會生成熔斷時間,并且不回記錄事件的成功或失敗
- 退出這兩個狀態的唯一方法是觸發狀態轉換或者熔斷器
配置
failure-rate-threshold | 以百分比配置失敗率峰值 |
---|---|
sliding-window-type | 斷路器的滑動窗口期類型: 基于“次數”(COUNT_BASED)、“時間”(TIME_BASED)進行熔斷,默認是COUNT_BASED。 |
sliding-window-size | 若COUNT_BASED,則N次調用中有failure-rate-threshold%失敗(即5次)打開熔斷斷路器;若為TIME_BASED則,此時還有額外的兩個設置屬性,含義為:在N秒內(sliding-window-size)100%(slow-call-rate-threshold)的請求超過N秒(slow-call-duration-threshold)打開斷路器。 |
slowCallRateThreshold | 以百分比的方式配置,斷路器把調用時間大于slowCallDurationThreshold的調用視為慢調用,當慢調用比例大于等于峰值時,斷路器開啟,并進入服務降級。 |
slowCallDurationThreshold | 配置調用時間的峰值,高于該峰值的視為慢調用。 |
permitted-number-of-calls-in-half-open-state | 運行斷路器在HALF_OPEN狀態下時進行N次調用,如果故障或慢速調用仍然高于閾值,斷路器再次進入打開狀態。 |
minimum-number-of-calls | 在每個滑動窗口期樣本數,配置斷路器計算錯誤率或者慢調用率的最小調用數。比如設置為5意味著,在計算故障率之前,必須至少調用5次。如果只記錄了4次,即使4次都失敗了,斷路器也不會進入到打開狀態。 |
wait-duration-in-open-state | 從OPEN到HALF_OPEN狀態需要等待的時間 |
例子:
6次訪問中當執行方法的失敗率達到50%時CircuitBreaker將進入開啟OPEN狀態(保險絲跳閘斷電)拒絕所有請求。
等待5秒后,CircuitBreaker將自動從開啟OPEN狀態過渡到半開HALF_OPEN狀態,允許一些請求通過以測試服務是否恢復正常。
如還是異常CircuitBreaker將重新進入開啟OPEN狀態;如正常將進入關閉CLOSE閉合狀態恢復正常處理請求。
failure-rate-threshold: 50 # 設置50%的失敗率閾值,超過失敗請求百分比CircuitBreaker變為open
sliding-window-type: COUNT_BASED # 滑動窗口的類型
sliding-window-size: 6 # 滑動窗口的大小,單位為請求數
minimum-number-of-calls: 6 # 斷路器計算失敗率或慢調用之前所需的最小樣本(每個滑動窗口周期)
automatic-transition-from-open-to-half-open-enabled: true # 是否啟用自動從開啟到半開啟狀態,默認值為false
wait-duration-in-open-state: 5s # 從OPEN到HALF_OPEN狀態的等待時間
permitted-number-of-calls-in-half-open-state: 2 # 半開狀態允許的最大請求數,默認值為10.
failure-rate-threshold: 50 # 設置50%的失敗率閾值,超過失敗請求百分比CircuitBreaker變為open
slow-call-duration-threshold: 2s # 慢調用時間閾值,高于此時間的調用將被視為慢調用并增加調用比例。
slow-call-rate-threshold: 30 # 慢調用百分比閾值,超過此百分比的慢調用將觸發斷路器。
sliding-window-type: TIME_BASED
sliding-window-size: 2 # 滑動窗口的大小配置,配置TIME_BASED表示2秒
minimum-number-of-calls: 2 # 斷路器計算失敗率或慢調用之前所需的最小樣本(每個滑動窗口周期)
permitted-number-of-calls-in-half-open-state: 2 # 半開狀態允許的最大請求數,默認值為10.
wait-duration-in-open-state: 5s # 從OPEN到HALF_OPEN狀態的等待時間
record-exceptions:- java.lang.Exception
艙壁隔離(Bulkhead)
依賴隔離&負載保護:用來限制對于下游服務的最大并發的限制
艙壁隔離說明
兩種隔離方式
- 信號量艙壁(SemaphoreBulkhead):
- 當信號量有空閑時,進入系統的請求會直接獲取信號量并開始業務處理。
- 當信號量全備占用時,接下來的請求將會進入阻塞狀態,SemaphoreBulkhead提供了一個阻塞計時器
- 如果阻塞狀態的請求在阻塞計時內無法獲取到信號量則系統會拒絕這些請求。
- 若請求在阻塞計時內獲取到了信號量,那將直接獲取信號量并執行相應的業務處理
- FixedThreadPoolBulkhead使用了有界隊列和固定大小線程池
- 當線程池中存在空閑時,則此時進入系統的請求將直接進入線程池開啟新線程或使用空閑線程來處理請求
- 當線程中無空閑時,接下來的請求進入等待隊列
- 若等待隊列仍然無剩余空間時接下來的請求將直接被拒絕
- 在隊列中的請求等待線程池出現空閑時,將進入線程池進行業務處理
- 另,ThreadPoolBulkhead只對CompletableFuture方法有效,所以必須創建返回CompletableFuture類型的方法
速率限制(Rate Limiter)
速率限制說明
限流算法
- 漏斗算法(Leaky Bucket)
- 一個固定容量的漏桶,按照設定常量固定速率流出水滴,類似醫院打吊針,不管你源頭流量多大,我設定勻速流出。
- 如果流入水滴超出了桶的容量,則流入的水滴將會溢出了(被丟棄),而漏桶容量是不變的。
- 缺點:對于存在突發特性的流量來說缺乏效率。
- 令牌桶算法(Token Bucket)
- Spring Cloud 默認使用的算法
- 當用戶發起請求,先判斷桶空不空
- 令牌桶算法會勻速的添加令牌至令牌桶中,桶可容納令牌的數量是有限的。
- 用戶每次發起請求,先檢查桶是否為空。若桶空,則丟棄請求;若桶不空,則申請獲得令牌,獲得令牌則可排隊讓處理器處理當前請求
- 滾動時間窗口算法(Tumbling time window)
- 允許固定數量的請求進入(比如1秒取4個數據相加,超過25值就over)超過數量就拒絕或者排隊,等下一個時間段進入。
- 由于是在一個時間間隔內進行限制,如果用戶在上個時間間隔結束前請求(但沒有超過限制),同時在當前時間間隔剛開始請求(同樣沒超過限制),在各自的時間間隔內,這些請求都是正常的。
- 缺點:間隔臨界的一段時間內的請求就會超過系統限制,可能導致系統被壓垮。
- 滑動時間窗口算法(Sliding time window)
- 滑動窗口算法是把固定時間片進行劃分并且隨著時間移動,移動方式為開始時間點變為時間列表中的第2個時間點,結束時間點增加一個時間點。
- 不斷重復,通過這種方式可以巧妙的避開計數器的臨界點的問題。
Sleuth(Micrometer) + Zipkin(分布式鏈路追蹤)
分布式鏈路追蹤(Distributed Tracing),就是將一次分布式請求還原成調用鏈路,進行日志記錄,性能監控并將一次分布式請求的調用情況集中展示。比如各個服務節點上的耗時、請求具體到達哪臺機器上、每個服務節點的請求狀態等等。
?? Sleuth停更進維, Sleuth替代方案Micrometer
Micrometer官方文檔
分布式鏈路追蹤原理
兩個關鍵id:Track Id(鏈路id)、Span Id(節點id)、Parent Id(父級節點id,Span Id)
主要通過Span Id記錄整條鏈路
Dashboard
- ZipKin:由Twitter公司開源,開放源代碼分布式的跟蹤系統,用于收集服務的定時數據,以解決微服務架構中的延遲問
題,包括:數據的收集、存儲、查找和展現。結合spring-cloud-sleuth使用較為簡單,集成方便,但是功能較
簡單。 - Cat:由大眾點評開源,基于Java開發的實時應用監控千臺,包括實時應用監控,業務監控。集成方案是通過代碼埋
點的方式來實現監控,比如:攔截器,過濾器等。對代碼的侵入性很大,集成成本較高。風險較大。 - Pinpoint:Pinpoint是一款開源的基于字節碼注入的調用鏈分析,以及應用監控分析工具。特點是支持多種插件,UI功能
強大,接入端無代碼侵入。 - Skywalking: SkyWalking是國人開源的基于字節碼注入的調用鏈分析,以及應用監控分析工具。特點是支持多
種插件,UI功能較強,接入端無代碼侵入。
整合Micrometer Tracing
由于Micrometer Tracing是一個門面工具自身并沒有實現完整的鏈路追蹤系統,具體的鏈路追蹤另外需要引入的是第三方鏈路追蹤系統的依賴:
- micrometer-tracing-bom 導入鏈路追蹤版本中心,體系化說明
- micrometer-tracing 指標追蹤
- micrometer-tracing-bridge-brave 一個Micrometer模塊,用于與分布式跟蹤工具 Brave 集成,以收集應用程序的分布式跟蹤數據。Brave是一個開源的分布式跟蹤工具,它可以幫助用戶在分布式系統中跟蹤請求的流轉,它使用一種稱為"跟蹤上下文"的機制,將請求的跟蹤信息存儲在請求的頭部,然后將請求傳遞給下一個服務。在整個請求鏈中,Brave會將每個服務處理請求的時間和其他信息存儲到跟蹤數據中,以便用戶可以了解整個請求的路徑和性能。
- micrometer-observation 一個基于度量庫 Micrometer的觀測模塊,用于收集應用程序的度量數據。
- feign-micrometer 一個Feign HTTP客戶端的Micrometer模塊,用于收集客戶端請求的度量數據。
- zipkin-reporter-brave 一個用于將 Brave 跟蹤數據報告到Zipkin 跟蹤系統的庫。
- 補充包:spring-boot-starter-actuator SpringBoot框架的一個模塊用于監視和管理應用程序
Gateway(服務網關 )
以前都是用Zuul,但是Zuul更新太水了,Spring Cloud 自己研發了Gateway替代Zuul
Spring Cloud Gateway組件的核心是一系列的過濾器,通過這些過濾器可以將客戶端發送的請求轉發(路由)到對應的微服務。 Spring Cloud Gateway是加在整個微服務最前沿的防火墻和代理器,隱藏微服務結點IP端口信息,從而加強安全保護。Spring Cloud Gateway本身也是一個微服務,需要注冊進服務注冊中心。
Spring官方介紹
三大核心
- 路由(Route):網關的基本構建塊。它由一個ID、一個目標URI、一組斷言和一組過濾器定義。如果斷言為True,則匹配路由。
- 斷言(Predicate):參考的是Java8中的java.util.function.Predicate, 允許匹配HTTP請求中的任何內容,例如頭或參數。如果請求與斷言相匹配則進行路由。
- 過濾器(Filter):指的是Spring框架中GatewayFilter的實例,使用過濾器,可以在請求被路由前或者之后對請求進行修改。
Gateway工作流程
工作流程說明
客戶端向 Spring Cloud Gateway 發出請求。然后在 Gateway Handler Mapping 中找到與請求相匹配的路由,將其發送到 Gateway Web Handler。Handler 再通過指定的過濾器鏈來將請求發送到我們實際的服務執行業務邏輯,然后返回。
過濾器之間用虛線分開是因為過濾器可能會在發送代理請求之前(Pre)或之后(Post)執行業務邏輯。
在“pre”類型的過濾器可以做參數校驗、權限校驗、流量監控、日志輸出、協議轉換等;
在“post”類型的過濾器中可以做響應內容、響應頭的修改,日志的輸出,流量監控等有著非常重要的作用。
核心:路由轉發 + 斷言判斷 + 執行過濾器鏈
Route以微服務名 - 動態獲取服務URI
在配置route的uri時使用 lb://服務名 即可
Predicate斷言
Predicate斷言說明
Spring Cloud中Gateway有一個RoutePredictFactory,通過RoutePredicate工廠類可以創建Predict對象,Predict對象用于Route的匹配。Spring Cloud Gateway中包含多個內置的Route Predicate Factories:After、Before、Between、Cookie、Header、Host、Method、Path、Query、ReadBody、RemoteAddr、XForwardedRemoteAddr、Weight、CloudFoundryRouteService,當然也可以自定義。
Filter過濾
Filter過濾說明
功能上類似SpringMVC里的攔截器Interceptor,Servlet里的過濾器
"pre"和"post"分別會在請求被執行前調用和被執行后調用,用來修改請求和響應信息
作用:請求鑒權、異常處理、記錄接口調用時長統計
過濾器分類
- 單一內置過濾器
- 自定義過濾器