spring cloud Alibaba 容错服务-Sentinel学习笔记七

一、雪崩效应

英文名为cascading failure,也叫级联失效,级联故障;每个微服务并不是100%可用,网络也有可能出问题,如有一个高并发的微服务系统,如下图,包含四个微服务,开始都为正常,在某个时间点,当A挂了,而此系统为高并发系统,B服务疯狂调用A服务,而A挂了,B发往A的请求就会强制等待,知道请求超时,在java程序中,一次请求往往对应一个线程,请求强制等待,线程就会强制阻塞,一直等到线程超时,才会被释放,在高并发情况下,阻塞线程越来越多,而线程对应的又是服务器计算资源,如不做任务处理,随着积累,B服务将无法创建线程,B服务也挂了,同理,C、D也疯狂请求B,C、D也会因为同样原因而不可用,这就是雪崩效应;导致雪崩效应的原因就是服务消费者未做好容错措施
在这里插入图片描述
1、业界常用容错方案

  • 设置请求超时时间
  • 设置限流,根据最大qbs,限流,超出后不再接受请求
  • 仓壁模式:如ShareController有一个thread-pool-1独立线程池,coreSize=10;TestController也有一个thread-pool-x线程池;当线程池1满了,将拒绝访问请求,不会影响线程x运行
  • 断路器模式:
    ① 现实中例子:监控+开关,实时监控电路状态,当发现某段时间电流过大,认为电路短路,将会自动跳闸,保证电路不被烧毁
    ② 代码例子:某服务接口5秒以内请求错误率、错误次数达到阈值,就跳闸;而该接口又恢复正常,该怎么办?断路器模式巧妙设计了一种半开状态,如下图
    在这里插入图片描述
    当请求错误率达到阈值,断路器打开,然后会有一个短暂的窗口期,允许服务请求一次,若失败,则拒绝服务;果断时间又打开窗口,允许服务请求一次,成功,则断路器关闭,允许请求,达到自我修复

PS:四种方案思想,超时,释放够快,就不那么容易死;限流,只有一碗饭量,哪怕给三碗,也只吃一碗;仓壁模式,不把鸡蛋放一个篮子里,你有你的线程池,我有我的线程池;断路器模式,监控+开关,当监控API达到一定预值,就跳闸

二、Sentinel实现容错

轻量级的流量控制、熔断降级java库

1、整合sentinel

  • 加依赖:通过 /actuator/sentinel 暴露端点进行容错启用验证
<!-- 容错服务组件 sentinel依赖  -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>0.2.0.RELEASE</version>
</dependency>
<!-- 添加 actuator依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

actuator配置,将隐藏的端点暴露出来
在这里插入图片描述

  • 加注解
  • 写配置

2、Sentinel控制台

  • 选择最新的jar包下载到本地,然后用java -jar xxx.jar启动控制台 控制台下载地址
  • 为内容中心整合控制台:
    在这里插入图片描述
    访问localhost:8080,发现列表为空,因为sentinel是懒加载,内容中心发送请求后,列表就会显示内容中心微服务信息

三、容错规则

sentinel配置规则

1、流控规则
在这里插入图片描述

  • 关联:用于保护资源,如资源为查询,关联资源为修改,若修改过于频繁,就会限制查询,是保护关联资源的一种设计
  • 链路:细粒度配置,直接、关联都是微服务级别,链路则细分至API级别

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、降级规则(断路器模式)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3、热点规则(特殊的流控规则):可对参数限流,亦或是对参数的值进行限流;适用于某些参数传递频繁,但有希望提高接口可靠性的场景;热点参数必须是基本类型或者String,否则不会生效

在这里插入图片描述
4、系统规则-阈值类型

在这里插入图片描述
在这里插入图片描述
5、授权规则:对消费者进行授权控制,白名单,则是允许test微服务访问内容中心 /shares/1 API,黑名单则是不允许访问
在这里插入图片描述

代码配置规则

Alibaba Sentinel 规则参数总结

四、sentinel与控制台通信原理剖析

