版本说明
<spring.boot.version>3.2.0</spring.boot.version>
<spring.cloud.version>2023.0.0</spring.cloud.version>
<spring.cloud.alibaba.version>2023.0.1.2</spring.cloud.alibaba.version>
是什么
能干嘛
面试题
服务雪崩
安装使用
后台 8719,前端 8080,用户名密码 sentinel
本次版本 1.8.6
服务加入监控
启动 nacos, sentinel
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
server:port: 8401spring:application:name: cloud-alibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848sentinel:transport:dashboard: localhost:8080port: 8719 # 默认端口
@Slf4j
@RestController
public class FlowLimitController {@GetMapping("/testA")public String testA() {return "into ... A ...";}@GetMapping("/testB")public String testB() {return "into ... B ...";}}
GET http://localhost:8401/testA
流控规则
流控模式
直接
每秒超过2qps
关联
压测 testB,访问 testA
链路
厚此薄彼
添加一个service 资源,cd来调用同一个 service
@Slf4j
@Service
public class FlowLimitService {@SentinelResource("common")public void common() {log.info("into ... common ...");}
}// controller
@GetMapping("/testC")
public String testC() {flowLimitService.common();return "into ... C ...";
}@GetMapping("/testD")
public String testD() {flowLimitService.common();return "into ... D ...";
}
新增配置
spring.cloud.sentinel:web-context-unify: false # controller 对 service 调用默认不是一个根链路
common上添加链路流控
c 超过qps会异常,d正常
流控效果
快速失败
warm up
排队等待
流控效果v2-并发线程数(对比 qps)
流控效果默认直快速失败,可能存在不准确?
熔断规则
慢调用比例
异常比例
异常数
@SentinelResource
rest 地址限流
@GetMapping("/")
public String rateLimitByUrl() {return "限流测试未使用注解";
}
返回默认信息
按SentinelResource+自定义限流返回
@GetMapping("/rateLimitByResource")
@SentinelResource(value = "ByResource", blockHandler = "byResourceHandler")
public String rateLimitByResource() {return "限流测试使用注解, 返回指定的字符串";
}public String byResourceHandler(BlockException blockException) {return "服务不可用, 这是自定义返回的字符串";
}
额外添加服务降级
@GetMapping("/rateLimitByFallback/{i}")@SentinelResource(value = "rateLimitByFallback", blockHandler = "byBlockHandler", fallback = "byFallback")public String rateLimitByFallback(@PathVariable("i") Integer i) {if (i == 0) {throw new RuntimeException("i == 0 异常");}return "使用注解并使用, Fallback";}public String byBlockHandler(@PathVariable("i") Integer i, BlockException blockException) {log.error("配置了自定义限流, {}", blockException.getMessage());return "服务不可用, 这是自定义返回的字符串";}public String byFallback(@PathVariable("i") Integer i, Throwable throwable) {log.error("程序逻辑异常, {}", throwable.getMessage());return "逻辑异常, 这是自定义返回的字符串";}
热点规则
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey", blockHandler = "testHotKeyBlockHandler")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,@RequestParam(value = "p2", required = false) String p2) {return "testHotKey ...";
}public String testHotKeyBlockHandler(@RequestParam(value = "p1", required = false) String p1,@RequestParam(value = "p2", required = false) String p2, BlockException blockException) {return "testHotKey blockException ...";
}
对 p1 进行限流
参数例外项
授权规则
@Slf4j
@RestController
public class EmpowerController {@GetMapping("empower")public String requestSentinel() {log.info("授权规则");return "授权规则";}
}public class CustomRequestOriginParser implements RequestOriginParser {@Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {return httpServletRequest.getParameter("serverName");}
}
持久化规则
持久化到 nacos
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
spring:cloud:nacos:discovery:server-addr: localhost:8848sentinel:datasource:ds1:nacos:server-addr: localhost:8848data-id: ${spring.application.name}group-id: DEFAULT_GROUPdata-type: jsonrule-type: flow # com.alibaba.cloud.sentinel.datasource.RuleType (flow代表流控)
rule-type?
针对每一个rule-type单独配置 数据源
配置
nacos中添加 json 格式配置
测试重启,规则仍存在
整合OpenFeign实现 fallback 统一服务降级
blockHandler 处理 sentinel 流控问题,fallback 处理方法内抛出的异常
本例中,把 fallback 交给feign处理,再公共api-common中配置
服务提供者
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
server:port: 9001
spring:application:name: nacos-pay-providercloud:nacos:discovery:server-addr: localhost:8848 # nacos地址sentinel:transport:dashboard: localhost:8080port: 8719 # 默认端口8719
// openfeign和sentinel 进行流量降级和流量监控的case
@GetMapping("/pay/nacos/get/{orderNo}")
@SentinelResource(value = "getPayByOrderNo", blockHandler = "handlerBlockHandler")
public Result<PayDTO> getPayByOrderNo(@PathVariable("orderNo") String orderNo) {// 模拟查询PayDTO payDTO = new PayDTO(1024, orderNo, "pay" + IdUtil.simpleUUID(), 1, BigDecimal.valueOf(9.9));return Result.success(payDTO);
}public Result<PayDTO> handlerBlockHandler(String orderNo, BlockException e) {return Result.fail("服务提供者" + e.getMessage());
}
common
@FeignClient(value = "nacos-pay-provider", fallback = PayFeignSentinelFallback.class)
public interface PayFeignSentinelApi {@GetMapping("/pay/nacos/get/{orderNo}")Result<PayDTO> getPayByOrderNo(@PathVariable("orderNo") String orderNo);
}@Component
public class PayFeignSentinelFallback implements PayFeignSentinelApi {@Overridepublic Result<PayDTO> getPayByOrderNo(String orderNo) {return Result.fail("服务不可达");}
}
消费者
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
feign:sentinel:enabled: true
@GetMapping("/consume/pay/nacos/get/{orderNo}")
Result<PayDTO> getPayByOrderNo(@PathVariable("orderNo") String orderNo){return payFeignSentinelApi.getPayByOrderNo(orderNo);
}
sentinel配置&测试流控
单独测试服务端下流流控
localhost:9002/pay/nacos/get/1
测试 feign 异常
GET http://localhost:9000/consume/pay/nacos/get/1
让服务端宕机
整合 Gateway 实现服务限流
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-transport-simple-http</artifactId><version>1.8.6</version>
</dependency>
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-spring-cloud-gateway-adapter</artifactId><version>1.8.6</version>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- loadbalancer -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version><scope>compile</scope>
</dependency>
server:port: 9528spring:application:name: cloud-alibaba-sentinel-gatewaycloud:nacos:discovery:server-addr: localhost:8848 # nacos地址gateway:routes:- id: pay_routh1uri: lb://nacos-pay-provider# uri: http://localhost:9001predicates:- Path=/pay/**
@Configuration
public class GatewayConfiguration {private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {this.viewResolvers = viewResolvers;this.serverCodecConfigurer = serverCodecConfigurer;}@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);}@Bean@Order(-1)public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();}@PostConstructpublic void doInit() {initBlockHandler();}private void initBlockHandler() {Set<GatewayFlowRule> rules = new HashSet<>();rules.add(new GatewayFlowRule("pay_routh1").setCount(2).setIntervalSec(1));GatewayRuleManager.loadRules(rules);BlockRequestHandler handler = (serverWebExchange, throwable) -> {HashMap<String, String> map = new HashMap<>();map.put("ErrorCode", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());map.put("ErrorMessage", "请求过于频繁, 触发了sentinel限流 ... ");return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(map));};GatewayCallbackManager.setBlockHandler(handler);}
}
http://localhost:9528/pay/nacos/get/1