我们接上回

上一篇中,我们进行了简要的微服务实现,也体会到了SpringCloudAlibaba的强大和神奇之处

我们仅改动了两个注释,其他全篇代码不变,原来的独立服务就被我们分为了provider和consumer两个独立部分,二者各司其职,分工明确

篇尾我们留下了一个疑问,consumer对provider有依存关系,如果下游的provider出现异常,上游的consumer如何自处?

其实这种单一的上下游关系仅是微服务日常运转下各种复杂情境的一个缩影,真实的生产环境下各个微服务节点可能会形成更为复杂的依赖关系,那我们该如何解决这些问题?

本节中我们引入Sentinel框架:

什么是 Sentinel?

在基于 SpringCloud 构建的微服务体系中,服务间的调用链路会随着系统的演进变得越来越长,这无疑会增加了整个系统的不可靠因素。

在并发流量比较高的情况下,由于网络调用之间存在一定的超时时间,链路中的某个服务出现宕机都会大大增加整个调用链路的响应时间,而瞬间的流量洪峰则会导致这条链路上所有服务的可用线程资源被打满,从而造成整体服务的不可用,这也就是我们常说的 “雪崩效应”。

而在微服务系统设计的过程中,为了应对这样的糟糕情况,最常用的手段就是进行 ”流量控制“ 以及对网络服务的调用实现“熔断降级”。因此,Sentinel 就因运而生了。

Sentinel 是一款面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来保障服务的稳定性,核心思想是:根据对应资源配置的规则来为资源执行相应的流控/降级/系统保护策略,Sentinel 的主要特性如下图:

以上内容来源于Sentinel官方文档,看到这一大堆不明所以的名词接释,新入坑的同学可能已经劝退了。。

接下来我们依然沿用前几篇的思想,先不要管这一堆的理论,先来看怎么用,实际运用过程中如何达到我们预期的效果,返回来再看这些名词,你自然会有更清晰的认知

Sentinel怎么用?

Sentinel的使用分为两部分 —— 代码和控制台

首先我们先下载Sentinel控制台:https://github.com/alibaba/Sentinel/tags,本节中我使用的是Sentinel1.8版本

下载编译好的jar包,而后执行命令行:

  1. java -jar .\sentinel-dashboard.jar --server.port=9999

之后我们打开浏览器,输入地址:http://127.0.0.1:9999/

打开Sentinel控制台如图所示:

整个面板是空白的,什么都没有?!不用着急,我们继续往下看代码的部分

上一节我们提到了微服务环境下consumer工程存在的隐患问题,接下来我们就要消除这个隐患!

dubbo-nacos-consumer工程引入Sentinel依赖库

  1. <!-- 引入sentinel依赖 -->
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  5. </dependency>

这一步肯定是必须的,没什么好解释的

添加Sentinel相关配置

在SpringCloudAlibaba体系下,我们以Nacos为配置中心,所以我们编辑Nacos中的相关配置即可,非常方便

我把代码文本也贴出来,便于大家自行复制

  1. # sentinel看板配置
  2. spring.cloud.sentinel.transport.dashboard = 127.0.0.1:9999
  3. # 开启对sentinel看板的饥饿式加载。sentinel默认是懒加载机制,只有访问过一次的资源才会被监控,通过关闭懒加载,在项目启动时就连接sentinel控制台
  4. spring.cloud.sentinel.eager = true

这两句的主要意图在于将Consumer工程关联到Sentinel控制台,便于后续通过控制台统一管控

改造原有的PersonController

我们先创建一个ViewObject

代码如下:

  1. package com.example.dubbonacosconsumer.vo;
  2.  
  3. import com.example.dubbonacosapi.model.Person;
  4.  
  5. import java.util.List;
  6.  
  7. public class SelectRetVo {
  8. private List<Person> persons;
  9. private String error;
  10.  
  11. public List<Person> getPersons() {
  12. return persons;
  13. }
  14.  
  15. public void setPersons(List<Person> persons) {
  16. this.persons = persons;
  17. }
  18.  
  19. public String getError() {
  20. return error;
  21. }
  22.  
  23. public void setError(String error) {
  24. this.error = error;
  25. }
  26. }

