微服务 11: Sentinel的微服务 限流与 熔断降级(文末有项目连接)

文章知识来源主要来源于:赵俊夫先生的博客  以下为原文链接
https://blog.csdn.net/u011177064/category_9572944.html
1:微服务限流?
在之前一节 已经讲过了Sentinel在GateWay进行限流了,
下面所讲的是在微服务进行限流。
主要应用于在给该服务的一些核心接口进行限流,防止被频繁调用。
比如说 现金提现  转账之类的。
2:什么是熔断降级?
很多时候,在网上都会把熔断和降级区分成狭义上两个概念。
比如说:单个服务的熔断  整体服务的降级

但我认为实际上是一个东西 也就是因为熔断才导致降级的  所有没有必要区分清楚

首先我们需要认识到微服务架构的一大特点就是服务被拆分地非常细:
    所以一个可供前端使用的、完整的接口,调用经常会需要涉及到多个微服务,
    比如说一个获取订单详情的接口,开放给前端的就是单单一个接口,
    但是这个接口里面可能调用了订单数据、然后同时还调用了商品的接口、用户的接口,最后把数据聚合后返回给前端。
  
假设其中有某个接口突然失效了
    比如说因为各种原因(比如缓存失效,数据库崩溃等)导致响应时间过长,那么所有需要调用到此接口的其他服务将全部“卡死”在这儿了,
    随之而来的就是整个系统逐渐把所有网络连接挂起,服务器连接数暴增,最后系统崩溃了,通常这种情况叫做雪崩。

熔断降级就是解决此类问题:
    它可以以响应时间、异常比例或次数等多个纬度来定制熔断规则,即达到设定的条件后,马上把该服务熔断,
    让其他调用者马上知道这个服务暂时不可用,并执行自己的应对措施,从而保证系统的整体稳定可用。
    因为这样子的整体稳定是一种“降级”的稳定,所以我们一般把它称为熔断降级。
3:需要进行熔断的服务的Pom文件(consumer 或者 provider)
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
4:编写 微服务限流 的接口
使用 @SentinelResource 注解,代替 Sentinel 编程式风格,更为简洁。
@RestController
@RequestMapping(value = "/sentinelFlow")
public class SentinelFlow {
    /**
     * 在构造方法里初始化了 限流 规则。
     * 写在这里只是为了方便测试,实际开发中,规则的初始化可以放在统一的地方,而不是写在每个控制器里
     */
    public SentinelFlow() {
        initFlowRules();
    }
    
    /**
     * 熔断测试接口
     *
     * @return
     * @SentinelResource 中的 value 即为熔断规则中设置的 Resource 名
     * blockHandler与blockHandlerClass 结合起来是定义
     * 熔断后处理逻辑由ExceptionUtil.handleException(前面可加自己的参数,BlockException e)控制
     */
    @GetMapping(value = "/test")
    @SentinelResource(value = "sentinelFlowTest", blockHandler = "handleException", blockHandlerClass = {SentinelException.class})
    public String test() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "sentinel pass";
    }


    /**
         * 初始化限流规则
         */
    private static void initFlowRules() {
        /*
         *  resource:资源名,即限流规则的作用对象
            count: 限流阈值
            grade: 限流阈值类型(QPS 或并发线程数)
            limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
            strategy: 调用关系限流策略
            controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
         */
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource("sentinelFlowTest");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(10);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}
5:编写 熔断降级 的接口
@RestController
@RequestMapping(value = "/sentinelDegrade")
public class SentinelDegrade {
    /**
     * 在构造方法里初始化了熔断 规则。
     * 写在这里只是为了方便测试,实际开发中,规则的初始化可以放在统一的地方,而不是写在每个控制器里
     */
    public SentinelDegrade() {
        initDegradeRules();
    }