控制台是如何获取到微服务的监控信息?用控制台配置规则时,控制台是如何将规则发送到各个微服务?

  • sentinel 与 控制台通信:sentinel实现了一套服务发现机制
    在这里插入图片描述
  • 监控信息:通过调用API实现
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

五、控制台相关配置项

  • 应用端连接控制台配置项
    在这里插入图片描述
  • 控制台配置项
    在这里插入图片描述
    在这里插入图片描述
    配置使用

在这里插入图片描述

六、sentinel API详解(自定义异常提示)

在这里插入图片描述

  • SphU:定义资源,让资源受到监控,并且可以保护资源
  • Tracer:对想要的异常进行统计
  • ContextUtil:可以实现调用来源,还可以标记调入

PS:代码示例

@GetMapping("/test-sentinel-api")
public String testSentinelApi(@RequestParam String a){
    //定义一个sentinel保护资源,名称为test-sentinel-api
    String resourceName = "test-sentinel-api";
    //标记资源
    ContextUtil.enter(resourceName,"test-wfw");
    Entry entry = null;
    try {
        entry = SphU.entry(resourceName);
        if(StringUtils.isBlank(a)){
            throw new IllegalArgumentException("a不能为空!");
        }
        return a;
    } catch (BlockException e) {
        e.printStackTrace();
        return "限流或者降级了";
    }catch (IllegalArgumentException e2){
        Tracer.trace(e2);
        return "非法参数";
    }finally {
        if(entry != null){
            entry.exit();
        }
        ContextUtil.exit();
    }
}

七、sentinel Resource注解详解(自定义异常提示)

PS:让代码更加优雅,参考手记

PS:代码示例

/**
 * sentinel 注解
 * @param a
 * @return
 */
@GetMapping("/testSentinelResource")
@SentinelResource(
        value = "test-sentinel-resource",
        blockHandler = "block",
        blockHandlerClass = TestControllerBlockHandler.class,
        fallback = "fallback",
        defaultFallback = "defaultFallback",
        fallbackClass = TestControllerFallback.class
)
public String testSentinelResource(@RequestParam String a){
    if(a.equals("0")){
        throw new IllegalArgumentException("a不能为0!");
    }
    return a;
}
public class TestControllerFallback {
    public static String defaultFallback(String a,Throwable throwable){
        return "降级,或者其他异常:" + throwable.getMessage();
    }

    public static String fallback(String a,Throwable throwable){
        return "指定异常:" + throwable.getMessage();
    }
}
public class TestControllerBlockHandler {
    public static String block(String a, BlockException e){
        return "限流或者降级了,block";
    }
}

在这里插入图片描述

八、Template整合sentinel

  • 加注解
//在spring容器中,创建一个对象,类型RestTemplate,名称/id为方法名
//相当于传统<bean id="restTemplate" class="xxx.RestTemplate"/>
@Bean
@LoadBalanced
@SentinelRestTemplate(
    blockHandler = "block",
    blockHandlerClass = TestControllerBlockHandler.class,
    fallback = "fallback",
    fallbackClass = TestControllerFallback.class
)
public RestTemplate restTemplate(){
    return new RestTemplate();
}
  • 开关
#Template整合sentinel 开关
resttemplate:
  sentinel:
    enabled: true
  • 相关源码:涉及Spring生命周期、反射、拦截器,其中不懂的知识点可以重新复习
    在这里插入图片描述

九 、feign整合sentinel

  • 加注解
    在这里插入图片描述
  • 限流降级发生时,如何定义自己的处理逻辑
    ① 在用户中心 feign客户端进行fallback配置
    在这里插入图片描述
    ② 新建类实现 feign客户端接口
package com.hzb2i.contentcenter.feignClient;
import com.hzb2i.feignapi.domain.dto.user.UserDto;
import org.springframework.stereotype.Component;

@Component
public class UserCenterFeignClientFallback implements UserCenterFeignClient{
    @Override
    public UserDto findById(Integer id) {
        UserDto userDto = new UserDto();
        userDto.setWxNickname("默认用户");
        return userDto;
    }