然后是PersonController的改造

  1. package com.example.dubbonacosconsumer.controller;
  2.  
  3. import com.alibaba.csp.sentinel.annotation.SentinelResource;
  4. import com.alibaba.csp.sentinel.slots.block.BlockException;
  5. import com.example.dubbonacosapi.model.Person;
  6. import com.example.dubbonacosapi.service.PersonService;
  7. import com.example.dubbonacosconsumer.vo.SelectRetVo;
  8. import org.apache.dubbo.config.annotation.DubboReference;
  9. import org.springframework.web.bind.annotation.GetMapping;
  10. import org.springframework.web.bind.annotation.PostMapping;
  11. import org.springframework.web.bind.annotation.RequestMapping;
  12. import org.springframework.web.bind.annotation.RestController;
  13.  
  14. import java.util.ArrayList;
  15.  
  16. @RestController
  17. @RequestMapping("/person")
  18. public class PersonController {
  19. @DubboReference
  20. PersonService service;
  21.  
  22. @PostMapping("/insert")
  23. public Integer insert(Person person) {
  24. return service.insert(person);
  25. }
  26.  
  27. @PostMapping("/update")
  28. public Integer update(Person person) {
  29. return service.update(person);
  30. }
  31.  
  32. @PostMapping("/delete")
  33. public Integer delete(int id) {
  34. return service.delete(id);
  35. }
  36.  
  37. @GetMapping("/select")
  38. @SentinelResource(value = "person/select", blockHandler = "selectBlock")
  39. public SelectRetVo select() {
  40. SelectRetVo vo = new SelectRetVo();
  41. vo.setPersons(service.select());
  42. vo.setError("ok");
  43. return vo;
  44. }
  45.  
  46. public SelectRetVo selectBlock(BlockException e) {
  47. SelectRetVo vo = new SelectRetVo();
  48. vo.setPersons(new ArrayList<Person>());
  49. vo.setError(“当前访问人数过多,请稍后...”);
  50. return vo;
  51. }
  52. }

我们将原有的select方法的返回值从原来的List<Person>升级为SelectPersonVo,后者在前者原有基础上扩展了一个error字段,用于返回异常信息

接着是本节的关键:select方法新增@SentinelResource注解,我们前边讲过:Sentinel根据对应资源配置的规则来为资源执行相应的流控/降级/系统保护策略

因此这个注解的作用是——用于标明这是一个Sentinel系统中的资源

value代表这个资源的名称是person/select,这个名字可以随自己的习惯自定义

blockHandler指定了一个方法,这个方法在Sentinel系统触发某种规则的时候会被执行,关于“某种规则”我们稍后会讲

这里指定的selectBlock方法,在定义时是有硬性要求的:

1. 保留select方法一样的参数,外加一个BlockException参数

2. 返回值必须和select方法相同 —— 所以明白为什么要额外定义一个SelectRetVo了吧?

控制台的使用

我们像上一节一样,分别启动provider和consumer,然后刷新Sentinel控制台页面

看到这个界面的时候,你是否有种豁然开朗的感觉?

因为consumer中定义了Sentinel资源,所以当dubbo-nacos-consumer工程执行之后,控制台会有相关显示

而功能菜单中有N多项的名称都是XX规则,这也就是前边我们定义的blockHandler对应的某种规则

我们在post中调用consumer中的select方法,此时实时监控页面显示如下内容:

这里的person/select自然就是我们定义的【资源】,控制台配套显示了其各项指标数据,很直观也很方便

下边还有一项/person/select(开头多个/),这个又是什么?这里先直接告诉大家答案——Sentinel默认会将所有Controller添加请求映射的方法视为资源

那我们额外添加一个@SentinelResource注解是否多此一举?答案是否,因为Controller生成的默认Sentinel资源是不带自定义规则触发方法的

因此@SentinelResource依然是有必要的,待本篇内容结束之后,大家可以自行验证这个说法

而从第二项【簇点链路】中,我们也能看到person/select和/person/select本身具备从属关系

流控规则

第三项【流控规则】对应了本节标题中提到的【限流】

这里涉及到三个概念:

阈值类型

QPS —— 服务器每秒接受的最大请求数

线程数 —— 服务器能容忍的最大线程占用数,一般用于保护服务器的业务线程池不被耗尽

流控模式

直接 —— 默认项,接口到达限流要求时,规则直接生效

关联 —— 当关联在资源到达阈值时,直接限流自己,一般应用于效率让步的诉求

