目錄
注冊中心 nacos | eurekaServer |zookeeper(dubbo)
配置中心 nacos | config Server
遠程服務調用 httpClient | RestTemplate | OpenFeign
負載均衡服務 ribbon | loadbalancer
網關 zuul | gateway
熔斷器 hystrix | sentinel
網關
sentinel
流控
壓測工具
1、作用
2、熔斷
3、降級
配置啟動sentinel
注冊微服務到sentinel
測試流控
降級測試
流控模式詳解
Sentinel 流控效果詳解
hystrix 熔斷器
注冊中心 nacos | eurekaServer |zookeeper(dubbo)
配置中心 nacos | config Server
遠程服務調用 httpClient | RestTemplate | OpenFeign
負載均衡服務 ribbon | loadbalancer
網關 zuul | gateway
熔斷器 hystrix | sentinel
網關
1、統一項目入口 不需要關注每一個微服務的ip和端口,客戶端只需要網關的ip和端口
2、路由功能 轉發請求到達目標服務
網關的配置文件,通過路由鑒權(斷言匹配,路由地址匹配,路徑匹配),滿足規則,轉發請求到目標服務
- id: provider
# ? ? ? ? uri: http://localhost:8080/ #靜態路由uri: lb://provider ? #動態路由predicates: ? #斷言匹配- Path=/pro/**filters:- RewritePath=/pro/(?<segment>.*), /p/$\{segment}
# ? ? ? ? ? - StripPrefix=1 #刪除前綴路徑 刪除一層
# ? ? ? ? ? - PrefixPath=/p- AddRequestParameter=name,zhangsan
3、過濾無效請求,減少對目標服務器的壓力,可以同時起到安全性作用
4、負載均衡服務器 負載均衡算法(輪詢、隨機) nginx(反向代理和負載均衡服務器)
5、局部過濾器
修改請求路徑 StripPrefix=1 刪除一層路徑
- RewritePath=/pro/(?<segment>.*), /p/${segment} 重寫路徑
添加請求頭 AddRequestHeader
添加請求參數 AddRequestparameter=key,value
6、全局過濾器
package com.hl.filter;
?
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
?
/*
全局過濾器*/
@Component
public class GlobalGatewayFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {System.out.println("filter.........");//放行,繼續到達下一個過濾器return chain.filter(exchange);}//數字越小,執行越往前@Overridepublic int getOrder() {return 0;}
}
-
GlobalFilter 全局過濾器,不需要在配置文件中配置,系統初始化時加載,并作用在每個路由上。
-
Spring Cloud Gateway 核心的功能也是通過內置的全局過濾器來完成。
sentinel
限流、降級
資源是Sentinel
中的核心概念之一。我們說的資源,可以是任何東西,服務,服務里的方法,甚至是一段代碼。最常用的資源是我們代碼中的Java方法。Sentinel
提供了@SentinelResource
注解用于定義資源,并提供了AspectJ
的擴展用于自動定義資源、處理BlockException
等。
流控
-
QPS(每秒請求數量):當調用該api的QPS達到閾值的時候,進行限流
-
線程數:當調用該api的線程數達到閾值的時候,進行限流
壓測工具
壓力測試工具,jmeter
1、作用
-
為什么要使用熔斷和降級
在一個分布式系統里,一個服務依賴多個服務,可能存在某個服務調用失敗,比如超時、異常等,需要保證在一個依賴出問題的情況下,不會導致整體服務失敗。
2、熔斷
暫時中斷程序,稍后可繼續訪問。
3、降級
走降級方案,返回之前指定的失敗給用戶。(柔性失敗)
避免服務整體崩潰,系統宕機。讓當前方法失敗,返回失敗響應給調用者。
配置啟動sentinel
1、下載安裝包 https://github.com/alibaba/Sentinel/releases
2、啟動(win+r? ? cmd)
java -Dserver.port=8718 -jar 安裝包路徑/安裝包名稱
?java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar D:\IDEA2024\sentinel-dashboard-1.8.0.jar
默認用戶名:sentinel
密碼: sentinel
注冊微服務到sentinel
配置文件:
# Nacos幫助文檔: https://nacos.io/zh-cn/docs/concepts.html
# Nacos認證信息
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=nacos
spring.cloud.nacos.config.contextPath=/nacos
# 設置配置中心服務端地址
spring.cloud.nacos.config.server-addr=localhost:8848
# Nacos 配置中心的namespace。需要注意,如果使用 public 的 namcespace ,請不要填寫這個值,直接留空即可
# spring.cloud.nacos.config.namespace=
spring.config.import=nacos:product.yml?refresh=true
?
?
?
# Nacos幫助文檔: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=product
?
?
?
# Nacos認證信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服務發現與注冊配置,其中子屬性 server-addr 指定 Nacos 服務器主機和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注冊到 nacos 的指定 namespace,默認為 public
spring.cloud.nacos.discovery.namespace=public
?
?
# 應用服務 WEB 訪問端口
server.port=8088
# Sentinel 控制臺地址
spring.cloud.sentinel.transport.dashboard=localhost:8718
# 取消Sentinel控制臺懶加載
# 默認情況下 Sentinel 會在客戶端首次調用的時候進行初始化,開始向控制臺發送心跳包
# 配置 sentinel.eager=true 時,取消Sentinel控制臺懶加載功能
spring.cloud.sentinel.eager=true
# 如果有多套網絡,又無法正確獲取本機IP,則需要使用下面的參數設置當前機器可被外部訪問的IP地址,供admin控制臺使用
# spring.cloud.sentinel.transport.client-ip=
測試流控
@RestController
@RequestMapping("/demo")
public class DemoController {
?@Autowiredprivate DemoService demoService;
?@GetMapping("/bonjour/{name}")public String apiSayHelloLocal(@PathVariable String name) throws Exception {for(int i = 1;i<10;i++){String s1 = demoService.bonjour(name);System.out.println(s1);}return ff;}
@Service
public class DemoService {@SentinelResource(value = "DemoService#bonjour",defaultFallback = "bonjourFallback")public String bonjour(String name) throws Exception {return "Bonjour, " + name;}
?public String bonjourFallback(Throwable t) {if (BlockException.isBlockException(t)) {return "Blocked by Sentinel: " + t.getClass().getSimpleName();}return "Oops, failed: " + t.getClass().getCanonicalName();}
}
降級測試
/** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** ? ? https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
?
package com.hl.product.demos.sentinel;
?
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
?
/*** @author Eric Zhao*/
@RestController
@RequestMapping("/demo")
public class DemoController {
?@Autowiredprivate DemoService demoService;
?@GetMapping("/bonjour/{name}")public String apiSayHelloLocal(@PathVariable String name) throws Exception {boolean flag = true;String s0 = demoService.bonjour(name,flag);System.out.println(s0);
?flag = false;for(int i = 1;i<10;i++){String s1 = demoService.bonjour(name,flag);System.out.println(s1);}System.out.println("---------------");flag = true;String ff = demoService.bonjour(name,flag);System.out.println(ff);
?return ff;}
?
?
}
/** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** ? ? https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
?
package com.hl.product.demos.sentinel;
?
import org.springframework.stereotype.Service;
?
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
?
/*** @author Eric Zhao*/
@Service
public class DemoService {@SentinelResource(value = "DemoService#bonjour",defaultFallback = "bonjourFallback")public String bonjour(String name,Boolean flag) throws Exception {if(flag == false){throw new Exception("異常。。。。。。");}return "Bonjour, " + name;}
?public String bonjourFallback(Throwable t) {if (BlockException.isBlockException(t)) {return "Blocked by Sentinel: " + t.getClass().getSimpleName();}return "Oops, failed: " + t.getClass().getCanonicalName();}
}
/** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.hl.product.demos.sentinel;import java.io.PrintWriter;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;/*** @author Eric Zhao*/
@Configuration
public class SentinelWebConfig {@Beanpublic BlockExceptionHandler sentinelBlockExceptionHandler() {return (request, response, e) -> {// 429 Too Many Requestsresponse.setStatus(429);PrintWriter out = response.getWriter();out.print("Oops, blocked by Sentinel: " + e.getClass().getSimpleName());out.flush();out.close();};}
}
流控模式詳解
-
直接模式
-
概念:直接模式是最基本的流控模式。它是對當前資源(接口或方法)本身的請求進行流量控制。當請求到達被保護的資源時,Sentinel 會直接根據設置的規則(如 QPS 閾值、并發線程數閾值等)來判斷是否允許該請求通過。
-
示例:假設有一個用戶登錄接口
/login
,設置其 QPS(每秒查詢率)的直接流控閾值為 100。這意味著當每秒請求該/login
接口的次數超過 100 時,Sentinel 就會開始進行流量控制,可能會對超出的請求進行限流(如直接拒絕、排隊等待等處理方式)。 -
應用場景:適用于對單一資源的流量進行簡單直接的控制,比如限制某個核心 API 接口的訪問頻率,以防止該接口被過度調用而導致系統崩潰或性能下降。
-
-
關聯模式
-
概念:關聯模式是一種相對復雜的流控方式。它主要用于當兩個資源之間存在關聯關系時,通過對其中一個資源的流量控制來間接控制另一個資源的流量。當關聯的資源 A 達到流控閾值時,就會對目標資源 B 進行流量控制。
-
示例:考慮一個電商系統,有一個商品詳情接口
/product/detail
和一個購買接口/product/buy
。這兩個接口是關聯的,因為用戶在查看商品詳情后可能會進行購買。如果設置/product/detail
接口與/product/buy
接口為關聯模式,并且當/product/detail
的 QPS 達到某個閾值(如 50)時,開始對/product/buy
接口進行流量控制,比如限制/product/buy
接口的 QPS 為 30。這樣可以防止在商品詳情頁被大量訪問時,購買接口被過度請求而導致庫存系統等后端服務壓力過大。 -
應用場景:常用于存在先后調用關系或者關聯關系的資源之間,例如在一個業務流程中,前一個步驟的操作頻繁可能會導致后一個步驟的服務壓力過大,通過關聯模式可以提前控制后一個步驟的流量。
-
-
鏈路模式
-
概念:鏈路模式主要是從調用鏈路的角度進行流量控制。它會根據請求的調用鏈路來判斷是否進行流量控制。在微服務架構或者復雜的分布式系統中,一個請求可能會經過多個服務和方法的調用鏈路。Sentinel 鏈路模式可以針對特定的調用鏈路進行流控,而不是簡單地基于資源本身。
-
示例:假設有一個分布式系統,包括服務 A、服務 B 和服務 C。服務 A 調用服務 B,服務 B 又調用服務 C。在服務 B 中有一個方法
methodB
,在鏈路模式下,可以針對從服務 A - 服務 B - 服務 C 這個特定鏈路中對methodB
進行流量控制。如果在其他鏈路中也調用了methodB
,只要不是這個特定的鏈路,就不受這個鏈路模式下的流控規則限制。 -
應用場景:特別適用于微服務架構和復雜的分布式系統,能夠精確地對特定的調用鏈路進行流量管理,避免因為某個鏈路的流量過大而影響整個系統的穩定性,同時又不會影響其他鏈路對相同資源的正常調用。
-
Sentinel 流控效果詳解
Sentinel 流控效果主要有快速失敗、Warm Up 和排隊等待三種,它們的區別如下:
-
快速失敗(Fail Fast)
-
概念
-
當請求流量超過設定的閾值時,直接拒絕超出的請求。這種方式能夠快速地對超出流量進行限制,以保護后端服務。
-
-
示例
-
假設對一個 API 接口設置的 QPS(每秒查詢率)閾值為 100,當每秒請求數達到 101 時,第 101 個及之后的請求會立即被拒絕,并返回相應的限流錯誤信息,如 HTTP 狀態碼為 429(Too Many Requests)。
-
-
應用場景
-
適用于對系統資源消耗較大,且對請求的實時性要求不是特別高的接口。例如,一些批量數據處理接口,當流量過載時,直接拒絕新請求可以避免系統因過度負載而崩潰。同時,對于一些非關鍵業務的接口,快速失敗可以簡單有效地控制流量,確保核心業務的穩定運行。
-
-
-
Warm Up(預熱)
-
概念
-
預熱模式是一種漸進式的流量控制方式。它會根據設定的預熱時長和閾值,在系統啟動或者流量突然增加時,緩慢地增加允許通過的流量,直到達到設定的最大閾值。這樣可以避免系統在冷啟動或者流量突發時,因為瞬間的高負載而出現性能問題。
-
-
示例
-
假設設置一個接口的 QPS 閾值為 100,預熱時長為 5 分鐘。系統啟動時,開始允許通過的 QPS 可能只有 20,然后隨著時間逐漸增加,在 5 分鐘后達到 100。如果在預熱期間流量就超過了當前允許的閾值,超出部分會被拒絕。
-
-
應用場景
-
常用于系統冷啟動后需要逐步加載資源(如緩存預熱、數據庫連接池初始化等)的場景。同時,對于一些具有性能瓶頸,需要一定時間來適應高流量的服務,如數據庫密集型的接口或者計算資源消耗大的算法接口,Warm Up 模式可以讓系統有一個緩沖階段,平穩地過渡到高流量狀態。
-
-
-
排隊等待(Rate Limiter)
-
概念
-
當請求流量超過閾值時,不是直接拒絕請求,而是讓請求進行排隊等待。Sentinel 會按照請求的到達順序,將超出閾值部分的請求放入隊列中,當系統有能力處理新請求時,再從隊列中取出請求進行處理。這樣可以保證請求在一定程度上的公平性,避免大量請求被直接拒絕。
-
-
示例
-
設一個接口的 QPS 閾值為 100,隊列長度為 200。當每秒請求數達到 120 時,其中 20 個請求會被放入隊列中排隊等待。如果系統處理速度能夠跟上,這些排隊的請求會在后續得到處理。
-
-
應用場景
-
適用于對請求響應時間要求相對不那么嚴格,且希望盡可能處理所有請求的場景。比如,對于一些異步任務處理的接口,或者消息隊列消費者接口,排隊等待可以確保請求不會因為瞬間流量高峰而丟失,同時可以在一定程度上平滑流量,提高系統整體的資源利用率。
-
-
hystrix 熔斷器
熔斷和降級
熔斷(目標服務不讓訪問,暫時中斷)
降級(目標資源服務訪問時,走降級方法,備份方法,返回特定信息給調用者,避免無限制等待)
熔斷器 有三個狀態: 打開 半開 關閉