服务降级学习
降级概述
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。
服务降级和熔断的区别
服务的降级
假如有3个并行的服务,A,B,C,在调用的时候,A服务总是会出现慢调用的情况,这时候我们会利用降级来停掉A服务,这就叫降级
服务的熔断
假如有3个串行的服务,A,B,C,B服务在调用C服务时,C服务会出现慢调用,这叫熔断
代码演示熔断
修改ConumserController 类中的doRestEcho01方法,假如没有创建即可,基于此方法演示慢调用过程下的限流,代码如下:
//AtomicLong 类支持线程安全的自增自减操作
private AtomicLong atomicLong=new AtomicLong(1);
@Value("${spring.application.name}")
private String appName;
@GetMapping("/consumer/doRestEcho1")
public String doRestEcho1() throws InterruptedException {
//获取自增对象的值,然后再加1
long num=atomicLong.getAndIncrement();
if(num%2==0){//模拟50%的慢调用比例
Thread.sleep(200);
}
sentinel降级入门
1.服务启动后,选择要降级的链路–启动所有关联的链路
这里表示熔断策略为慢调用比例,表示链路请求数超过3时,假如平均响应时间假如超过200毫秒的有50%,则对请求进行熔断,熔断时长为10秒钟,10秒以后恢复正常。
2.访问对指定链路进行刷新,多次访问测试,假如出现了降级熔断,会出现如下结果:
3.我们也可以进行断点调试,在DefaultBlockExceptionHandler中的handle方法内部加断点,分析异常类型,假如异常类型为DegradeException则为降级熔断。
sentinel异常处理
系统提供了默认的异常处理机制,假如默认处理机制不满足我们需求,我们可以自己进行定义。定义方式上可以直接或间接实现BlockExceptionHandler接口,并将对象交给spring管理。
/**
* 默认的异常处理规则不能满足我们的需求,所以自定义异常
*/
@Component
public class ServiceBlockException implements BlockExceptionHandler {
/**
* 一旦服务被降级或熔断,sentinel系统底提供的拦截器会调用此方法对异常进行处理
*自定义
*/
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
//方案1.抛出,异常继续抛出
//throw e;
//方案2:对异常进行处理(返回客户端能看懂的信息
//思考:向客户端输出数据(写数据)输出流
//1.如何获取输出流对象(javaEE规范中,需要借助HttpServletResponse)
//2.向客户端输出数据,除了响应业务数据外,
//3.
//设置编码格式
httpServletResponse.setCharacterEncoding("utf-8");
//httpServletResponse.setContentType("text/html;charset=utf-8");
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
Map<String,Object> map = new HashMap<>();
map.put("status", 429);
if(e instanceof DegradeException){
map.put("message","服务暂时不可用");
// out.println("服务暂时不可用");
}else {
map.put("message","访问太频繁");
// out.println("访问太频繁");
}
//{} --将map对象转换为Json格式字符串
String jsonStr = new ObjectMapper().writeValueAsString(map);
out.println(jsonStr);
out.flush();//所有字符流都要刷新
}