Sentinel笔记

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容