随着分布式系统变得越来越流行,服务之间的可靠性变得比以往任何时候都更加重要。Sentinel是强大的流控制组件,以“流”为切入点,涵盖多个领域,包括流控制,并发限制,电路中断和自适应系统保护,以确保微服务的可靠性。
一句话讲就是Spring Cloud Alibaba
用来替换之前的Hystrix
的技术。
1.下载地址
2.启动
java -jar sentinel-dashboard-1.7.2.jar
1.新建一个springboot项目
2.引入依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.csp
sentinel-datasource-nacos
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
3.启动类添加注解@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class AlibabaSentinelServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AlibabaSentinelServiceApplication.class, args);
}
}
4.application.properties
配置
server.port=8401
spring.application.name=cloudalibaba-sentinel-service
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.transport.port=8719
spring.cloud.sentinel.datasource.dsl.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.dsl.nacos.data-id=${spring.application.name}
spring.cloud.sentinel.datasource.dsl.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.dsl.nacos.data-type=json
spring.cloud.sentinel.datasource.dsl.nacos.rule-type.=flow
management.endpoints.web.exposure.include=*
5.提供测试接口
@RestController
public class FlowLimitController {
private static Logger log = LoggerFactory.getLogger(FlowLimitController.class);
@GetMapping("/testA")
public String testA(){
// try {
// TimeUnit.MILLISECONDS.sleep(800);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
return "testA-----";
}
@GetMapping("/testB")
public String testB(){
log.info(Thread.currentThread().getName() + "...testB ");
return "testB -----";
}
}
6.启动sentinel
和nacos
不懂nacos可以参考
default
(不区分来源)配置如下:
当你连续点击看到如下限流提示(1s请求数量超过配置阈值)
@GetMapping("/testA")
public String testA(){
//测试线程阈值
try {
TimeUnit.MILLISECONDS.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "testA-----";
}
配置如下
用postman模拟与/testA
关联的/testB
到达阈值
postman启动之后调用/testA
可以看到该接口被限流。
配置如下(访问流量忽然增大时,从阈值/冷加载因子 开始经过预热时长达到每秒可访问阈值,即从流量增大时刚开始可以支持每秒2次访问,经过3s可以支持每秒6次访问接口),默认冷加载因子(coldFactor)为3
可以点击访问/testA
接口测试,开始慢慢慢点击没问题,然后加快速度会出现限流,过一会之后,限流就没有了。
@GetMapping("/testB")
public String testB(){
log.info(Thread.currentThread().getName() + "...testB ");
return "testB -----";
}
官方文档
Sentienl熔断降级会在调用调用链路中某个资源出现不稳定时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误。
当资源降级后,在接下来的降级时间窗口之内,对该资源的调用自动熔断(默认行为是抛出DegradeException)
1.添加测试接口
@GetMapping("/testD")
public String testD(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("testD 测试RT");
return "testD -----";
}
为了让平均响应时间增大,在代码中加入sleep休眠。
2.利用JMater进行压力测试
1s 10个线程访问/testD
接口
3.配置如下
4.启动JMeter,自己调用/testD
,可以看到该接口已被熔断降级。
熔断分析:1.平均响应时间(1000ms)超出阈值(200ms) 2.在时间窗口内通过请求10*5=50>=5。满足两个条件,所以发生熔断降级
资源每秒请求量>=5&&每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的时间窗口之内,对这个方法的调用都会自动的返回(服务熔断)。异常比率阈值范围[0.0,1.0],代表0%-100%。
1.提供测试异常比例接口
@GetMapping("/testException")
public String testException(){
log.info("testException 异常比例");
int age = 10 /0 ;
return "testException -----";
}
2.利用JMater进行压力测试,除接口外其他配置同上面测试RT
3.sentinel配置
4.重启项目并启动JMeter进行测试,调用/testException
可以服务熔断提示信息
当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若时间窗口小于60s,则结束熔断状态后可能再进入熔断状态。
1.提供测试接口
@GetMapping("/testExceptionCount")
public String testExceptionCount(){
log.info("testExceptionCount 异常数");
int age = 10 /0 ;
return "testExceptionCount -----";
}
2.sentinel配置
3.重启项目进行测试
前面5次访问都报错,后面访问进入熔断后降级。
熔断后降级如下图
1.提供接口进行测试
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey", blockHandler = "dealTestHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2){
return "testHotKey -----";
}
public String dealTestHotKey(String p1, String p2, BlockException blockException){
return "dealTestHotKey---------";
}
2.sentinel配置
上面的配置第一个参数p1,当QPS超过1秒1次点击后马上被限流。
3.重启项目测试
1.sentinel配置如下
以上配置含义,当第一个参数p1的值为5时接口/testHotKey
的流量阈值为200
注意:
@SentinelResource处理的是控制台配置的违规情况,有blockHandler方法配置的兜底处理。
但是@SentinelResource不管代码中出现的运行时期异常(RuntimeException)
1.提供接口
/**
* (违反sentinel配置)手动配置兜底处理blockHandler
* @return
*/
@GetMapping(value = "/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public CommonResult byResource(){
return new CommonResult(200, "按资源名称限流测试OK");
}
public CommonResult handleException(BlockException blockException){
return new CommonResult<>(444, blockException.getClass().getCanonicalName()+"\t服务不可用" );
}
/**
* 默认处理
* @return
*/
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl(){
return new CommonResult(200, "by url限流测试OK");
}
3.重新启动测试
访问byResource
当每秒访问超过1次时,使用自定义的兜底处理。
访问/rateLimit/byUrl
当每秒访问超过1次时,使用默认处理。
以上配置存在问题:
1.提供接口
public class CustomerBlockHandler {
public static CommonResult handlerException(BlockException exception) {
return new CommonResult(444, "客户自定义,global handlerException---1");
}
public static CommonResult handlerException2(BlockException exception) {
return new CommonResult(444, "客户自定义,global handlerException---2");
}
}
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
public CommonResult customerBlockHandler(){
return new CommonResult(200, "客户自定义 限流测试OK");
}
1.创建三个springboot项目,分别为alibaba-consumer2、alibaba-provider3、alibaba-provider4(alibaba-provider3/alibaba-provider4一样)
由于东西过多这里不罗列,详细参考https://github.com/xiaoxiaoshou/springclouddemo
主要示例代码:
@RestController
@Slf4j
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public class CircleBreakerController {
private static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback") //没有配置
// @SentinelResource(value = "fallback",fallback = "handlerFallback") //配置了fallback的,fallback只负责业务异常
// @SentinelResource(value = "fallback",blockHandler = "blockHandler") // 配置了blockHandler,只负责sentinel控制台配置违规
@SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler") // 配置了blockHandler和fallback
// @SentinelResource(value = "fallback",fallback = "handlerFallback", blockHandler = "blockHandler", exceptionsToIgnore = {IllegalArgumentException.class}) // 忽略运行时IllegalArgumentException异常不进行自定义处理
public CommonResult fallback(@PathVariable("id") Long id){
CommonResult commonResult = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
if(id == 4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常");
}else if(commonResult.getData() == null){
throw new NullPointerException("NullPointerException,该ID没有记录,空指针异常");
}
return commonResult;
}
// 本例是fallback
public CommonResult handlerFallback(Long id, Throwable e){
Payment payment = new Payment(id, null);
return new CommonResult(444, "兜底异常handler,exception内容"+e.getMessage(), payment);
}
public CommonResult blockHandler(Long id, BlockException exception){
Payment payment = new Payment(id, null);
return new CommonResult<>(445, "blockHandler-sentinel 限流,无此流水号:blockException" + exception.getMessage(), payment);
}
}
fallback
对应方法(handlerFallback方法)处理代码运行时期异常blockHandler
对应方法(blockHandler)处理Sentinel中违规exceptionsToIgnore
忽略运行时期某个异常不进行自定义处理配置Sentinel(对资源fallback阈值为1)并启动项目测试,可以看到既可以处理调用过程中对应Sentinel违规也可以处理运行时期异常。
1.主要配置
#激活sentinel对feign的支持
feign:
sentinel:
enabled: true
2.绑定对应服务
@FeignClient(value = "nacos-payment-provider", fallback = PaymentFallbackService.class)
public interface PaymentService {
@GetMapping("/paymentSQL/{id}")
CommonResult paymentSQL(@PathVariable("id") Long id);
}
@Component
public class PaymentFallbackService implements PaymentService {
@Override
public CommonResult paymentSQL(Long id) {
return new CommonResult<>(444, "fallback");
}
}
3.提供访问接口
@Resource
private PaymentService paymentService;
@GetMapping("/consumer/paymentSQL/{id}")
public CommonResult paymentSQL(@PathVariable("id") Long id){
return paymentService.paymentSQL(id);
}
4.重启项目即可冲过consumer接口调用第三方接口
当你每次重启对应服务,你会发现在Sentinel中配置对应的规则就没有了,在生产环境中我们需要配置规则的持久化(持久化工具都可以,官方推荐Nacos)。
1.引入依赖
com.alibaba.csp
sentinel-datasource-nacos
2.配置文件配置
spring.cloud.sentinel.datasource.dsl.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.dsl.nacos.data-id=${spring.application.name}
spring.cloud.sentinel.datasource.dsl.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.dsl.nacos.data-type=json
spring.cloud.sentinel.datasource.dsl.nacos.rule-type=flow
3.在nacos中配置
[
{
"resource":"/rateLimit/byUrl",
"limitApp":"default",
"grade":1,
"count":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":false
}
]