介紹
Spring Cloud Gateway是Spring Cloud官方推出的第二代網(wǎng)關(guān)框架编曼,取代Zuul網(wǎng)關(guān)揉忘。網(wǎng)關(guān)作為流量的矿筝,在微服務(wù)系統(tǒng)中有著非常作用兜材,網(wǎng)關(guān)常見(jiàn)的功能有路由轉(zhuǎn)發(fā)罕邀、權(quán)限校驗(yàn)乏苦、限流控制等作用株扛。
Sentinel是阿里開(kāi)源的項(xiàng)目,提供了流量控制汇荐、熔斷降級(jí)洞就、系統(tǒng)負(fù)載保護(hù)等多個(gè)維度來(lái)保障服務(wù)之間的穩(wěn)定性。(https://github.com/alibaba/Sentinel)
- 整體結(jié)構(gòu)圖如下掀淘,將原有的 Spring Cloud Gateway中集成Hystrix替換成Sentinel來(lái)進(jìn)行限流旬蟋、降級(jí)等功能, Hystrix和Sentinel的區(qū)別可以參考:[Hystrix和Sentinel對(duì)比][1]革娄,總結(jié)來(lái)說(shuō):Hystrix常用的線(xiàn)程池隔離會(huì)造成線(xiàn)程上下切換的overhead比較大倾贰;Hystrix使用的信號(hào)量隔離對(duì)某個(gè)資源調(diào)用的并發(fā)數(shù)進(jìn)行控制,效果不錯(cuò)拦惋,但是無(wú)法對(duì)慢調(diào)用進(jìn)行自動(dòng)降級(jí)匆浙;Sentinel通過(guò)并發(fā)線(xiàn)程數(shù)的流量控制提供信號(hào)量隔離的功能
Gateway網(wǎng)關(guān)接入
- 創(chuàng)建項(xiàng)目mas-openapi-geteway,遵從SpringBoot家族開(kāi)箱即用的慣例厕妖,在maven中加入如下配置:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
- gateWay內(nèi)部實(shí)際使用的是Reactor模式首尼,所有的請(qǐng)求都是異步非阻塞處理的,加入webFlex的配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
- gateWay的主要功能之一是轉(zhuǎn)發(fā)請(qǐng)求言秸,轉(zhuǎn)發(fā)規(guī)則的定義主要包含三個(gè)部分软能,其中Route和Predicate必須同時(shí)申明:
Route(路由) | 路由是網(wǎng)關(guān)的基本單元,由ID举畸、URI查排、一組Predicate、一組Filter組成抄沮,根據(jù)Predicate進(jìn)行匹配轉(zhuǎn)發(fā)跋核。 | |
Predicate(謂語(yǔ)岖瑰、斷言) | 路由轉(zhuǎn)發(fā)的判斷條件,目前SpringCloud Gateway支持多種方式了罪,常見(jiàn)如:Path锭环、Query、Method泊藕、Header等辅辩,寫(xiě)法必須遵循 key=vlue的形式 | |
Filter(過(guò)濾器) | 過(guò)濾器是路由轉(zhuǎn)發(fā)請(qǐng)求時(shí)所經(jīng)過(guò)的過(guò)濾邏輯,可用于修改請(qǐng)求娃圆、響應(yīng)內(nèi)容 |
- 規(guī)則可通過(guò)yml文件方式玫锋、代碼方式和動(dòng)態(tài)推送(通過(guò)配置中心Nacos推送)配置,這里網(wǎng)關(guān)的地址為localhost:9022
//通過(guò)配置文件配置
spring:
cloud:
gateway:
routes:
- id: gate_route
uri: http://localhost:9023
predicates:
## 當(dāng)請(qǐng)求的路徑為gate讼呢、rule開(kāi)頭的時(shí)撩鹿,轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上
- Path=/gate/**,/rule/**
### 請(qǐng)求路徑前加上/app
filters:
- PrefixPath=/app
- 轉(zhuǎn)發(fā)規(guī)則(predicates),轉(zhuǎn)發(fā)uri都設(shè)定為http://localhost:9023
規(guī)則 | 實(shí)例 | 說(shuō)明 |
---|---|---|
Path | - Path=/gate/,/rule/ | ## 當(dāng)請(qǐng)求的路徑為gate悦屏、rule開(kāi)頭的時(shí)节沦,轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上 |
Before | - Before=2017-01-20T17:42:47.789-07:00[America/Denver] | 在某個(gè)時(shí)間之前的請(qǐng)求才會(huì)被轉(zhuǎn)發(fā)到 http://localhost:9023服務(wù)器上 |
After | - After=2017-01-20T17:42:47.789-07:00[America/Denver] | 在某個(gè)時(shí)間之后的請(qǐng)求才會(huì)被轉(zhuǎn)發(fā) |
Between | - Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver] | 在某個(gè)時(shí)間段之間的才會(huì)被轉(zhuǎn)發(fā) |
Cookie | - Cookie=chocolate, ch.p | 名為chocolate的表單或者滿(mǎn)足正則ch.p的表單才會(huì)被匹配到進(jìn)行請(qǐng)求轉(zhuǎn)發(fā) |
Header | - Header=X-Request-Id, \d+ | 攜帶參數(shù)X-Request-Id或者滿(mǎn)足\d+的請(qǐng)求頭才會(huì)匹配 |
Host | - Host=www.hd123.com | 當(dāng)主機(jī)名為www.hd123.com的時(shí)候直接轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上 |
Method | - Method=GET | 只有GET方法才會(huì)匹配轉(zhuǎn)發(fā)請(qǐng)求,還可以限定POST础爬、PUT等請(qǐng)求方式 |
- 過(guò)濾器規(guī)則(Filter)
過(guò)濾規(guī)則 | 實(shí)例 | 說(shuō)明 |
---|---|---|
PrefixPath | - PrefixPath=/app | 在請(qǐng)求路徑前加上app |
RewritePath | - RewritePath=/test, /app/test | 訪(fǎng)問(wèn)localhost:9022/test,請(qǐng)求會(huì)轉(zhuǎn)發(fā)到localhost:8001/app/test |
SetPath | SetPath=/app/{path} | 通過(guò)模板設(shè)置路徑甫贯,轉(zhuǎn)發(fā)的規(guī)則時(shí)會(huì)在路徑前增加app,{path}表示原請(qǐng)求路徑 |
注:當(dāng)配置多個(gè)filter時(shí)看蚜,優(yōu)先定義的會(huì)被調(diào)用叫搁,剩余的filter將不會(huì)生效
- 通過(guò)代碼進(jìn)行配置,將路由規(guī)則設(shè)置為一個(gè)Bean即可:
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/get")
.uri("http://httpbin.org"))
.route("host_route", r -> r.host("*.myhost.org")
.uri("http://httpbin.org"))
.route("rewrite_route", r -> r.host("*.rewrite.org")
.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
.uri("http://httpbin.org"))
.route("hystrix_route", r -> r.host("*.hystrix.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
.uri("http://httpbin.org"))
.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
.uri("http://httpbin.org"))
.route("limit_route", r -> r
.host("*.limited.org").and().path("/anything/**")
.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
.uri("http://httpbin.org"))
.build();
}
- 使用nacos實(shí)現(xiàn)動(dòng)態(tài)路由供炎,以上兩種方式都是實(shí)現(xiàn)的靜態(tài)配置路徑渴逻,只能應(yīng)對(duì)部分場(chǎng)景,接下來(lái)配置nacos實(shí)現(xiàn)動(dòng)態(tài)配置以及配置的存儲(chǔ)音诫,由于gateWay并沒(méi)有適配nacos惨奕,需要自定義監(jiān)聽(tīng)器:
@Component
@Slf4j
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {
private String dataId = "gateway-router";
private String group = "DEFAULT_GROUP";
@Value("${spring.cloud.nacos.config.server-addr}")
private String serverAddr;
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher applicationEventPublisher;
private static final List<String> ROUTE_LIST = new ArrayList<>();
@PostConstruct
public void dynamicRouteByNacosListener() {
try {
ConfigService configService = NacosFactory.createConfigService(serverAddr);
configService.getConfig(dataId, group, 5000);
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
clearRoute();
try {
if (StringUtil.isNullOrEmpty(configInfo)) {//配置被刪除
return;
}
List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
addRoute(routeDefinition);
}
publish();
} catch (Exception e) {
log.error("receiveConfigInfo error" + e);
}
}
@Override
public Executor getExecutor() {
return null;
}
});
} catch (NacosException e) {
log.error("dynamicRouteByNacosListener error" + e);
}
}
private void clearRoute() {
for (String id : ROUTE_LIST) {
this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
}
ROUTE_LIST.clear();
}
private void addRoute(RouteDefinition definition) {
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
ROUTE_LIST.add(definition.getId());
} catch (Exception e) {
log.error("addRoute error" + e);
}
}
private void publish() {
this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
- 在nacos中增加一個(gè)規(guī)則:
[{
"filters": [],
"id": "baidu_route",
"order": 0,
"predicates": [{
"args": {
"pattern": "/baidu"
},
"name": "Path"
}],
"uri": "https://www.baidu.com"
}]
- 訪(fǎng)問(wèn)網(wǎng)關(guān)的路由規(guī)則,能看到剛剛加入的規(guī)則竭钝,訪(fǎng)問(wèn)http://localhost:9022/baidu時(shí)請(qǐng)求直接被轉(zhuǎn)發(fā)到百度的首頁(yè)了墓贿。
基礎(chǔ)配置:
- 現(xiàn)在的請(qǐng)求通過(guò)經(jīng)過(guò)gateWay網(wǎng)關(guān)時(shí),需要在網(wǎng)關(guān)統(tǒng)一配置跨域請(qǐng)求蜓氨,需求所有請(qǐng)求通過(guò)
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowed-origins: "*"
allowed-headers: "*"
allow-credentials: true
allowed-methods:
- GET
- POST
- DELETE
- PUT
- OPTION
- eureka、admin-client队伟、actuator健康檢查配置穴吹,為之后的功能提供支持,此部分比較簡(jiǎn)單嗜侮,不再贅述港令,加入以下maven依賴(lài)和配置
## maven依賴(lài)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
##配置項(xiàng)
spring:
application:
name: mas-cloud-gateway
boot:
admin:
client:
### 本地搭建的admin-server
url: http://localhost:8011
eureka:
client:
registerWithEureka: true
fetchRegistry: true
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://localhost:6887/eureka/
enabled: true
feign:
sentinel:
enabled: true
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: ALWAYS
- 若轉(zhuǎn)發(fā)的目標(biāo)地址為微服務(wù)中組件啥容,不為具體ip:port形式的,應(yīng)寫(xiě)成lb://mas-openapi-service形式顷霹,目標(biāo)地址會(huì)從注冊(cè)中心直接拉取
Sentinel
使用Sentinel作為gateWay的限流咪惠、降級(jí)、系統(tǒng)保護(hù)工具
基本概念
- 資源
資源是 Sentinel 的關(guān)鍵概念淋淀。它可以是 Java 應(yīng)用程序中的任何內(nèi)容遥昧,例如,由應(yīng)用程序提供的服務(wù)朵纷,或由應(yīng)用程序調(diào)用的其它應(yīng)用提供的服務(wù)炭臭,甚至可以是一段代碼。在接下來(lái)的文檔中都會(huì)用資源來(lái)描述代碼塊袍辞。只要通過(guò) Sentinel API 定義的代碼鞋仍,就是資源,能夠被 Sentinel 保護(hù)起來(lái)搅吁。大部分情況下威创,可以使用方法簽名,URL谎懦,甚至服務(wù)名稱(chēng)作為資源名來(lái)標(biāo)示資源肚豺。
- 規(guī)則
圍繞資源的實(shí)時(shí)狀態(tài)設(shè)定的規(guī)則莉御,可以包括流量控制規(guī)則并闲、熔斷降級(jí)規(guī)則以及系統(tǒng)保護(hù)規(guī)則。所有規(guī)則可以動(dòng)態(tài)實(shí)時(shí)調(diào)整晰绎。
依賴(lài)
<!--alibaba 流量衛(wèi)士-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>${sentinel.version}</version>
</dependency>
同時(shí)也將SpringCloud寞奸、GateWay整合呛谜,加入如下配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.7.1</version>
</dependency>
- 配置
- 由于sentinel的工作原理其實(shí)借助于全局的filter進(jìn)行請(qǐng)求攔截并計(jì)算出是否進(jìn)行限流、熔斷等操作的枪萄,增加SentinelGateWayFilter配置
@Bean//攔截請(qǐng)求
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
- sentinel 不僅支持通過(guò)硬代碼方式進(jìn)行資源的申明隐岛,還能通過(guò)注解方式進(jìn)行聲明,為了讓注解生效瓷翻,還需要配置切面類(lèi)SentinelResourceAspect
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
- sentinel攔截包括了視圖聚凹、靜態(tài)資源等,需要配置viewResolvers以及攔截之后的異常齐帚,我們也可以自定義拋出異常的提示
public SentinelConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean//自定義異常
@Order(Ordered.HIGHEST_PRECEDENCE)
public ExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new ExceptionHandler(viewResolvers, serverCodecConfigurer);
}
- 自定義異常提示:當(dāng)發(fā)生限流妒牙、熔斷異常時(shí),會(huì)返回定義的提示信息对妄。
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
ServerHttpResponse serverHttpResponse = exchange.getResponse();
serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
MasResponse<String> stringMasResponse = MasResponse.fail(
"限流了"
);
byte[] datas = JSON.toJSONString(stringMasResponse).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
return serverHttpResponse.writeWith(Mono.just(buffer));
}
不需要額外的配置湘今,sentinel就已經(jīng)可以正常工作了
網(wǎng)關(guān)流控實(shí)現(xiàn)原理
當(dāng)通過(guò) GatewayRuleManager 加載網(wǎng)關(guān)流控規(guī)則(GatewayFlowRule)時(shí),無(wú)論是否針對(duì)請(qǐng)求屬性進(jìn)行限流剪菱,Sentinel 底層都會(huì)將網(wǎng)關(guān)流控規(guī)則轉(zhuǎn)化為熱點(diǎn)參數(shù)規(guī)則(ParamFlowRule)摩瞎,存儲(chǔ)在GatewayRuleManager 中拴签,與正常的熱點(diǎn)參數(shù)規(guī)則相隔離。轉(zhuǎn)換時(shí) Sentinel 會(huì)根據(jù)請(qǐng)求屬性配置旗们,為網(wǎng)關(guān)流控規(guī)則設(shè)置參數(shù)索引(idx)蚓哩,并同步到生成的熱點(diǎn)參數(shù)規(guī)則中。
外部請(qǐng)求進(jìn)入 API Gateway 時(shí)會(huì)經(jīng)過(guò) Sentinel 實(shí)現(xiàn)的 filter上渴,其中會(huì)依次進(jìn)行 路由/API 分組匹配岸梨、請(qǐng)求屬性解析和參數(shù)組裝。Sentinel 會(huì)根據(jù)配置的網(wǎng)關(guān)流控規(guī)則來(lái)解析請(qǐng)求屬性驰贷,并依照參數(shù)索引順序組裝參數(shù)數(shù)組盛嘿,最終傳入SphU.entry(res, args) 中。Sentinel API Gateway Adapter Common 模塊向 Slot Chain 中添加了一個(gè) GatewayFlowSlot括袒,專(zhuān)門(mén)用來(lái)做網(wǎng)關(guān)規(guī)則的檢查次兆。GatewayFlowSlot 會(huì)從GatewayRuleManager中提取生成的熱點(diǎn)參數(shù)規(guī)則,根據(jù)傳入的參數(shù)依次進(jìn)行規(guī)則檢查锹锰。若某條規(guī)則不針對(duì)請(qǐng)求屬性芥炭,則會(huì)在參數(shù)最后一個(gè)位置置入預(yù)設(shè)的常量,達(dá)到普通流控的效果恃慧。
- 過(guò)濾順序:當(dāng)請(qǐng)求到來(lái)時(shí)园蝠,會(huì)依次調(diào)用下面的規(guī)則進(jìn)行校驗(yàn),而chain繼承自AbstractLinkedProcessorSlot<Object>痢士,將過(guò)濾的規(guī)則依次進(jìn)行直到不通過(guò)或者到最后一個(gè)規(guī)則彪薛。
##對(duì)應(yīng)代碼中定義的順序
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
// Prepare slot
chain.addLast(new NodeSelectorSlot());
chain.addLast(new ClusterBuilderSlot());
// Stat slot
chain.addLast(new LogSlot());
chain.addLast(new StatisticSlot());
// Rule checking slot
chain.addLast(new AuthoritySlot());
chain.addLast(new SystemSlot());
chain.addLast(new GatewayFlowSlot());
chain.addLast(new ParamFlowSlot());
chain.addLast(new FlowSlot());
chain.addLast(new DegradeSlot());
return chain;
- 通過(guò)一層層的插點(diǎn)slot,以達(dá)到統(tǒng)計(jì)怠蹂、限流善延、降級(jí)等功能,各個(gè)slot功能如下(按照?qǐng)?zhí)行順序):
NodeSelectorSlot 負(fù)責(zé)收集資源的路徑城侧,并將這些資源的調(diào)用路徑易遣,以樹(shù)狀結(jié)構(gòu)存儲(chǔ)起來(lái),用于根據(jù)調(diào)用路徑來(lái)限流降級(jí)嫌佑;
ClusterBuilderSlot 則用于存儲(chǔ)資源的統(tǒng)計(jì)信息以及調(diào)用者信息豆茫,例如該資源的 RT, QPS, thread count 等等,這些信息將用作為多維度限流屋摇,降級(jí)的依據(jù)揩魂;
LogSlot:用來(lái)記錄系統(tǒng)日志,當(dāng)前通過(guò)數(shù)量炮温、拒絕數(shù)量等
StatistcSlot 則用于記錄火脉,統(tǒng)計(jì)不同緯度的 runtime 信息;
AuthorizationSlot 則根據(jù)黑白名單,來(lái)做黑白名單控制忘分;
SystemSlot 則通過(guò)系統(tǒng)的狀態(tài),當(dāng)前的運(yùn)行狀態(tài)白修、CPU占用率等妒峦,來(lái)控制總的入口流量;
GatewayFlowSlot::網(wǎng)關(guān)限流規(guī)則
ParamFlowSlot:參數(shù)值限流規(guī)則定義
FlowSlot 則用于根據(jù)預(yù)設(shè)的限流規(guī)則兵睛,以及前面 slot 統(tǒng)計(jì)的狀態(tài)肯骇,來(lái)進(jìn)行限流
-
DegradeSlot 則通過(guò)統(tǒng)計(jì)信息,以及預(yù)設(shè)的規(guī)則祖很,來(lái)做熔斷降級(jí)笛丙;
注:Sentinel 1.6.0之后的版本引入了 Sentinel API Gateway Adapter Common 模塊,此模塊中包含網(wǎng)關(guān)限流的規(guī)則和自定義 API的實(shí)體和管理邏輯
- AuthorizationSlot:若未配置任何規(guī)則假颇,所有的請(qǐng)求都將成功通過(guò)胚鸯,規(guī)則字段解釋如下:
resource | 指定訪(fǎng)問(wèn)的資源名稱(chēng) |
limitApp | 限制來(lái)源,可通過(guò)英文逗號(hào)(,)指定多個(gè)來(lái)源限定笨鸡,通常為請(qǐng)求者IP或者消費(fèi)者名稱(chēng) |
strategy | 限制模式姜钳,白名單還是黑名單,默認(rèn)為白名單形耗,啟用后只有在白名單的來(lái)源才能通過(guò) |
### 只允許來(lái)源為127.0.0.1或者localhost的請(qǐng)求才能通過(guò)
AuthorityRule rule = new AuthorityRule();
rule.setResource("info");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("127.0.0.1,localhost");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));
###黑名單類(lèi)似哥桥,來(lái)源為指定的值時(shí)將之前拒絕請(qǐng)求
rule.setStrategy(RuleConstant.AUTHORITY_BLACK);
rule.setLimitApp("127.0.0.1,localhost");
注:當(dāng)其他的來(lái)源的應(yīng)用訪(fǎng)問(wèn)時(shí),該請(qǐng)求將無(wú)法通過(guò)激涤,如果未查找到該請(qǐng)求來(lái)源將直接通過(guò)拟糕。
- SystemSlot:系統(tǒng)限制規(guī)則,作為整個(gè)系統(tǒng)的衡量標(biāo)準(zhǔn)倦踢,不需要指定資源名稱(chēng)送滞,字段解釋如下:
|||
|-|-|-|
|highestSystemLoad|最大的Load,目前只針對(duì)Unix/Linux生效硼一,默認(rèn)為-1不生效|
|highestCpuUsage |最高CPU使用率累澡,范圍[0-1],高于該值時(shí)將拒絕所有請(qǐng)求般贼,做降級(jí)處理|
|qps |所有入口資源的QPS愧哟,默認(rèn)為-1不生效|
|avgRt |所有入口流量的平均響應(yīng)時(shí)間,ms為單位|
|maxThread|入口流量的最大并發(fā)數(shù)哼蛆,默認(rèn)為-1不生效|
##限制QPS為3蕊梧,平均返回時(shí)間為200ms
List<SystemRule> rules = new ArrayList<>();
SystemRule rule = new SystemRule();
// rule.setHighestSystemLoad(3.0);//系統(tǒng)負(fù)載 只針對(duì)Linux/unix
rule.setHighestCpuUsage(-1);//當(dāng)前系統(tǒng)的 CPU 使用率
rule.setAvgRt(200);//所有入口流量的平均響應(yīng)時(shí)間 ms
rule.setQps(3);
rule.setMaxThread(500);
rules.add(rule);
SystemRuleManager.loadRules(rules);
訪(fǎng)問(wèn)本地API服務(wù)器,請(qǐng)求第一次時(shí)可以得到正確的返回信息腮介,但是第二次訪(fǎng)問(wèn)時(shí)肥矢,由于上一次的請(qǐng)求耗時(shí)544/2ms,第二次請(qǐng)求將不會(huì)被通過(guò)
請(qǐng)求次數(shù):2
請(qǐng)求路徑:/api/query
訪(fǎng)問(wèn)成功了!甘改! 我是API服務(wù)器
{"echoCode":500,"echoMessage":"限流了","success":false}
544ms
更改系統(tǒng)規(guī)則中的QPS參數(shù)旅东,不限制平均響應(yīng)時(shí)間
List<SystemRule> rules = new ArrayList<>();
SystemRule rule = new SystemRule();
// rule.setHighestSystemLoad(3.0);//系統(tǒng)負(fù)載 只針對(duì)Linux/unix
rule.setHighestCpuUsage(-1);//當(dāng)前系統(tǒng)的 CPU 使用率
//rule.setAvgRt(200);//所有入口流量的平均響應(yīng)時(shí)間 ms
rule.setQps(3);
rule.setMaxThread(500);
rules.add(rule);
SystemRuleManager.loadRules(rules);
由于我們?cè)O(shè)置的QPS為3,那么當(dāng)我們請(qǐng)求10次時(shí)十艾,前三次可以成功請(qǐng)求抵代,但第四次開(kāi)始,請(qǐng)求將不會(huì)被處理忘嫉。
請(qǐng)求次數(shù):10
請(qǐng)求路徑:/api/query
訪(fǎng)問(wèn)成功了;珉埂! 我是API服務(wù)器
訪(fǎng)問(wèn)成功了G烀帷康吵! 我是API服務(wù)器
訪(fǎng)問(wèn)成功了!访递! 我是API服務(wù)器
訪(fǎng)問(wèn)成功了;耷丁! 我是API服務(wù)器
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
530ms
- GatewayFlowRule:網(wǎng)關(guān)限流規(guī)則力九,針對(duì) API Gateway 的場(chǎng)景定制的限流規(guī)則耍铜,可以針對(duì)不同 route 或自定義的 API 分組進(jìn)行限流,支持針對(duì)請(qǐng)求中的參數(shù)跌前、Header棕兼、來(lái)源 IP 等進(jìn)行定制化的限流。其中網(wǎng)關(guān)限流規(guī)則 GatewayFlowRule 的字段解釋如下:
resource | 資源名稱(chēng)抵乓,可以是網(wǎng)關(guān)中的 route 名稱(chēng)或者用戶(hù)自定義的API 分組名稱(chēng)伴挚。 | |
resourceMode | 規(guī)則是針對(duì) API Gateway 的route(RESOURCE_MODE_ROUTE_ID)還是用戶(hù)在 Sentinel 中定義的API 分組(RESOURCE_MODE_CUSTOM_API_NAME),默認(rèn)是route灾炭。 | |
grade: | 限流指標(biāo)維度茎芋,同限流規(guī)則的grade 字段。 | |
count: | 限流閾值 | |
intervalSec: | 統(tǒng)計(jì)時(shí)間窗口蜈出,單位是秒田弥,默認(rèn)是1 秒(目前僅對(duì)參數(shù)限流生效)。 | |
controlBehavior | 流量整形的控制效果铡原,同限流規(guī)則的 controlBehavior 字段偷厦,目前支持快速失敗和勻速排隊(duì)兩種模式,默認(rèn)是快速失敗燕刻。 | |
burst: | 應(yīng)對(duì)突發(fā)請(qǐng)求時(shí)額外允許的請(qǐng)求數(shù)目(目前僅對(duì)參數(shù)限流生效)只泼。 | |
maxQueueingTimeoutMs: | 勻速排隊(duì)模式下的最長(zhǎng)排隊(duì)時(shí)間,單位是毫秒卵洗,僅在勻速排隊(duì)模式下生效请唱。 | |
paramItem: | 參數(shù)限流配置。若不提供,則代表不針對(duì)參數(shù)進(jìn)行限流十绑,該網(wǎng)關(guān)規(guī)則將會(huì)被轉(zhuǎn)換成普通流控規(guī)則聚至;否則會(huì)轉(zhuǎn)換成熱點(diǎn)規(guī)則。其中的字段: | |
parseStrategy: | 從請(qǐng)求中提取參數(shù)的策略本橙,目前支持提取來(lái)源 | IP(PARAM_PARSE_STRATEGY_CLIENT_IP)晚岭、Host(PARAM_PARSE_STRATEGY_HOST)、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 參數(shù)(PARAM_PARSE_STRATEGY_URL_PARAM)四種模式勋功。 |
fieldName: | 若提取策略選擇 Header 模式或 URL 參數(shù)模式,則需要指定對(duì)應(yīng)的 header 名稱(chēng)或 URL 參數(shù)名稱(chēng)库说。 | |
pattern 和 matchStrategy: | 為后續(xù)參數(shù)匹配特性預(yù)留狂鞋,目前未實(shí)現(xiàn)。 |
- 根據(jù)GateWay提供的特性潜的,可設(shè)置兩個(gè)維度的限流和熔斷骚揍,指定API或者轉(zhuǎn)發(fā)路由,為了驗(yàn)證通過(guò)性啰挪,通過(guò)GateWay路由分別路由服務(wù)器CMS和PRODUCT服務(wù)器信不,并都將它們都注冊(cè)到Eureka中,配置規(guī)則如下:
## 將包含content請(qǐng)求路徑都轉(zhuǎn)發(fā)到CMS服務(wù)器上,其他求都轉(zhuǎn)發(fā)到product服務(wù)器上
- id: cms_route
uri: lb://mas-cms-service
predicates:
- Path=/{tenant}/service/content/**
- id: product_route
uri: lb://mas-product-service
predicates:
- Path=/**
路由轉(zhuǎn)發(fā)正常
- 自定義路由攔截規(guī)則:在上圖中我們定義并請(qǐng)求了三個(gè)路徑亡呵,其中前面兩個(gè)都是對(duì)CMS服務(wù)器的請(qǐng)求抽活,所以對(duì)于前面兩個(gè)請(qǐng)求對(duì)應(yīng)的路由資源為cms_route,第三個(gè)請(qǐng)求的路由資源為product_route锰什,限定第一個(gè)請(qǐng)求的路徑每秒QPS為3時(shí):
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("cms_route")
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)//路由規(guī)則
.setCount(3)
.setGrade(RuleConstant.FLOW_GRADE_QPS)//限制規(guī)則為QPS
.setIntervalSec(1)
);
GatewayRuleManager.loadRules(rules);
針對(duì)同一個(gè)ROUTE_ID的請(qǐng)求下硕,后面的將會(huì)被攔截,而不同ROUTE_ID的請(qǐng)求將不會(huì)受到影響汁胆。
- 自定義API攔截規(guī)則:若只攔截以{tenant}/service/content/place形式的接口時(shí)梭姓,需要根據(jù)正則表達(dá)式匹配該模式的請(qǐng)求地址并將其聲明為可攔截的API資源,再設(shè)定限流嫩码、熔斷規(guī)則
Set<ApiDefinition> definitions = new HashSet<>();
//顯式申明API
ApiDefinition placeApi = new ApiDefinition("place_flow")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/lh9999/service/content/place/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(placeApi);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
//設(shè)置規(guī)則
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("place_flow")
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(3)
.setGrade(RuleConstant.FLOW_GRADE_QPS)
.setIntervalSec(1)
);
GatewayRuleManager.loadRules(rules);
-
當(dāng)同時(shí)請(qǐng)求服務(wù)器時(shí)誉尖,只要路徑符合**/lh9999/service/content/place/****形式的請(qǐng)求都適用上面的規(guī)則,這里我們使用線(xiàn)程池模擬并發(fā)請(qǐng)求铸题,請(qǐng)求第四次時(shí)將直接失敗铡恕,限流效果還是很明顯的。
熱點(diǎn)參數(shù)規(guī)則(ParamFlowRule)類(lèi)似于流量控制規(guī)則(FlowRule)
屬性 | 說(shuō)明 | 默認(rèn)值 |
---|---|---|
resource | 資源名回挽,必填 | |
count 限流閾值没咙,必填 | ||
grade | 限流模式 | QPS 模式 |
paramIdx | 熱點(diǎn)參數(shù)的索引,必填千劈,對(duì)應(yīng) SphU.entry(xxx, args) 中的參數(shù)索引位置 | |
paramFlowItemList | 參數(shù)例外項(xiàng)祭刚,可以針對(duì)指定的參數(shù)值單獨(dú)設(shè)置限流閾值,不受前面 count 閾值的限制。僅支持基本類(lèi)型 | |
clusterMode | 是否是集群參數(shù)流控規(guī)則 | false |
clusterConfig | 集群流控相關(guān)配置 |
- 熱點(diǎn):經(jīng)常被訪(fǎng)問(wèn)的數(shù)據(jù)涡驮,作為公共資源暗甥,很多時(shí)候,我們希望統(tǒng)計(jì)訪(fǎng)問(wèn)某個(gè)熱點(diǎn)數(shù)據(jù)中訪(fǎng)問(wèn)頻次最高的幾項(xiàng)數(shù)據(jù)捉捅,并將其設(shè)置訪(fǎng)問(wèn)限制撤防,例如:
- 商品ID:針對(duì)某個(gè)熱點(diǎn)商品ID設(shè)置訪(fǎng)問(wèn)限制
- 用戶(hù)ID:針對(duì)某個(gè)熱點(diǎn)用戶(hù)ID設(shè)置訪(fǎng)問(wèn)限制
熱點(diǎn)參數(shù)限流會(huì)統(tǒng)計(jì)傳入?yún)?shù)中的熱點(diǎn)參數(shù),并根據(jù)配置的限流閾值與模式棒口,對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用進(jìn)行限流寄月。熱點(diǎn)參數(shù)限流可以看做是一種特殊的流量控制,僅對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用生效无牵。
- 可以通過(guò) SphU 類(lèi)里面幾個(gè) entry 重載方法來(lái)傳入對(duì)應(yīng)的參數(shù)以便 Sentinel 統(tǒng)計(jì),為對(duì)應(yīng)的資源配置熱點(diǎn)參數(shù)限流規(guī)則漾肮,并在 entry 的時(shí)候傳入相應(yīng)的參數(shù),即可使熱點(diǎn)參數(shù)限流生效茎毁。
public static Entry entry(String name, EntryType type, int count, Object... args) throws BlockException
public static Entry entry(Method method, EntryType type, int count, Object... args) throws BlockException
- 實(shí)例:若一個(gè)用戶(hù)短時(shí)間內(nèi)登錄次數(shù)過(guò)多克懊,將限制其登錄行為,防止惡意登錄
@PostMapping("{account}/checkLogin")
@SentinelResource("checkLogin")
public BaasResponse<String> checkLogin(@PathVariable(value = "account") String account, @RequestBody String password) {
try {
SphU.entry("checkLogin", EntryType.IN, 1, account);
...chekcLogin
} catch (BlockException e) {
BaasResponse<String> response = new BaasResponse<>();
response.setSuccess(false);
response.setEchoMessage(account + "被限制登錄");
return response;
}
return BaasResponse.success(account + "登錄成功");
}
制定限制規(guī)則七蜘,時(shí)間窗口限制為1s谭溉,在這1s中所有用戶(hù)最多允許登錄5次,通過(guò)ParamFlowRuleManager.loadRuls我們可以很容易制定限流規(guī)則
List<ParamFlowRule> rules = new ArrayList<>();
ParamFlowRule rule = new ParamFlowRule();
//閾值類(lèi)型:只支持QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//閾值
rule.setCount(5);
//資源名
rule.setResource("checkLogin");
rule.setParamIdx(0);//指配熱點(diǎn)參數(shù)的下標(biāo)
//統(tǒng)計(jì)窗口時(shí)間長(zhǎng)度
rule.setDurationInSec(1);
List<ParamFlowItem> items = new ArrayList<>();
ParamFlowItem item = new ParamFlowItem();
item.setClassType(String.class.getTypeName());
item.setCount(5);
items.add(item);
rule.setParamFlowItemList(items);
rules.add(rule);
ParamFlowRuleManager.loadRules(rules);
同一時(shí)刻tom橡卤、jack同時(shí)登錄扮念,tom登錄兩次,jack登錄了10次碧库,那么根據(jù)我們制定的規(guī)則扔亥,jack的行為將會(huì)被限制,其他用戶(hù)將不會(huì)受到影響
Sentinel圖形界面
如果上面的代碼配置不夠靈活可以通過(guò)Sentinel提供的用戶(hù)界面sentinel-dashboard實(shí)時(shí)進(jìn)行規(guī)則的制定谈为,可同時(shí)管理多個(gè)客戶(hù)端
- 客戶(hù)端配置:在配置文件中增加下列配置旅挤,dashboard就可以輕松管理客戶(hù)端了,還有一種方式是在啟動(dòng)時(shí)加入
spring:
cloud:
sentinel:
transport:
## VM
##-Djava.net.preferIPv4Stack=true -Dcsp.sentinel.dashboard.server=localhost:8080 -Dcsp.sentinel.api.port=8666 -Dproject.name=gateway -Dcsp.sentinel.app.type=1
dashboard: localhost:8880
port: 8880
下載完成可直接啟動(dòng)伞鲫,java -jar sentinel-dashboard.jar粘茄,登錄dashboard,就可以很清楚的看到眾多限流秕脓、熔斷等功能
同時(shí)對(duì)于剛剛建立的熱點(diǎn)參數(shù)柒瓣,還有其他特殊的限制功能,如圖所示吠架,可以限制某些特殊值不處理:
規(guī)則持久化:在dashboard中配置的規(guī)則都是存儲(chǔ)在內(nèi)存中的芙贫,dashboard 是通過(guò) transport 模塊來(lái)獲取每個(gè) Sentinel 客戶(hù)端中的規(guī)則的,獲取到的規(guī)則通過(guò) RuleRepository 接口保存在 Dashboard 的內(nèi)存中傍药,如果在 Dashboard 頁(yè)面中更改了某個(gè)規(guī)則磺平,也會(huì)調(diào)用 transport 模塊提供的接口將規(guī)則更新到客戶(hù)端中去魂仍。
試想這樣一種情況,客戶(hù)端連接上 Dashboard 之后拣挪,我們?cè)?Dashboard 上為客戶(hù)端配置好了規(guī)則擦酌,并推送給了客戶(hù)端。這時(shí)由于一些因素客戶(hù)端出現(xiàn)異常菠劝,服務(wù)不可用了赊舶,當(dāng)客戶(hù)端恢復(fù)正常再次連接上 Dashboard 后,這時(shí)所有的規(guī)則都丟失了赶诊,我們還需要重新配置一遍規(guī)則笼平,這肯定不是我們想要的。-
把原本保存在 內(nèi)存中的規(guī)則舔痪,持久化一份副本出去出吹。這樣下次客戶(hù)端重啟后,可以從持久化的副本中把數(shù)據(jù) load 進(jìn)內(nèi)存中辙喂,這樣就不會(huì)丟失規(guī)則了,如下圖所示:
Sentinel 為我們提供了兩個(gè)接口來(lái)實(shí)現(xiàn)規(guī)則的持久化鸠珠,他們分別是:ReadableDataSource 和 WritableDataSource巍耗。因?yàn)橥ǔ8鞣N持久化的數(shù)據(jù)源已經(jīng)提供了具體的將數(shù)據(jù)持久化的方法了,我們只需要把數(shù)據(jù)從持久化的數(shù)據(jù)源中獲取出來(lái)渐排,轉(zhuǎn)成我們需要的格式就可以了炬太。
下面我們來(lái)看一下 ReadableDataSource 接口的具體的定義:
public interface ReadableDataSource<S, T> {
// 從數(shù)據(jù)源中讀取原始的數(shù)據(jù)
S readSource() throws Exception;
// 將原始數(shù)據(jù)轉(zhuǎn)換成我們所需的格式
T loadConfig() throws Exception;
// 獲取該種數(shù)據(jù)源的SentinelProperty對(duì)象
SentinelProperty<T> getProperty();
}
接口很簡(jiǎn)單,最重要的就是這三個(gè)方法驯耻,另外 Sentinel 還為我們提供了一個(gè)抽象類(lèi):AbstractDataSource亲族,該抽象類(lèi)中實(shí)現(xiàn)了兩個(gè)方法,具體的數(shù)據(jù)源實(shí)現(xiàn)類(lèi)只需要實(shí)現(xiàn)一個(gè) readSource 方法即可可缚,具體的代碼如下:
public abstract class AbstractDataSource<S, T>
implements ReadableDataSource<S, T> {
// Converter接口負(fù)責(zé)轉(zhuǎn)換數(shù)據(jù)
protected final Converter<S, T> parser;
// SentinelProperty接口負(fù)責(zé)觸發(fā)PropertyListener
// 的configUpdate方法的回調(diào)
protected final SentinelProperty<T> property;
public AbstractDataSource(Converter<S, T> parser) {
if (parser == null) {
throw new IllegalArgumentException("parser can't be null");
}
this.parser = parser;
this.property = new DynamicSentinelProperty<T>();
}
@Override
public T loadConfig() throws Exception {
return loadConfig(readSource());
}
public T loadConfig(S conf) throws Exception {
return parser.convert(conf);
}
@Override
public SentinelProperty<T> getProperty() {
return property;
}
}
-
每個(gè)具體的 DataSource 實(shí)現(xiàn)類(lèi)需要做三件事:
- 實(shí)現(xiàn) readSource 方法將數(shù)據(jù)源中的原始數(shù)據(jù)轉(zhuǎn)換成我們可以處理的數(shù)據(jù)S
- 提供一個(gè) Converter 來(lái)將數(shù)據(jù)S轉(zhuǎn)換成最終的數(shù)據(jù)T
- 將最終的數(shù)據(jù)T更新到具體的 RuleManager 中去
DataSource 擴(kuò)展常見(jiàn)的實(shí)現(xiàn)方式有:
拉模式:客戶(hù)端主動(dòng)向某個(gè)規(guī)則管理中心定期輪詢(xún)拉取規(guī)則霎迫,這個(gè)規(guī)則中心可以是 RDBMS、文件帘靡,甚至是 VCS 等知给。這樣做的方式是簡(jiǎn)單,缺點(diǎn)是無(wú)法及時(shí)獲取變更描姚;
推模式:規(guī)則中心統(tǒng)一推送涩赢,客戶(hù)端通過(guò)注冊(cè)監(jiān)聽(tīng)器的方式時(shí)刻監(jiān)聽(tīng)變化,比如使用 Nacos轩勘、Zookeeper 等配置中心筒扒。這種方式有更好的實(shí)時(shí)性和一致性保證。
- Sentinel 目前支持以下數(shù)據(jù)源擴(kuò)展:能保證更新規(guī)則時(shí)绊寻,客戶(hù)端能得到通知即可
規(guī)則的更新可以通過(guò) Sentinel Dashboard 也可以通過(guò)各個(gè)配置中心自己的更新接口來(lái)操作
AbstractDataSource 中的 SentinelProperty 持有了一個(gè) PropertyListener 接口花墩,最終更新 RuleManager 中的規(guī)則是 PropertyListener 中實(shí)現(xiàn)的
Pull-based: 文件悬秉、Consul
Push-based: ZooKeeper, Redis, Nacos, Apollo, etcd
- 規(guī)則持久化:對(duì)這5種方式一一進(jìn)行了解,以持久化限流的規(guī)則為例观游。
- File
文件持久化有一個(gè)問(wèn)題就是文件不像其他的配置中心搂捧,數(shù)據(jù)發(fā)生變更后會(huì)發(fā)出通知,使用文件來(lái)持久化的話(huà)就需要我們自己定時(shí)去掃描文件懂缕,來(lái)確定文件是否發(fā)現(xiàn)了變更允跑。
文件數(shù)據(jù)源是通過(guò) FileRefreshableDataSource 類(lèi)來(lái)實(shí)現(xiàn)的,他是通過(guò)文件的最后更新時(shí)間來(lái)判斷規(guī)則是否發(fā)生變更的搪柑。
首先需要引入依賴(lài):
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
接入的方法如下:
private void init() throws Exception {
// 保存了限流規(guī)則的文件的地址
String flowRuleName = yourFlowRuleFileName();
Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
// 創(chuàng)建文件規(guī)則數(shù)據(jù)源
FileRefreshableDataSource<List<FlowRule>> flowRuleDataSource = new FileRefreshableDataSource<>(flowRuleName, parser);
// 將Property注冊(cè)到 RuleManager 中去
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
在系統(tǒng)啟動(dòng)的時(shí)候調(diào)用該數(shù)據(jù)源注冊(cè)的方法聋丝,否則不會(huì)生效的。具體的方式有很多工碾,可以借助 Spring 來(lái)初始化該方法弱睦,也可以自定義一個(gè)類(lèi)來(lái)實(shí)現(xiàn) Sentinel 中的 InitFunc 接口來(lái)完成初始化。
Sentinel 會(huì)在系統(tǒng)啟動(dòng)的時(shí)候通過(guò) spi 來(lái)掃描 InitFunc 的實(shí)現(xiàn)類(lèi)渊额,并執(zhí)行 InitFunc 的 init 方法Redis 數(shù)據(jù)源的實(shí)現(xiàn)類(lèi)是 RedisDataSource况木。
首先引入依賴(lài):
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-redis</artifactId>
</dependency>
接入方法如下:
private void init() throws Exception {
Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
RedisConnectionConfig config = RedisConnectionConfig.builder()
.withHost(redisHost)
.withPort(redisPort)
.build();
ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<>(config, ruleKey, channel, parser);
FlowRuleManager.register2Property(redisDataSource.getProperty());
}
- Nacos
Nacos 數(shù)據(jù)源的實(shí)現(xiàn)類(lèi)是 NacosDataSource。
首先引入依賴(lài):
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
接入方法如下:
private void init() throws Exception {
Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
ReadableDataSource<String, List<FlowRule>> nacosDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId, parser);
FlowRuleManager.register2Property(nacosDataSource.getProperty());
}
- Zk
Zk 數(shù)據(jù)源的實(shí)現(xiàn)類(lèi)是 ZookeeperDataSource旬迹。
首先引入依賴(lài):
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-zookeeper</artifactId>
</dependency>
接入方法如下:
private void init() throws Exception {
String remoteAddress = yourRemoteAddress();
String path = yourPath();
Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
ReadableDataSource<String, List<FlowRule>> zookeeperDataSource = new ZookeeperDataSource<>(remoteAddress, path, parser);
FlowRuleManager.register2Property(zookeeperDataSource.getProperty());
}
- Apollo
Apollo 數(shù)據(jù)源的實(shí)現(xiàn)類(lèi)是 ApolloDataSource火惊。
首先引入依賴(lài):
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-apollo</artifactId>
</dependency>
接入方法如下:
private void init() throws Exception {
Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
ReadableDataSource<String, List<FlowRule>> apolloDataSource = new ApolloDataSource<>(namespaceName, ruleKey, path, defaultRules);
FlowRuleManager.register2Property(apolloDataSource.getProperty());
}
可以看到5種持久化的方式基本上大同小異,主要還是對(duì)接每種配置中心奔垦,實(shí)現(xiàn)數(shù)據(jù)的轉(zhuǎn)換屹耐,并且監(jiān)聽(tīng)配置中心的數(shù)據(jù)變化,當(dāng)接收到數(shù)據(jù)變化后能夠及時(shí)的將最新的規(guī)則更新到 RuleManager 中去就可以了椿猎。其他規(guī)則類(lèi)似惶岭,可通過(guò)解析type進(jìn)行區(qū)分。
注:https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard