)
基础概念分布式架构中微服务之间的稳定性是非常重要的某一个微服务发生错误可能会带来非常大的损失因此我们需要对这些微服务进行“服务保护”在介绍Sentinel的各个功能之前我们先来了解一下他在我们整一个项目是如何运用如何运行的吧架构原理整一个架构原理如下面第二张图所示但是下图有一些专门的名词在此之前我们需要简单解释资源是一个非常宽泛的概念可以是HTTP请求也可以是一段业务代码就是和“资源”这个名词的本意一样。对于资源主流框架的Web接口都被自动视作资源(比如Controller接口)即使没有被自动识别我们也可以在任意代码上使用“编程式”、“声明式”来定义资源规则也是和名字的本意一样就是我们指定好的特定“规则”用这些“规则”去保护我们的“资源”若某一个微服务违反了规则那就会发生错误若我们有做兜底(像我们之前做的服务熔断)那就返回我们预设的数据。常见的规则同上图所示需要我们后续学习涉及架构原理我们定义好规则后可以由Sentinel的客户端去存储规则存储的位置可以是Nacos的配置中心或者是我们的内存中(只不过存储在内存中重启项目规则也就随同消失而已)同时Sentinel客户端也会推送这些规则到每一个微服务中用这些规则去保护相应的资源。当我们修改规则Sentinel客户端也会同时推送这些新的规则到各个微服务中工作原理如下图所示用一个简单的例子来解释一下吧用户访问一个资源(HTTP请求比如是下订单的请求)我们为该资源(下订单的请求)设置了相应的规则在用户访问该资源的时候Sentinel就会检查相关的规则查看是否违反了我们定义的规则没有违反就直接放行违反了规则那就发生错误对于错误我们可以设置兜底返回。基础场景实践需要提前安装好sentineldashbroad并且启动接下来我们来简单看看Sentinel对资源的监控sentinel对于所有的微服务都要使用先在service层级引入相关依赖接着是对每一个微服务的配置文件中配置连接Sentinel的客户端(这里以order微服务的配置文件为例)红色框住部分为有关于Sentinel的配置dashboard为sentinel的端口下面的eagertrue为微服务一启动Sentinel就可以识别到前置工作准备好后我们就来模拟一下之前我们提及的例子“创建订单”。为了更加清晰地理解“资源”概念我们把创建订单索要调用地方法createOrderWithFeign设置为资源接着我们启动项目然后发一条创建订单地请求请求发送成功来到簇点链路可以看到受Sentinel保护地资源可以看到框架的请求(Controller)以及我们刚刚自己定义的createOrder和OpenFeign的远程调用都被视作资源其中我们可以在该控制台中对这些资源进行有关于留空、熔断、热点、授权 的操作。我们来简单试一试吧我们对请求做流控操作在刚刚的面板中点击“流控”操作我们在阈值类型里面点击“QPS”(每秒可以通过的请求数量)并且把值设置为1作为测试这样我们最多对该请求每秒只能发一次如果多了就会违反我们刚刚设置的“流控”规则从而发生错误我们在请求测试这里试着连续点多次发送请求看看会发生什么可以看到返回了错误信息并且显示是由于违反了flow limiting(流控规则)异常处理Sentinel本质就是针对各个“资源”是否违反“规则”做出相应处理对于“资源”常见的有下图中的四种上面的基础场景我们简单实现了Sentinel对于规则的异常处理但是我们平常的项目中前后端是分离的直接返回像刚刚那样的字符串不太合规我们理想是对于错误应该返回标准化的Result结果集这样前端也好渲染相关错误对于上面的场景属于Web接口的异常处理该异常使用了默认的BlockExceptionHandler返回了指定字符串如果我们想返回指定结果(比如Result结果集)那么我们就得自己编写相关异常处理不用默认的异常处理器了Web接口异常接下来我们来自定义相关BlockExceptionHandler来处理Web接口发生的异常让其返回我们想要的Result结果集首先先来简单的设计Result结果集由于该类后续的微服务都要使用于是我们把它放在公用的莫model模块接着我们来设计异常处理类实现默认Block异常处理接口的同时把它注册到Spring容器中这样出现相关异常时才会被调用让其在捕获到错误时返回我们希望返回的Result结果集测试效果成功返回Result结果集SentinelResource异常还记得这个注解吗这是用于标注任意一段代码为“资源”的注解若该资源违反了“规则”出现了异常我们来看看它处理该资源异常的方式简单来说该资源发生异常后会检测注解是否有BlockHandler与fallback的属性若有则执行“属性所指向的”兜底回调方法若兜底回调方法执行失败则走Springboot的全局异常处理器若Sentinel注解没有定义任何上述属性则注解所标注的资源发生异常的化就会直接走Springboot的全局异常处理器就像下图一样(下图是我们没有自定义任何全局异常处理器这是Springboot自己的全局异常处理器的返回结果)看完上面的解释可能会有点懵其实把上文回想一下我们之前学习OpenFeign的时候末尾我们就实现了一次兜底回调原理其实是一样的在Sentinel注解里面标注“BlockHander/fallback”属性这两个属性的作用就是让其资源违反规则发生异常时去执行我们指定的兜底方法因此这两个属性的属性值就是兜底方法的方法名接下来我们来简单实现一下在我们之前定义好的资源(如这个createOrderWithFeign)中定义属性blockHandler属性值填写兜底回调方法然后再简单编写兜底回调方法最后我们进行相关测试发现返回了我们指定的兜底回调方法的执行结果OpenFeign异常其实我们之前讲解OpenFeign的兜底回调已经讲解过了大致执行流程也和上面的SentinelResouce一样只不过OpenFeign只能用fallback属性去指定兜底回调方法若兜底回调方法执行失败或者没有设置兜底回调方法则直接走Springboot的全局异常处理器来处理异常都是重复内容这里不过多赘述详情可见该博客OpenFeign-CSDN博客规则我们上文学习了异常处理的方法知道了对于常见的资源发生异常怎么处理那么接下来我们只需要再学习这些保护限制资源的规则即可。这样Sentinel总链路资源-规则-异常处理 就完成了流控规则我们前文所举例的例子都是使用流控规则加以限制该规则可以很好的保护我们的资源在完整的项目执行时业务模块是非常占用资源的不仅涉及了各种调用可能还会涉及当量计算当大量请求发出流控规则会控制请求的流量只让一部分请求进入多余的请求直接丢弃这样就不会有大量业务执行从而保证了资源不被耗尽阈值介绍来简单的对流控规则的面板的各个属性进行介绍资源名和名字一样就是资源的名称针对来源从哪里来的请求需要遵守我们制定的流控规则(default默认即可代表全部)阈值类型QPS每秒可通过的请求数量在单机阈值里面填写数字就是每秒可通过几个并发线程数和QPS一样只不过它是通过线程池去统计线程数量的由于涉及到线程因此它没有QPS轻量性能会低一些接下来讲集群模式的阈值阈值类型和上文一样这里不过多赘述集群阈值模式单机均摊集群模式下每台服务器每秒最多可以接收的请求数量总体阈值集群模式下所有服务器每秒最多可以接收的请求数量举个例子假设有三台服务器在单机均摊下均摊阈值为5那么三台服务器每台每秒最多可接收的请求数为5。若在总体阈值下均摊阈值为5则三台服务器每秒一共能接收的请求数量为5按照负载均衡可能为“221”的接收请求流控模式点击流控规则的“高级选项”即可看到我们的流控模式流控模式简单来说就是“如何给资源设置流控规则”同样的流控规则但是流控模式不同所导致的效果也就不同看到这里可能还会有些许疑惑我们通过接下来的例子来简单说明直接这是默认的流控模式我们之前有关于流控规则的所有例子都是使用该流控模式该模式很好理解和名字一样“直接”若你给某一个资源设立了流控规则那么该规则就直接作用于该资源。链路按照下图来简单举一个例子对于“创建订单”服务我们有两种接口访问一种是正常的创建订单接口/create另一种是秒杀订单的创建接口/seckill活动期间秒杀接口肯定很多人访问因此我们希望通过“秒杀”所创建的订单流量不要那么多防止服务器资源占用因此我们这里就可以使用“链路”的流控模式对资源B(createOrder)进行规则限制让从资源C(秒杀)入口访问它的请求做流量限制。这样从资源A去访问资源B可以正常访问但是从资源C去访问资源B就得被规则限制防止爆单了看到这里你可能会想既然这样为什么不直接对资源C使用“直接”流控模式进行规则限制呢这是因为实际项目中调用规则比较复杂上图的例子不够说明“链路”的优势我们来看下图就一目了然了。假设资源C还有其他资源要访问比如资源D(领取优惠券)如果使用“直接”流控模式对C做规则限制那么资源D也会不可控制的受到影响如果我们只希望是从资源C--资源B这里被限流那么使用“链路”的形式来限制资源C--资源B才是最优解刚刚我们提及了“资源C--资源B”这就是“链路”接下来我们来实践一下首先新增秒杀接口依旧调用创建订单业务然后来到yml配置文件设置“统一上下文”web-context-unify的值为false这里简单说一下这个“上下文”是啥他其实就是我们前文说的“链路”如果统一上下文那么就是“一条链子”我们使用“链路”流控规则可能达不成我们想要的结果把统一上下文关闭那么用不同资源的访问就会形成一条条链路资源A--资源B资源C--资源B接着我们来到Sentinel控制台通过两个接口对其资源访问后我们就能看到两个不同的上下文(链路)然后我们对秒杀接口的createOrder使用“链路”的流控模式对其进行流量的规则限制,让其每秒最多通过1个请求注意入口资源要选对接着我们进行测试测试结果如下可以发现当出现异常时触发的是Web接口的异常处理结果很好的说明了我们“链路”流控模式的“看似作用它实则作用它的调用方”关联一句话简单解释舍车保帅以数据库读写操作来举例子吧我们假设“读数据库”是“车”“写数据库”是“帅”。因为实际场景中数据库的“写”的重要性是远远大于“读数据库”的写入失败引出的问题很大因此写是比读重要的“关联”这种模式就是用来针对重要性来进行取舍的规则限制当大量的请求访问读和写操作若不进行流控规则的控制下那么线程池的资源全部耗尽了读或者写都不能保证谁的成功率高当我们把“写”当作“重要的帅”读当作“保帅的车”让读来关联写那么在相同的情况下写的优先级高于读“读”操作就会被流控规则限制从而保证线程池的资源都给到“写”简单来说被关联的就是“重要优先执行的”简单来实践一下先简单写两个模拟读写的接口然后在Sentinel中让“读”去关联“写”两边同时发大量的请求发现“读”被流控规则所限制而写还能正常运行流控效果一共有三种效果接下来我们来简单说明快速失败这也是我们之前例子用到的流控效果其作用就是当资源访问不合规(违反了流控规则比如每秒请求量超出QPS)那么就会快速返回一个BlockException因此我们之前的异常处理(如SentinelResource异常)我们写的属性就是BlockException来处理相关异常其表现出来的效果就是多余的请求直接丢掉不给访问资源Warmup--预热/冷启动可以类比踩油门我们开车时就算一脚把油门踩到底速度也不会直接打到我们踩得速度而是会逐步达到那个速度Warmup也同理我们可以设置QPS和PeriodPeriod是预热时间如下图所示QPS设置为3Period设置为3那么每秒可通过的请求就是逐步到达3第一秒是1个第二秒是2个第三秒可通过的就变成最大值3了匀速排队简单来说用处就是“拿延迟换通过的请求数量”这里不仔细介绍它的工作机制了我们只需要知道在什么时候使用它业务上宁可慢一点也不要失败发短信、支付回调、写库、调老系统→ 用匀速排队。熔断规则背景微服务项目中微服务之间的调用关系非常复杂可以参照一下下图此时若有一个微服务出现了问题比如D出现问题那么该调用链路的所有微服务(ABGF)都要受到影响.为了防止出现一直等导致服务雪崩我们希望调用链路有关于D的直接不调用D了直接返回预设好的结果比如G F本来要调用D但是D出现问题那直接不调用D直接返回预设结果实现这种效果我们就需要“熔断器”的帮助在熔断降级这种策略中它是一种“保护自身”的策略因此这种策略的相关配置都是配置在调用方也就是G F断路器是熔断降级的基础可以把它看作物理电路上的“开关”有“闭合”、“打开”、“半开”三种状态接下来我们来简单介绍一下熔断器的工作原理断路器工作原理首先先来介绍一下熔断器的三种状态吧打开就像物理电路一样当开关处于“打开”状态那么就会“断路”则调用请求就不会发送闭合和打开一样闭合了电路就流通了那么调用请求也能正常发送半开属于两者之间的状态半开状态下只会让这些调用请求去通过几个查看这些调用请求是否成功调用待会介绍的断路器工作就会根据半开的结果来决定熔断器要把状态更新为“打开”还是“闭合”接着我们来介绍断路器的工作原理通常来说项目中的断路器默认是关闭的让调用可以成功运行但是当被调用方出现异常时我们的断路器就得打开那要怎么打开呢我们可以通过判定请求状态的方式来决定断路器是否打开判断标准常见的有以下三种慢调用比例、异常比例、异常数这里以“慢调用比例”来举例吧我们先简单介绍以下慢调用比例和名字一样当一个调用请求迟迟拿不到结果执行的非常慢我们就吧这种请求称作“慢(调用)请求”实现这个我们可以用一个“统计时长”来看这个请求执行了多久若超出规定时间我们就把它标记为“慢请求”。再来说“比例”请求肯定不是一条而是大量请求打过来当一定数量的请求都被标记为“慢请求”达到一定的比例(该比例我们也可以自己设定)那么断路器就根据“慢调用比例”的判定方式从而打开打开之后在“熔断时长”(就是一段规定好的时间)内剩下的请求就不会再去执行远程调用从而直接返回预设结果。当熔断时长结束后断路器就变成“半开”的状态放行一些少量的请求看看是否可以成功调用若还是调用失败那继续让断路器处于“打开”状态并且打开状态的维持“熔断时长”的时间。若半开状态下试放行请求都通过了那么断路器会关闭让其链路正常远程调用从而回到一开始的循环慢调用比例上面说了那么多我们来实践以下这个熔断规则用“创建订单”为例给我们的调用方(订单微服务)设置熔断规则最大RT就是我们前文说的对于“慢请求”的判定时间超过这个时间就会被判定熔断时长熔断的时间比例阈值我们前文说的预设的比例若慢请求打到这个比例则断路统计时长在一定时间内来看慢比例是否打到比例阈值的“一定时间”最小请求数一定时间内最少要发X个请求才开始统计比例熔断规则面对的对象是“调用方微服务”因此你可以在该微服务的任意位置(资源)去设立该熔断规则建议设立在有做兜底方案的资源设立我设立在了下方红框区域该资源是用于远程调用的Feign我们之前就对其做了兜底方案接下来我们稍微修改一下product微服务让其睡眠2s来模拟响应慢的场景来到接口测试模拟发送20条请求可以看到前面请求返回了正常结果但是响应速度慢当慢请求打到一定比例时断路器直接打开返回兜底方法的数据异常比例/异常数和名字一样就是当异常比例打到某一个预设值那么断路器就会被打开在实践之前我想先声明清楚一个概念微服务项目中无论返回什么数据肯定时越快越好。我们以远程调用发生错误为例子吧还记得我们之前给远程调用Feign做了兜底回调方法吗当Feign调用发生错误时那么就返回兜底回调假设这个错误是发生在被调用的product微服务上。那么这个兜底回调执行的流程就是Feign--product(出现异常)--兜底回调那像上面这样执行兜底回调还要经过product总体来讲速度就不是很快还有提升的地方因此我们熔断规则的优势就体现出来了由于有断路器的存在当违反了熔断规则断路器直接打开兜底回调就不会经过product微服务这样我们返回数据的速度又快了一些返回速度越快服务雪崩发生的概率才越小我们简单了解一下熔断规则即可由于原理和之前的“慢调用比例”相同这里就不过多实践了异常数的熔断规则热点规则热点就是很多人要访问的资源因此对热点做规则其实本质上还是一种“流控规则”只不过热点规则中可以对请求携带的特定参数做规则限制我们可以把它简单看作更加“高级”的流控规则我们直接使用实践来讲解我们来一次完成以下需求前置须知实践之前须知web接口是默认不支持热点规则的如果想要给web接口实现热点规则那么就必须使用SentinelResouce注解把该Web接口标注为资源并且资源名不可以和Sentinel自动识别该Web资源的名称一致因此对于秒杀接口我们先得这样设置并且写好它的兜底方法这样我们对于Web接口就能从这里添加热点规则了实践首先来满足需求1、2因为他们是针对同一个参数做需求的先来满足第一个需求吧添加热点规则参数索引填1是因为我们刚刚的接口中userId是第二个参数单机阈值就是QPS的值那这样需求1的规则就添加完成接下来到需求2让userId为6的请求不限制QPS在刚刚的页面点击高级选项然后我们就可以针对参数索引为1(也就是userId)进行特例操作不限制QPS那我们就把它的QPS设置为一个非常大的数字这样就相当于不限制QPS了接下来我们来满足需求3不让productId为666的请求访问也是很简单不让访问那我们直接吧阈值设置为0即可除了productId为666的其余的可以正常访问接下来我们来测试一下结果userid为6时就可以不受QPS限制当为其他值时访问就会收QPS限制当productId为666直接访问失败当productId为其他任意值访问成功