版本说明
<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: 8401
spring:
application:
name: cloud-alibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 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 {
@Override
public 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:8848
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848
data-id: ${spring.application.name}
group-id: DEFAULT_GROUP
data-type: json
rule-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-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos地址
sentinel:
transport:
dashboard: localhost:8080
port: 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 {
@Override
public 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: 9528
spring:
application:
name: cloud-alibaba-sentinel-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos地址
gateway:
routes:
- id: pay_routh1
uri: lb://nacos-pay-provider
# uri: http://localhost:9001
predicates:
- 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();
}
@PostConstruct
public 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);
}
}