链路 —— 记录链路流量,当入口资源到达阈值,则限流自己

流控效果

快速失败 —— 默认项,超出阈值后新请求直接拒绝

排队等待 —— 让请求匀速通过(漏桶算法),每个请求在一个允许的延迟时长范围

Warm up —— 冷启动模式,防止流量瞬间暴增直接将服务压垮,而是逐渐外放请求上限

我们先按图所示创建一个最简单的限流规则 —— QPS阈值为1,直接快速失败

而后我们启动两个post,同时向服务器端发送select请求,则结果对比如下:

 

由于QPS指定的阈值为1,因此同时发起的第二个请求会因触发限流规则而执行blockHandler方法

而我们针对 阈值类型、流控模式、流控效果 这三个指标交叉组合,可以创造出适用于各种服务器场景之下的限流规则

再比如我们按如下设置限流规则

这种设置方式代表我们所能允许select方法同时请求的上限值为2000,但是这个数值是在5分钟(300秒)之间逐步放开的,比如我们用这种模式可以应对类似双11期间大力度优惠而带来的突发流量洪峰

通过【限流】可以很好的起到保护服务器的作用,在特殊时期针对流量进行削峰填谷,使得服务器处在一个长期稳定的环境

降级规则

第四项【降级规则】对应了本节标题中的【熔断】

所谓“熔断”,其实最早这个概念来源于家庭电路中的保险丝,当家庭中的某件家用电器功率异常,或者某处电路出现短路,

为防止电流过大造成更大的损失,此刻保险丝即会【熔断】,造成我们日常所说的“掉闸”

熔断策略

慢调用比例 —— 慢调用其实就是响应超过预定时长,当这样的调用到达一定比例后,触发降级规则

异常比例 —— 方法调用出现异常数到达一定比例后,触发降级规则

异常数 —— 方法调用出现异常到达一定数目后,触发降级规则

我们按照图中所示创建一条降级规则,其含义为【任意1次并发请求(QPS)中有1次异常则触发熔断规则,时长为10秒】

这里的10秒时间是我们一个大概的预估值,一般理解为系统达到自愈效果需要大概10秒左右

之后我们直接关停下游的provider,则第一次请求会导致异常,而第二次请求则触发降级规则

若我们不设定降级规则,则大量的异常请求会堆积在consumer一侧,导致consumer最终崩溃

而熔断机制的设立,可以使得下游provider异常的情况下,上游的consumer依然做出正常的应答,

而10秒钟provider服务自愈之后,所有的异常影响将消失于无形 —— 这就是微服务方案下针对我们上一节所遗留的问题给出的答案

其他熔断策略同理,我们可以自行尝试

本节我们重点讲述了Sentinel系统中的限流熔断,而除此之外,Sentinel还有更多更丰富的规则设定,可以应对微服务体系中更加复杂多变的场景

这里我们推荐几篇文章,大家感兴趣可以自行阅读

sentinel 限流熔断神器详细介绍_张维鹏的博客-CSDN博客_sentinel熔断

Sentinel限流熔断最全教程_思月行云的博客-CSDN博客

本节内容到此为止,而微服务领域的探索却远不止于此

众多节点之间的复杂调用,不同微服务不同的认证方式,跨域问题等等一系列问题接踵而至,我们又当如何应对?

请期待下一节内容 —— 基于SpringCloudGateway的网关设计,谢谢~

  1. e.getMessage()

