之八 2流高手速成记:基于Sentinel实现微服务体系下的限流与熔断

我们接上回
上一篇中 , 我们进行了简要的微服务实现,也体会到了SpringCloudAlibaba的强大和神奇之处
我们仅改动了两个注释 , 其他全篇代码不变,原来的独立服务就被我们分为了provider和consumer两个独立部分,二者各司其职,分工明确
篇尾我们留下了一个疑问,consumer对provider有依存关系 , 如果下游的provider出现异常,上游的consumer如何自处?
其实这种单一的上下游关系仅是微服务日常运转下各种复杂情境的一个缩影,真实的生产环境下各个微服务节点可能会形成更为复杂的依赖关系,那我们该如何解决这些问题?
本节中我们引入Sentinel框架:
什么是 Sentinel?

在基于 SpringCloud 构建的微服务体系中,服务间的调用链路会随着系统的演进变得越来越长,这无疑会增加了整个系统的不可靠因素 。
在并发流量比较高的情况下,由于网络调用之间存在一定的超时时间,链路中的某个服务出现宕机都会大大增加整个调用链路的响应时间,而瞬间的流量洪峰则会导致这条链路上所有服务的可用线程资源被打满,从而造成整体服务的不可用,这也就是我们常说的 “雪崩效应” 。
而在微服务系统设计的过程中 , 为了应对这样的糟糕情况,最常用的手段就是进行 ”流量控制“ 以及对网络服务的调用实现“熔断降级” 。因此,Sentinel 就因运而生了 。
Sentinel 是一款面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来保障服务的稳定性,核心思想是:根据对应资源配置的规则来为资源执行相应的流控/降级/系统保护策略 , Sentinel 的主要特性如下图:
之八 2流高手速成记:基于Sentinel实现微服务体系下的限流与熔断

文章插图
以上内容来源于Sentinel官方文档,看到这一大堆不明所以的名词接释,新入坑的同学可能已经劝退了 。。
接下来我们依然沿用前几篇的思想,先不要管这一堆的理论,先来看怎么用,实际运用过程中如何达到我们预期的效果,返回来再看这些名词,你自然会有更清晰的认知
Sentinel怎么用?Sentinel的使用分为两部分 —— 代码和控制台
首先我们先下载Sentinel控制台:https://github.com/alibaba/Sentinel/tags , 本节中我使用的是Sentinel1.8版本
下载编译好的jar包,而后执行命令行:
java -jar .\sentinel-dashboard.jar --server.port=9999之后我们打开浏览器,输入地址:http://127.0.0.1:9999/
打开Sentinel控制台如图所示:
之八 2流高手速成记:基于Sentinel实现微服务体系下的限流与熔断

文章插图
整个面板是空白的,什么都没有?!不用着急,我们继续往下看代码的部分
上一节我们提到了微服务环境下consumer工程存在的隐患问题,接下来我们就要消除这个隐患!
dubbo-nacos-consumer工程引入Sentinel依赖库<!-- 引入sentinel依赖 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>这一步肯定是必须的,没什么好解释的
添加Sentinel相关配置
之八 2流高手速成记:基于Sentinel实现微服务体系下的限流与熔断

文章插图
在SpringCloudAlibaba体系下,我们以Nacos为配置中心,所以我们编辑Nacos中的相关配置即可,非常方便
我把代码文本也贴出来,便于大家自行复制
# sentinel看板配置spring.cloud.sentinel.transport.dashboard = 127.0.0.1:9999# 开启对sentinel看板的饥饿式加载 。sentinel默认是懒加载机制,只有访问过一次的资源才会被监控,通过关闭懒加载,在项目启动时就连接sentinel控制台spring.cloud.sentinel.eager = true这两句的主要意图在于将Consumer工程关联到Sentinel控制台,便于后续通过控制台统一管控
改造原有的PersonController我们先创建一个ViewObject
之八 2流高手速成记:基于Sentinel实现微服务体系下的限流与熔断

文章插图
代码如下:
package com.example.dubbonacosconsumer.vo;import com.example.dubbonacosapi.model.Person;import java.util.List;public class SelectRetVo {private List<Person> persons;private String error;public List<Person> getPersons() {return persons;}public void setPersons(List<Person> persons) {this.persons = persons;}public String getError() {return error;}public void setError(String error) {this.error = error;}}然后是PersonController的改造
package com.example.dubbonacosconsumer.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;import com.alibaba.csp.sentinel.slots.block.BlockException;import com.example.dubbonacosapi.model.Person;import com.example.dubbonacosapi.service.PersonService;import com.example.dubbonacosconsumer.vo.SelectRetVo;import org.apache.dubbo.config.annotation.DubboReference;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;@RestController@RequestMapping("/person")public class PersonController {@DubboReferencePersonService service;@PostMapping("/insert")public Integer insert(Person person) {return service.insert(person);}@PostMapping("/update")public Integer update(Person person) {return service.update(person);}@PostMapping("/delete")public Integer delete(int id) {return service.delete(id);}@GetMapping("/select")@SentinelResource(value = "https://www.huyubaike.com/biancheng/person/select", blockHandler = "selectBlock")public SelectRetVo select() {SelectRetVo vo = new SelectRetVo();vo.setPersons(service.select());vo.setError("ok");return vo;}public SelectRetVo selectBlock(BlockException e) {SelectRetVo vo = new SelectRetVo();vo.setPersons(new ArrayList<Person>());vo.setError(“当前访问人数过多,请稍后...”);return vo;}}

推荐阅读