    @Override
    public UserDto query(Integer id, String userName) {
        UserDto userDto = new UserDto();
        userDto.setWxNickname("默认用户");
        return userDto;
    }
}
  • 如何获取异常
    ① 在用户中心 feign客户端进行fallbackFactory配置
    在这里插入图片描述
    ② 实现FallbackFactory类
package com.hzb2i.contentcenter.feignClient;

import com.hzb2i.feignapi.domain.dto.user.UserDto;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class UserCenterFeignClientFallbackFactory implements FallbackFactory<UserCenterFeignClient> {
    @Override
    public UserCenterFeignClient create(Throwable throwable) {
        return new UserCenterFeignClient() {
            @Override
            public UserDto findById(Integer id) {
                log.error("限流或降级:" + throwable);
                UserDto userDto = new UserDto();
                userDto.setWxNickname("默认用户");
                return userDto;
            }

            @Override
            public UserDto query(Integer id, String userName) {
                log.error("限流或降级:" + throwable);
                UserDto userDto = new UserDto();
                userDto.setWxNickname("默认用户");
                return userDto;
            }
        };
    }
}

PS:sentinel小结在这里插入图片描述

十、规则持久化

从测试反馈,微服务每次重启,规则就消失了,每次都需要重新配置,不适用于生产,需进行规则持久化

PS:生产环境使用sentinel

  • 推拉模式持久化规则:推模式更佳
  • AHAS:开通地址

十一、集群流控

在这里插入图片描述

十二、sentinel扩展

  • 错误页提示优化:对异常进行细分(衔接六 sentinelAPI和七 sentinel Resource注解)
    ① 打开spring MVC端点保护
    在这里插入图片描述
    默认错误提示:
    在这里插入图片描述
    ② 实现BlockExceptionHandler,对异常提示进行优化:
@Component
public class MyUrlBlockHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        ErrMsg msg = null;
        if(e instanceof FlowException){ //限流异常
            msg = ErrMsg.builder().status("100").msg("限流了").build();
        }
        else if(e instanceof DegradeException){ //降级异常
            msg = ErrMsg.builder().status("101").msg("降级了").build();
        }
        else if(e instanceof ParamFlowException){ //热点异常
            msg = ErrMsg.builder().status("102").msg("热点参数限流").build();
        }
        else if(e instanceof SystemBlockException){ //系统异常
            msg = ErrMsg.builder().status("100").msg("系统规则不满足").build();
        }
        else if(e instanceof AuthorityException){ //权限异常
            msg = ErrMsg.builder().status("100").msg("授权规则不通过").build();
        }
        //http状态码
        httpServletResponse.setStatus(500);
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-type","application/json;charset=utf-8");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        new ObjectMapper().writeValue(
                httpServletResponse.getWriter(),
                msg
        );
    }
}

@Data
@Builder
@AllArgsConstructor
class ErrMsg{
    private String status;
    private String msg;
}

优化后提示:
在这里插入图片描述

  • 区分来源,异常提示优化
@Component
public class MyRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        String origin = httpServletRequest.getParameter("origin");
        if(StringUtils.isBlank(origin)){
            throw new IllegalArgumentException("origin must be special!");
        }
        return origin;
    }
}

测试:针对browser进行流控,访问地址参数带?origin=browser则会被限流,其他传值则不会限流
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
PS:不建议将origin参数放在url地址中,可放在header头部里

  • restfulUrl支持,实现URLCleaner接口,通过处理返回相同的url路径

处理前:
在这里插入图片描述
处理后:
在这里插入图片描述

@Slf4j
@Component
public class MyUrlCleaner implements UrlCleaner {
    @Override
    public String clean(String s) {
        log.info(s);
        String[] split = s.split("/");
        return Arrays.stream(split).map(string -> {
            if(StringUtils.equals(string,"{shareId}")){
            //if(NumberUtils.isNumber(string)){
                return "{number}";
            }
            return string;
        }).reduce((a,b) -> a + "/" + b).orElse("");
    }
}

PS:透过现象看本质,核心是CommonFilter过滤器
在这里插入图片描述

十三、Alibaba Sentinel 配置项总结

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值