2流高手速成记(之八):基于Sentinel实现微服务体系下的限流与熔断的更多相关文章

  1. 2流高手速成记(之七):基于Dubbo&Nacos的微服务简要实现

    本节内容会用到之前给大家讲过的这两篇: 2流高手速成记(之六):从SpringBoot到SpringCloudAlibaba 2流高手速成记(之三):SpringBoot整合mybatis/mybat ...

  2. 2流高手速成记(之六):从SpringBoot到SpringCloudAlibaba

    咱们接上回 2流高手速成记(之五):Springboot整合Shiro实现安全管理 - 14号程序员 - 博客园 (cnblogs.com) 身边常有朋友说:小项目用PHP.大项目用Java(这里绝无 ...

  3. 不死的小强 .net core 微服务 快速开发框架 Viper 限流

    1.Viper是什么? Viper 是.NET平台下的Anno微服务框架的一个示例项目.入门简单.安全.稳定.高可用.全平台可监控.底层通讯可以随意切换thrift grpc. 自带服务发现.调用链追 ...

  4. 2流高手速成记(之五):Springboot整合Shiro实现安全管理

    废话不多说,咱们直接接上回 上一篇我们讲了如何使用Springboot框架整合Nosql,并于文章最后部分引入了服务端Session的概念 而早在上上一篇中,我们则已经讲到了如何使用Springboo ...

  5. Dubbo使用Sentinel来对服务进行降级与限流

    一.Sentinel 是什么 Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳定 ...

  6. Spring cloud微服务安全实战-6-8sentinel限流实战

    阿里2018年开源的. 简单来说就是干三件事,最终的结果就是保证你的服务可用,不会崩掉.保证服务高可用. 流控 先从最简单的场景来入手. 1.引用一个依赖, 2,声明一个资源. 3.声明一个规则 注意 ...

  7. 基于.net的微服务架构下的开发测试环境运维实践

    眼下,做互联网应用,最火的架构是微服务,最热的研发管理就是DevOps, 没有之一.微服务.DevOps已经被大量应用,它们已经像传说中的那样,可以无所不能.特来电云平台,通过近两年多的实践,发现完全 ...

  8. 2流高手速成记(之三):SpringBoot整合mybatis/mybatis-plus实现数据持久化

    接上回 上一篇我们简单介绍了基于SpringBoot实现简单的Web开发,本节来看Web开发中必不可少的内容--数据持久化 先看项目结构: 1. 创建数据表 打开mysql,打开数据库 test (没 ...

  9. 2流高手速成记(之四):SpringBoot整合redis及mongodb

    最近很忙,好不容易才抽出了时间,咱们接上回 上次我们主要讲了如何通过SpringBoot快速集成mybatis/mybatis-plus,以实现业务交互中的数据持久化,而这一切都是基于关系型数据库(S ...

随机推荐

  1. 【java】学习路线10-权限修饰符详解

    /*关于修饰符:类:public default                public protected default privatesame class          √       ...

  2. 【java】学习路线2-构造、Scanner包导入、字符串操作、数组、引用类型

    请先查看前置知识: [JAVA]基础1-字符串.堆.栈.静态与引用类型 https://www.cnblogs.com/remyuu/p/15990274.html import java.util. ...

  3. 项目实践2:(问卷)用html和css做一个网页

    好家伙,又来写项目了 1.以下是考题,姑且把他理解为甲方吧. 2.以下是附带的题目素材 开干.

  4. openstack中Nova组件简解

    一.Nova组件概述 计算节点通过Nova Computer进行虚拟机创建,通过libvirt调用kvm创建虚拟机,nova之间通信通过rabbitMQ队列进行通信. Nova位于Openstack架 ...

  5. Linux虚拟机 RHEL8.0安装步骤

    一. 创建空白虚拟机 1.打开 VMware 虚拟机软件依次选择新建虚拟机并选择选择"自定义" 自定义功能更加全面,典型就是比较简单的配置 2.选择对应的 VMware 版本,此则 ...

  6. 3款知名RTMP推流模块比较:OBS VS SmartPublisher VS Flash Media Live Encoder

    OBS 功能强大,几乎所有你想要的场景它都有,用起来很顺手.可以将桌面.摄像头.程序窗口通过rtmp推送到流媒体服务器上. 当然如果你是开发者,想基于OBS做二次开发,实现二次产品化的化,难度比较大, ...

  7. 【python】生成一段连续的日期

    date-gen.py import datetime def date_generate(start_date, end_date): print(f'Hi, {start_date}, {end_ ...

  8. 一文搞懂mysql索引底层逻辑,干货满满!

    一.什么是索引 在mysql中,索引是一种特殊的数据库结构,由数据表中的一列或多列组合而成,可以用来快速查询数据表中有某一特定值的记录.通过索引,查询数据时不用读完记录的所有信息,而只是查询索引列即可 ...

  9. 《网页设计基础——HTML注释与CSS注释》

    网页设计基础--HTML注释与CSS注释       一.HTML注释: 格式: <!-- 在此处书写注释 --> 例如: <html> <head> <ti ...

  10. MySQL主从同步报错故障处理记录

    从库上记录删除失败,Error_code: 1032 问题描述:在master上删除一条记录,而slave上找不到,导致报错 Last_SQL_Error: Could not execute Del ...