上一章中咱們經過Dashboard來爲Sentinel客戶端設置各類各樣的規則,可是這些規則默認是存放在內存中,極不穩定,沒法用於生成環境,因此須要將其持久化。html
DataSource
擴展常見的實現方式有:java
Sentinel 目前支持如下數據源擴展:git
生產環境中通常經常使用的就是推模式
。這裏咱們使用Nacos存儲規則。推送模式的正確作法應該是 配置中心控制檯/Sentinel 控制檯 → 配置中心 → Sentinel 數據源 → Sentinel。github
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
spring: cloud: sentinel: datasource: # 名稱隨意 javatrip: nacos: server-addr: 127.0.0.1:8848 dataId: ${spring.application.name}-rules groupId: SENTINEL_GROUP # 規則類型,取值見: # org.springframework.cloud.alibaba.sentinel.datasource.RuleType rule-type: flow
@RestController class test{ @RequestMapping("/test") public String test(){ return "Java旅途"; } }
0
表明根據併發數量來限流,1
表明根據QPS來進行流量控制要想實如今sentinel-dashboard中修改規則並同步到nacos,咱們就須要修改sentinel服務。首先咱們去官網下載Sentinel。web
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <scope>test</scope> </dependency>
將
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <!--<scope>test</scope>--> </dependency>
sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos
目錄,將整個目錄拷貝到 sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/
。com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2
,將默認動態規則修改成nacos動態規則。@Autowired @Qualifier("flowRuleDefaultProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleDefaultPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
修改成:spring
@Autowired @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html
將如下內容註釋去掉json
<!--<li ui-sref-active="active" ng-if="entry.appType==0">--> <!--<a ui-sref="dashboard.flow({app: entry.app})">--> <!--<i class="glyphicon glyphicon-filter"></i> 流控規則 V1</a>--> <!--</li>-->
從新編譯打包,運行打包後的sentinel-dashboard.jar。併發
測試,咱們刪除nacos中的流量規則配置app
注意:以上只是演示了流控規則的持久化,sentinel還支持其餘規則,若是想實現哪一種規則均可以採用相同的方式實現!
限流:就是請求多了,對請求進行定製的快速響應處理,應用在服務提供者自己。
從 1.6.0 版本開始,Sentinel 提供了 Spring Cloud Gateway 的適配模塊,能夠提供兩種資源維度的限流:
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId> <version>x.y.z</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> </dependency>
SentinelGatewayFilter
實例以及 SentinelGatewayBlockExceptionHandler
實例。@Configuration public class GatewayConfiguration { private final List<ViewResolver> viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) { this.viewResolvers=viewResolversProvider.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { // Register the block exception handler for Spring Cloud Gateway. return new MySentinelGatewayBlockExceptionHandler(viewResolvers,serverCodecConfigurer); } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public GlobalFilter sentinelGatewayFilter() { return new SentinelGatewayFilter(); } }
public class MySentinelGatewayBlockExceptionHandler extends SentinelGatewayBlockExceptionHandler { private List<ViewResolver> viewResolvers; private List<HttpMessageWriter<?>> messageWriters; public MySentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) { super(viewResolvers,serverCodecConfigurer); this.viewResolvers = viewResolvers; this.messageWriters = serverCodecConfigurer.getWriters(); } @Override public Mono<Void> handle(ServerWebExchange serverWebExchange, Throwable throwable) { if(serverWebExchange.getResponse().isCommitted()){ return Mono.error(throwable); } if(!BlockException.isBlockException(throwable)){ return Mono.error(throwable); } return handleBlockedRequest(serverWebExchange, throwable).flatMap(response -> writeResponse(response, serverWebExchange)); } private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) { return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); } private final Supplier<ServerResponse.Context> contextSupplier = () -> new ServerResponse.Context() { @Override public List<HttpMessageWriter<?>> messageWriters() { return MySentinelGatewayBlockExceptionHandler.this.messageWriters; } @Override public List<ViewResolver> viewResolvers() { return MySentinelGatewayBlockExceptionHandler.this.viewResolvers; } }; private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) { ServerHttpResponse resp = exchange.getResponse(); resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); String json = "{\"code\": -1, \"data\": null, \"msg\": \"訪問量過大,請稍後再試\"}"; DataBuffer buffer = resp.bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8)); return resp.writeWith(Mono.just(buffer)); } }
server: port: 7003 spring: application: name: alibaba-gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: enabled: true discovery: locator: enabled: true # 開啓從註冊中心動態建立路由的功能,利用微服務名稱進行路由 routes: - id: sentinel-nacos # 路由id,建議配合服務名 uri: lb://sentinel-nacos #匹配路由名 predicates: - Path=/sentinel/** # 斷言,路徑相匹配的進行路由 filters: - StripPrefix=1
-Dcsp.sentinel.app.type=1 -Dcsp.sentinel.dashboard.server=localhost:8081 -Dproject.name=alibaba-gateway
降級:就是服務崩潰了,因此降級邏輯應該應用在消費者(調用者)那裏,加在服務提供者自己是毫無心義的,由於服務已經斷開了。
咱們根據實際需求在sentinel-dashboard中配置降級規則,而後編寫代碼。
@RequestMapping("/test") public String test(){ return "Java旅途"; }
@FeignClient(name = "nacos-sentinel",fallback = RmoteTestFallback.class) interface RemoteTest{ @RequestMapping("/test") public String test(); }
爲了簡寫fallback,咱們更傾向於用fallbackFactory = RmoteTestFallbackFactory.class
@FeignClient(name = "nacos-sentinel",fallbackFactory = RmoteTestFallbackFactory.class) interface RemoteTest{ @RequestMapping("/test") public String test(); }
@Component class RmoteTestFallback implements RemoteTest{ @Override public String test() { return null; } }
@Component class RmoteTestFallbackFactory implements FallbackFactory<RemoteTest> { @Override public RemoteTest create(Throwable throwable) { return null; } }