1.sentinel简介
Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
2.sentinel的安装和运行
1.下载sentinel.jar包
2.启动并运行
使用java -jar命令运行jar包,启动:
sentinel资源保护规则
sentinel支持多种保护规则:流量控制规则,熔断降级规则,系统保护规则,来源访问控制规则,热点参数规则。
sentinel流量控制统计有两种类型:
- 并发线程数:指当前请求的上下文线程数量,如果超出阈值,新的请求就会被拒绝
- QPS:表示每秒查询的数量,表示一台服务器每秒能相应的次数。
QPS流控行为
- 直接拒绝
- Warm Up ,冷启动
- 匀速排队
直接拒绝
@GetMapping("/testA")
public String testA() {
System.out.println(Thread.currentThread().getName()+"\t线程"+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
return "----A";
}
服务器请求testA,配置流控规则,阈值是1秒,当一秒查询数量超过每秒一次以后就会报错
Warm Up
它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的n/3(冷加载因子默认为3),然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。
刚启动服务时,最开始的阈值时10/3=3,然后再5秒内请求慢慢达到最大阈值10
匀速排队
匀速排队的方式会严格控制请求通过的时间间隔,让请求以匀速的速度通过。
一秒中只允许一个请求通过,超时时间设置为20秒
这里使用postman做测试
这里设置有10个线程,每500毫秒通过一次,运行查看控制台的输出。
可以看到每秒只允许通过一个进程。
QPS的流控模式
- 直接
- 关联
- 链路
直接:当每秒超过一个进程,就会显示错误信息
关联:服务间的互相关联,当关联资源被请求量过高,则主资源被限制访问。
使用postman做测试,每隔300毫秒发送一次请求,/testA就会拒绝访问,等过了20秒以后才可以访问。
sentinel服务降级常见方案
- 平均相应时间:比如1s内持续进入5个请求,对应时刻的平均响应时间均超过阈值,那么接下来在一个时间窗口期内,对这个方法的访问都会自动返回。
- 异常比例:当某个方法每秒调用所获得的异常总数的比例超过设定的时间阈值,该资源会自动进入降级状态。
- 异常数量:和异常比例相似,当某个方法在指定时间窗口内获得的异常数量超过阈值时会触发熔断。
平均响应时间
设置1秒内发送10个请求,我们希望200ms完成此次请求,如果超过200ms没有完成处理完,在未来的1s的时间窗口,断路器打开,微服务不可用。
@GetMapping("/testB")
public String testB() throws InterruptedException {
//System.out.println(Thread.currentThread().getName()+"\t线程"+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
TimeUnit.SECONDS.sleep(1);
return "----B";
}
异常比例
当异常的比例超过20%,微服务就不可用。
异常数量
在请求窗口期内,如果异常数超过5个,服务就会报错
@GetMapping("/testB")
public String testB() throws InterruptedException {
//System.out.println(Thread.currentThread().getName()+"\t线程"+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
//TimeUnit.SECONDS.sleep(1);
int a=10/0;
return "----B";
}
sentinel服务熔断
导入依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
application.yml配置
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719
server-url:
nacos-user-service: http://nacos-payment-provider
#激活Sentinel对Feign的依赖
feign:
sentinel:
enabled: true
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false)String p1,
@RequestParam(value = "p2",required = false)String p2){
return "-----testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception){
return "-----deal_testHotKey";
}
这里先创建一个微服务9003模拟数据库存储信息,当访问id不存在的时候(没有配置fallback和blockHandler时),页面报出异常。
blockHandler:负责控制台配置违规
fallback:负责业务异常
9003服务
@Value("${server.port}")
private String serverPort;
public static HashMap hashMap=new HashMap<>();
static {
hashMap.put(1L,new Payment(1L,"aaa"));
hashMap.put(2L,new Payment(2L,"bbb"));
hashMap.put(3L,new Payment(3L,"ccc"));
}
@GetMapping("/paymentSQL/{id}")
public CommonResult paymentSQL(@PathVariable("id")Long id){
Payment payment=hashMap.get(id);
CommonResult result=new CommonResult<>(200,"from mysql,serverPort: "+serverPort,payment);
return result;
}
84服务
@Value("${server-url.nacos-user-service}")
private String serverURL;
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback1")//没有配置
//@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责控制台配置违规
//@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
//exceptionsToIgnore = {IllegalArgumentException.class})
public CommonResult fallback(@PathVariable Long id) {
CommonResult result=restTemplate.getForObject(serverURL+"/paymentSQL/"+id,CommonResult.class,id);
if(id==4){
throw new IllegalArgumentException("非法参数异常");
}else if(result.getData()==null){
throw new NullPointerException("该id没有记录,空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
Payment payment=new Payment(id,"null");
return new CommonResult(444,"兜底方法handlerFallback,exception内容"+e.getMessage(),payment);
}
public CommonResult blockHandler(@PathVariable Long id, BlockException e){
Payment payment=new Payment(id,"null");
return new CommonResult(445,"限流blockHandler异常,exception内容"+e.getMessage(),payment);
}
在配置fallback参数后
@SentinelResource(value = "fallback",fallback = "handlerFallback")
只配置blockHandler参数
@SentinelResource(value = "fallback",blockHandler = "blockHandler")
没有处理blockHandler方法,因为blockHandler不负责业务异常,当sentinel控制台配置服务出现异常才会调用这个方法。
我们配置一个降级规则
当在浏览器上输入请求,第一次会有错误,等异常数超过2个,就会调用异常方法。
exceptionsToIgnore参数,忽略此异常
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
exceptionsToIgnore = {IllegalArgumentException.class})
public CommonResult fallback(@PathVariable Long id) {
CommonResult result=restTemplate.getForObject(serverURL+"/paymentSQL/"+id,CommonResult.class,id);
if(id==4){
throw new IllegalArgumentException("非法参数异常");
}else if(result.getData()==null){
throw new NullPointerException("该id没有记录,空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
Payment payment=new Payment(id,"null");
return new CommonResult(444,"兜底方法handlerFallback,exception内容"+e.getMessage(),payment);
}
当配置此参数后,当出现IllegalArgumentException异常后,不会调用handlerFallback方法。
使用OpenFeign调用远程服务
在接口上配置value参数指提供微服务名称,这里值9003微服务名称,fallback参数表示此微服务出错后调用的方法。
// OpenFeign
@Resource
private PaymentService paymentService;
@GetMapping("/consumer/paymentSQL/{id}")
public CommonResult paymentSQL(@PathVariable("id")Long id){
return paymentService.paymentSQL(id);
}
service方法
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {
@GetMapping("/paymentSQL/{id}")
public CommonResult paymentSQL(@PathVariable("id")Long id);
}
@Component
public class PaymentFallbackService implements PaymentService {
@Override
public CommonResult paymentSQL(Long id) {
return new CommonResult<>(444,"服务降级返回--payment service",new Payment(id,"null"));
}
}
此时如果关闭9003服务,再发送请求就会调用PaymentFallbackService里面的 paymentSQL方法。
总结
之前我以为服务降级和熔断只能在消费微服务端,或者在提供微服务端,但其实消费端与服务端是相互的,就比如使用openfeign调用远程微服务9003,此时84端是消费端,9003是提供方,但是当其他微服务请求84微服务的时候,这时候84微服务就是提供方,所有我们在84端配置的服务降级和服务熔断,在9003微服务中的方法也能够实现服务降级和熔断。
在9003服务端controller里面的 paymentSQL添加 @SentinelResource(value = "9003payment")注解,在浏览器上发送http://localhost:84/consumer/paymentSQL/1请求,可以看到sentinel控制台有此服务,表示当前方法也能够配置降级和熔断。
以下内容均是自己学习总结,如有不完善请指正,谢谢。
@GetMapping("/paymentSQL/{id}")
@SentinelResource(value = "9003payment")
public CommonResult paymentSQL(@PathVariable("id")Long id){
Payment payment=hashMap.get(id);
CommonResult result=new CommonResult<>(200,"from mysql,serverPort: "+serverPort,payment);
return result;
}