    /**
     * 熔断测试接口
     *
     * @return
     * @SentinelResource 中的 value 即为熔断规则中设置的 Resource 名
     * blockHandler与blockHandlerClass 结合起来是定义
     * 熔断后处理逻辑由ExceptionUtil.handleException(前面可加自己的参数,BlockException e)控制
     */
    @GetMapping(value = "/test")
    @SentinelResource(value = "sentinelDegrade", blockHandler = "handleException", blockHandlerClass = {SentinelException.class})
    public String test() {
        try {
            //睡眠500ms
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "sentinel pass";
    }


    /**
     * 初始化熔断规则
     */
    private static void initDegradeRules() {

//              【策略1】
//           平均响应时间 (DEGRADE_GRADE_RT)  触发: 1s 内持续进入超过 5 个请求
//           平均响应时间超过 count (10ms) 则熔断 timewindow(10s)
//        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
//        rule.setCount(10);
//        rule.setTimeWindow(10);

        List<DegradeRule> rules = new ArrayList<DegradeRule>();
        DegradeRule rule = new DegradeRule();
        rule.setResource("sentinelDegrade");
        //降级策略
        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        //平均响应时间 ms
        rule.setCount(10);
        //降级时间 s
        rule.setTimeWindow(10);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);


//          【策略2】
//          异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO) 触发:每秒请求量 >= 5
//          每秒异常总数占通过量的比值超过阈值count([0.0, 1.0],代表 0% - 100%) 之后 则熔断  timewindow(10s)
//        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
//        rule.setCount(0.1);
//        rule.setTimeWindow(10);
//
//          【策略3】
//          异常数 (DEGRADE_GRADE_EXCEPTION_COUNT)
//          当资源近 1 分钟的异常数超过阈值 (count) 之后会进行熔断
//          注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。
//          rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
//        rule.setCount(5);
//        rule.setTimeWindow(90);

    }
}
6:编写 限流功能 和 熔断降级的 单元测试
由于只使用Postman的并发测试,很难模拟 每隔多少秒进行请求的场景
因此编写单元测试 进行并发的测试
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class SentinelJunit {

   /**
    * 限流
    * @throws InterruptedException
    */
   @Test
   public void sentinelFlowJunit() throws InterruptedException {
       while(true) {
           //每秒发20次请求
           for (int i = 0; i < 20; i++) {
               final int i1=i;
               new Thread(() ->{
                   System.out.println((new Date()+"---"+i1+"---"+
                           sendGetRequest("http://localhost:9991/sentinelFlow/test")));
               }).start();
           }
           Thread.sleep(1000);
       }

   }

   /**
    * 熔断降级
    * @throws InterruptedException
    */
   @Test
   public void sentinelDegradeJunit() throws InterruptedException {
       while(true) {
           //每秒发20次请求
           for (int i = 0; i < 20; i++) {
               final int i1=i;
               new Thread(() ->{
                   System.out.println((new Date()+"---"+i1+"---"+
                           sendGetRequest("http://localhost:9991/sentinelDegrade/test")));
               }).start();
           }
           Thread.sleep(1000);
       }

   }

   public  String sendGetRequest(String getUrl) {
       StringBuffer sb = new StringBuffer();
       InputStreamReader isr = null;
       BufferedReader br = null;
       try {
           URL url = new URL(getUrl);
           URLConnection urlConnection = url.openConnection();
           urlConnection.setAllowUserInteraction(false);
           isr = new InputStreamReader(url.openStream());
           br = new BufferedReader(isr);
           String line;
           while ((line = br.readLine()) != null) {
               sb.append(line);
           }
       } catch (IOException e) {
           e.printStackTrace();
       }finally {
           try {
               br.close();
               isr.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
       return sb.toString();
   }
   
}
7:限流与熔断对比
 注解说明
 使用 @SentinelResource 注解,代替 Sentinel 编程式风格,更为简洁。
@GetMapping(value = "/test")
@SentinelResource(value = "sentinelTest", blockHandler = "handleException", blockHandlerClass = {SentinelException.class})

该注解会把test的这个接口路径 注册成为自定义名称的sentinelTest 
然后该自定义名称 sentinelTest 提供给到 限流或者熔断功能去监听
当限流以及熔断功能同时监听了该 名称
限流功能先进行限流 然后再进行熔断的判断

最后:
限流  一个接口限流后 实时不可用            (可以扩展成  多个接口  ->  服务)
熔断 一个接口熔断后 一段时间不可用        (可以扩展成  多个接口  ->  服务)
限流测试结果
1秒钟里面的20个请求必定有10个被拦截
熔断测试结果
 当该接口进行熔断的时候 10秒内不可用

项目连接

请配合项目代码食用效果更佳:
项目地址:
https://github.com/hesuijin/spring-cloud-alibaba-project
Git下载地址:
https://github.com.cnpmjs.org/hesuijin/spring-cloud-alibaba-project.git

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

推荐阅读更多精彩内容