美文网首页
Sentinel笔记

Sentinel笔记

作者: just_like_you | 来源:发表于2019-08-15 15:55 被阅读0次

    1.常见的处理服务雪崩的方式

    • 超时时间
    • 限流
    • 仓壁模式
    • 断路器模式

    2.Sentinel流控方式

    流控模式:

    • 直接:根据QPS或者线程数直接关联,超过阈值直接失败
    • 关联:关联资源超过阈值,则失败
    • 链路:绑定某入口资源的阈值,超时则失败

    流控策略:

    • 快速失败:直接失败抛出异常,源码位置com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
    • Warm Up : 预热模式,设置初始流量为阈值/codeFactor(默认为3),经过设置的默认时长才达到阈值,源码位置:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController,适用于大流量,避免一次性流量过大导致服务直接崩溃。
    • 排队等待:请求匀速通过,阈值必须设置成QPS,否则无效。适用于流量激增。

    3.降级策略

    • RT(秒级统计),在时间窗口内QPS>=5次,并且平均响应时间超出阈值,则触发降级,在时间窗口结束后关闭降级,RT的最大响应设置时间为4900ms,若需要指定更大,则需要通过-Dcsp.sentinel.statistic.max.rt=xx
    • 异常比例(秒级统计),QPS>=5并且异常比例超过阈值,则会进入熔断
    • 异常数(分钟统计),异常数超过阈值,则触发降级,若时间窗设置小于60S,那么可能在降级时间窗结束后在此进入降级

    4.热点策略

    • 针对api接口参数特定的值进行限流,其中配置参数索引的特点热点值数据只对基本数据类型和String有效
    • 可参考相关源码com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker#passCheck

    [图片上传失败...(image-87caf8-1565855752793)]

    5.系统规则(load1,cpu,..)

    load1 = cpu核心数 * 2.5

    6.授权规则

    针对微服务名称进行黑白名单设置,可以通过定义RequestOriginParser来设置名称规则

    7.代码配置方式配置Sentinel容错策略

    ex : 初始化一个简单的流控规则,在资源执行前调用即可

        private void initFlowQpsRule() {
            List<FlowRule> ruleList = new ArrayList<>();
            FlowRule rule = new FlowRule("/c");
            //设置阈值
            rule.setCount(1);
            //设置流控阈值类型
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            //设置流控模式
            rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
            //设置流控策略
            rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
            rule.setLimitApp("default");
            ruleList.add(rule);
            FlowRuleManager.loadRules(ruleList);
        }
    

    详细所有的配置信息参考 - > 大佬Sentinel博客

    8.Sentinel Dashboard 是如何知道微服务的信息?如何通信?

    • Sentinel实现了服务注册和发现
    • 通过配置的通信端口+服务ip,sentinel可以使用http调用其api来操作设置容错规则和获取监控信息

    源码解析:

    注册/心跳发送:com.alibaba.csp.sentinel.transport.heardheat.SimpleHttpHeartbeatSender

    通信api : com.alibaba.csp.sentinel.commonand.CommandHandler相关实现类

    9.Sentinel Api的简单使用

    • ContextUtil 定义入口资源
    • Sphu 定义资源
    • Tracer.trace() 统计资源 , 在非BlockException的异常时候需要显示的使用来统计,不然不会自动统计
       @GetMapping("/test-sentinel-api")
        public String testSentinelApi() {
           String resourceName = "test-sentinel-api";
            ContextUtil.enter(resourceName, "test-micro-service");
            Entry entry = null;
            try {
                //定义一个资源
                entry = SphU.entry(resourceName);
                //执行业务逻辑
                if (!StringUtils.isNotBlank("")) {
                    throw new IllegalAccessException("参数非法");
                }
                return "success";
            } catch (BlockException e) {
                return "容错触发!!";
            } catch (IllegalAccessException e) {
                //默认除了BlockException及其子类会计算容错次数,异常数等等。。
                //其他异常需要使用Tracer.trace()进行统计
                Tracer.trace(e);
                return "非法参数";
            } finally {
                if (entry != null) {
                    entry.exit();
                }
                ContextUtil.exit();
            }
        }
    
    

    10.sentinel整合RestTemplate和feign

    • 直接在注入的RestTemplate上面加入@SentinelRestTemplate注解即可

    核心处理原理见 : SentinelBeanPostProcessor

    • 整合feign,直接在配置文件中指定feing.sentinel.enabled=true即可。

    11.持久化Sentinel规则

    • 默认不持久化,保存在内存中
    • pull模式,将sentinel规则保存在文件或者数据库中
    • push模式(生成环境使用),将sentinel的持久化规则同步到nacos/zk/Apollo等配置中心上

    push模式的集成

    改造sentinel-dashboard控制台

    因为push模式是sentinel-dashboard直接和远程配置中心直接交互,而不用sentinel客户端去参加

    修改步骤:

    • 将sentinel-datasource-nacos依赖的范围变成compile
            <!-- for Nacos rule publisher sample -->
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
    <!--            <scope>test</scope>-->
            </dependency>
    
    • 修改拉取和推送的策略

    具体实现见 github

    微服务集成sentinel持久化规则
    • 加依赖
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
            </dependency>
    
    • 配置
    spring:
      cloud:
        sentinel:
          transport:
            dashboard: localhost:8080
            port: 8820
          datasource:
            # 名称随意
            flow:
              nacos:
                server-addr: 192.168.18.91:8848
                dataId: ${spring.application.name}-flow-rules
                groupId: SENTINEL_GROUP
                # 规则类型,取值见:
                # org.springframework.cloud.alibaba.sentinel.datasource.RuleType
                rule-type: flow
            degrade:
              nacos:
                server-addr: 192.168.18.91:8848
                dataId: ${spring.application.name}-degrade-rules
                groupId: SENTINEL_GROUP
                rule-type: degrade
            param-flow:
              nacos:
                server-addr: 192.168.18.91:8848
                dataId: ${spring.application.name}-param-flow-rules
                groupId: SENTINEL_GROUP
                rule-type: param-flow
    

    12.sentinel在默认是会拦截所有的controller请求

    可配置排除

    spring:
        cloud:
            sentinel:
                filter:
                  enabled: false
    

    如何定制默认的controller请求错误拦截页面?实现UrlBlockHandler即可

    @Component
    public class MyUrlBlockHandler implements UrlBlockHandler {
        @Override
        public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws IOException {
            ErrorMsg msg = null;
          //根据异常类型的不同判断是发生了限流还是降级
            if (e instanceof FlowException) {
                msg = ErrorMsg.builder()
                        .msg("被限流了")
                        .status(101)
                        .build();
            } else if (e instanceof DegradeException) {
                msg = ErrorMsg.builder()
                        .msg("被降级了")
                        .status(101)
                        .build();
            }
            response.setContentType("application/json;charset=utf-8");
            response.setCharacterEncoding("utf-8");
            response.setStatus(500);
            new ObjectMapper()
                    .writeValue(
                            response.getWriter(),
                            msg
                    );
        }
    
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        @Builder
        private static class ErrorMsg {
            private String msg;
            private Integer status;
        }
    }
    

    13.Sentinel的来源应用配置

    • 可以通过不同的来源来配置匹配的策略。
    • 可以通过授权规则来添加黑白名单。

    通过实现RequestOriginParser来实现,该方法的返回值就是应用的名称。

    /**
     * @author hj
     * 2019-08-15 13:55
     * 自定义区分来源解释器,通过origin请求参数来区分来源应用
     */
    @Component
    public class MyRequestOriginParser implements RequestOriginParser {
        @Override
        public String parseOrigin(HttpServletRequest httpServletRequest) {
            //如果参数中有origin参数那么获取并获取值作为来源
            String origin = httpServletRequest.getParameter("origin");
            if (StringUtils.isBlank(origin)) {
                throw new IllegalArgumentException("origin must not be null");
            }
            //无则抛出异常
            return origin;
        }
    }
    

    14.sentinel对Restful-url的支持

    sentinel默认对/a/{id}这种格式url会随着参数变化而产生多个资源,那么如何让这些资源共享一个sentinel规则? 通过UrlCleaner实现类将这种rest风格转化成相同的url

    实现UrlCleaner接口

    /**
     * @author hj
     * 2019-08-15 14:24
     * 扩展restFul-Url,让/shares/* 使用相同的逻辑
     */
    @Component
    public class MyUrlCleaner implements UrlCleaner {
        @Override
        public String clean(String url) {
            String[] split = url.split("/");
            return Arrays.stream(split)
                    .map(a -> {
                        if (NumberUtils.isNumber(a)) {
                            return "{number}";
                        }
                        return a;
                    })
                    .reduce((a, b) -> a + "/" + b)
                    .orElse("");
        }
    }
    

    15总结扩展表格

    其实上面的扩展点都是来源于CommonFilter

    public class CommonFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
            HttpServletRequest sRequest = (HttpServletRequest)request;
            Entry urlEntry = null;
    
            try {
                String target = FilterUtil.filterTarget(sRequest);
                //处理 REST APIS 的UrlCleaner
                UrlCleaner urlCleaner = WebCallbackManager.getUrlCleaner();
                if (urlCleaner != null) {
                    target = urlCleaner.clean(target);
                }
    
                if (!StringUtil.isEmpty(target)) {
                  //处理来源的RequestOriginParser
                    String origin = parseOrigin(sRequest);
                    ContextUtil.enter(WebServletConfig.WEB_SERVLET_CONTEXT_NAME, origin);
    
                    if (httpMethodSpecify) {
                        // Add HTTP method prefix if necessary.
                        String pathWithHttpMethod = sRequest.getMethod().toUpperCase() + COLON + target;
                        urlEntry = SphU.entry(pathWithHttpMethod, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
                    } else {
                        urlEntry = SphU.entry(target, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
                    }
                }
                chain.doFilter(request, response);
            } catch (BlockException e) {
                HttpServletResponse sResponse = (HttpServletResponse)response;
                //处理错误页面返回的UrlBlockHandler
                WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
            } catch (IOException | ServletException | RuntimeException e2) {
                Tracer.traceEntry(e2, urlEntry);
                throw e2;
            } finally {
                if (urlEntry != null) {
                    urlEntry.exit();
                }
                ContextUtil.exit();
            }
        
    
    核心接口 处理问题
    UrlBlockHandler 错误处理
    UrlCleaner 合并rest apis
    RequestOriginParser 来源控制ContextUtil

    更多配置和详细,见Sentinel GitHub

    相关文章

      网友评论

          本文标题:Sentinel笔记

          本文链接:https://www.haomeiwen.com/subject/kmbzjctx.html