目录
一、springcloud alibaba整合sentinel
首先创建父maven项目springcloudalibaba
,在pom文件中引入如下依赖,需要注意版本对应关系
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springcloudalibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloudalibaba</name>
<description>springcloudalibaba</description>
<modules>
<module>Order-sentinel</module>
</modules>
<properties>
<java.version>1.8</java.version>
<spring.cloud.alibaba.version>2.2.5.RELEASE </spring.cloud.alibaba.version>
<spring.cloud.version>Hoxton.SR8</spring.cloud.version>
</properties>
<dependencies>
<!-- springboot场景启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 依赖放在dependencyManagement中,子项目引用前需要声明,依赖不在dependencyManagement中,子项目默认直接继承-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
随后在父maven项目下创建子maven项目Order-sentinel
,pom文件如下
<parent>
<groupId>com.example</groupId>
<artifactId>springcloudalibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>org.example</groupId>
<artifactId>Order-sentinel</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
二、采用代码方式设置流控规则
创建一个Controller控制器
@RestController
@Slf4j
public class HelloController {
private static final String RESOURCE_NAME = "hello";
private static final String USER_RESOURCE_NAME = "user";
private static final String DEGRADE_RESOURCE_NAME = "degrade";
// 进行流控
@RequestMapping("/hello")
public String hello(){
Entry entry = null;
try {
// 通过接口路径定义资源名称,sentinel是通过资源进行限制的
entry = SphU.entry(RESOURCE_NAME);
// 被保护的业务逻辑
String str = "hello world";
log.info("========="+str+"===========");
return str;
}catch (BlockException e1){
// 资源阻止访问,被限制或者被降级
// 进行相应的处理操作
log.info("block!");
return "被流控了";
}catch (Exception e2){
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(e2,entry);
}finally {
if(entry!=null){
entry.exit();
}
}
return null;
}
/**
* @description 改善接口中资源定义和被流控后降级的处理方法
* 使用步骤:1、引入依赖
* 2、配置bean
* value 定义资源
* blockHandler 设置流控降级后的处理方法(默认该方法声明在同一个类中)
* 如果不在同一个类中可以通过 blockHandlerClass 设置,且方法需要用static修饰
* @return: com.tuling.sentineldemo.entity.User
*/
@RequestMapping("/user")
@SentinelResource(value = USER_RESOURCE_NAME,blockHandler = "blockHandlerForGetUser")
public User getUser(String id){
return new User("测试");
}
/**
* 注意 :
* 1、一定要public
* 2、返回值一定要和源方法一样,包含源方法参数,可以在参数最后加上异常类
*
*/
public User blockHandlerForGetUser(String id,BlockException e){
e.printStackTrace();
return new User("被流控了!");
}
@PostConstruct
private static void initFlowRules(){
// 流控规则
List<FlowRule> rules = new ArrayList<>();
// 流控
FlowRule rule = new FlowRule();
// 设置受保护的资源
rule.setResource(RESOURCE_NAME);
// 设置流控规则(QPS)
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
rule.setCount(2);
rules.add(rule);
// 流控
FlowRule rule1 = new FlowRule();
// 设置受保护的资源
rule1.setResource(USER_RESOURCE_NAME);
// 设置流控规则(QPS)
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
rule1.setCount(1);
rules.add(rule1);
// 加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
启动项目,浏览器快速访问user和hello接口,会看到被流控的字样,表明流控规则设置生效了
三、结合dashboard控制面板设置规则
3.1、准备工作
先下载安装好对应版本的dashboard,dashboard控制面板下载安装教程请看这篇《sentinel控制面板dashboard的下载安装教程》
随后在项目yml配置文件中进行相应的配置
server:
port: 8860
spring:
application:
name: order-sentinel
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
web-context-unify: false # 打开调用链路
order-sentinel
是对应的服务名,127.0.0.1:8080
为dashboard程序对应的访问ip和端口号
3.2、设置全局异常处理
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
System.out.println("BlockExceptionHandler ========="+e.getRule());
String r = null;
if(e instanceof FlowException){
r = "被流控了";
}else if(e instanceof DegradeException){
r = "被降级处理了";
} else if (e instanceof ParamFlowException) {
r = "热点参数限流了";
} else if (e instanceof SystemBlockException) {
r = "触发了系统保护规则";
} else if (e instanceof AuthorityException) {
r = "授权规则不通过";
}
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getWriter(),r);
}
}
配置了全局BlockException异常处理后,当某个接口被流控或者降级处理导致异常会通过该方法进行处理
当前整个项目目录结构如下
3.3、编写测试接口
controllert层
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private IOrderService orderService;
@GetMapping("/addOrder")
public String addOrder(){
System.out.println("下单成功");
return "下单成功";
}
@GetMapping("/getOrder")
public String getOrder(){
System.out.println("查询订单成功");
return "查询订单成功";
}
@GetMapping("/flow")
// @SentinelResource(value = "flow",blockHandler = "flowBlockHandler")
public String flow(){
return "返回成功";
}
@GetMapping("/flowThread")
// @SentinelResource(value = "flowThread",blockHandler = "flowBlockHandler")
public String flowThread() throws InterruptedException {
Thread.sleep(5000);
return "返回成功";
}
public String flowBlockHandler(BlockException e){
return "被流控了";
}
@RequestMapping("/test1")
public String test1(){
return orderService.getUser();
}
@RequestMapping("/test2")
public String test2(){
return orderService.getUser();
}
@RequestMapping("test3")
public String test3(){
int i = 1/0;
return "返回成功";
}
}
service层
public interface IOrderService {
String getUser();
}
service实现类
@Service
public class OrderServiceImpl implements IOrderService {
@Override
@SentinelResource(value = "getUser",blockHandler = "blockExceptionGetUser")
public String getUser() {
return "查询用户";
}
public String blockExceptionGetUser(BlockException e) {
return "流控";
}
}
最后,启动项目,依次访问各个接口,让各个接口能被dashboard识别并注册到控制面板中
。
3.4、结合dashboard控制面板设置流控规则
进入dashboard控制面板,点击簇点链路,可以看到各个接口并能做出相应的规则设置
3.4.1、流控规则设置并测试——QPS
对/order/addOrder资源进行流控规则设置,输入以上对应的值点击新增按钮,随后请求order/addOrder接口,只要在一秒内请求超过两次后会出现流控效果。QPS就表示为每秒请求数
3.4.2、流控规则设置并测试——线程数
同样为flowThread设置流控规则,由于flowThread接口设置了让线程睡眠5秒,让该接口积攒了超过两条线程后会出现流控效果
3.4.3、流控规则设置并测试——关联模式
现有下单接口addOrder和查询订单接口getOrder,当下单接口请求量高于一定阈值,为了节省服务器性能,保障下单接口正常可用,可以对查询订单接口进行限流,此时可以用到关联模式。对查询订单接口设置如上流控规则,当下单接口每秒请求数超过2次,则查询订单接口不可用。
3.4.4、流控规则设置并测试——链路模式
链路模式是当两个不同的接口调用一个公用的方法,可以对该方法设置流控规则。当公用的方法每秒请求超过一定限制可以通过链路模式对某个调用它的接口进行限流。
这里也可以对test1下的getUser设置规则,效果一样
当getUser请求数每秒超过2次,则/order/test1被流控,/order/test2正常调用
3.4.5、流控规则设置并测试——Warm Up效果
默认使用的是快速失败的效果,快速失败顾名思义就是当此次访问请求不符合设定的规则会直接抛出对应异常,而Warm Up则是预热效果,它是让每秒请求数从一个最小值依次递增到设定的阈值的一个过程,达到对系统的一个预热效果,可以有效防止缓存击穿(例如在系统刚开始启动的时候,缓存中还没有数据,此时涌入大量的请求,这些请求先是从缓存中查询数据,当发现缓存中没有想要的数据时会去请求数据库,数据库由于一次性收到大量的请求导致瘫痪,这就叫缓存击穿,即大量请求跳过缓存,直达数据库层,导致数据库瘫痪),这是因为在预热过程中,由于请求量依次递增,系统有时间去填充缓存中的数据,后续大部分的请求也能从缓存中找到相应的数据,避免了数据库层一次性涌入大量请求的情况。
这里为flow接口设定流控规则,上述规则表示flow接口有两秒的预热时间,在这两秒内进入flow接口的请求依次从最小值递增到阈值10,我们采用测试工具进行测试,设定了15秒执行200次请求,结果如下图,可以发现通过的请求依次递增到设定的阈值。
3.4.6、流控规则设置并测试——排队等待效果
排队等待效果就是当同一时间访问的请求数超出设定的阈值5,只允许前5个请求访问通过,其他请求进行排队等待,等待时间若超过设定的时常则抛出对应的流控异常,为了体现排队等待的效果,先用压测工具测试快速失败下的效果,并发数填30,压测轮数为2。
每经过5秒进行一次并发测试,快速失败效果如下。可发现,在脉冲流量情况下,快速失败仅在流量冲击阶段处理自身能力范围内(设定的阈值5)的请求量,其他请求全部拒绝,且在空闲阶段不做任何处理。
删除快速失败对应的流控规则,新增排队等待的流控规则。
同样的测试方法,发现排队等待效果在流量洪峰之后的空闲时间来处理剩下排队等待的请求,充分利用了空余时间。
3.5、结合dashboard控制面板设置降级规则
降级规则和流控规则的区别就是,流控规则针对访问请求过多而采取的限流策略,主要围绕如何处理超出自身负载的请求数,而降级规则主要围绕如何处理出现问题的接口,以下有三种处理方式。
3.5.1、降级规则设置并测试——慢调用比例
最大RT是用来自定义什么是慢调用,当请求时长超过2000毫秒则为慢调用,上图规则可阐述为 在请求2次flowThread资源,出现慢调用的比例达到0.5,则在接下来的10秒内不会再调用该请求对应的方法,转而执行对应的异常处理(降级处理)方法。当10秒之后会恢复正常请求的调用,若第一次就出现慢调用,则又进入10秒时间的降级处理中,否则按上述规则重新核验(也就是10秒后第一次不是慢调用,第二次是慢调用,第三次直接降级处理,因为比例达到了阈值)。
这里需要采用压测工具来实现具体效果,并发数5,轮数1,随后浏览器访问该接口会发现被降级处理。
3.5.2、降级规则设置并测试——异常比例
为test3设定降级规则,当test3接口短时间内2个请求中出现异常比例达到0.5则会出现熔断,熔断时长为10秒,可以在浏览器中快速刷新查看效果。
3.5.3、降级规则设置并测试——异常数
同样为test3设定如下规则
则在短时间内请求5次,其中有2次异常则会熔断10秒。
3.6、结合dashboard控制面板设置热点规则
热点规则主要针对接口中某个参数进行限制,例如查询商品id为1的请求量很大,可以针对id为1进行流控,其他id正常访问。
先新增热点规则,该规则针对接口索引为0的参数设置了全局的阈值,也就是不论什么id传入进来,每秒中不能超过10次访问量,否则进入异常处理。
随后在热点规则中点击编辑,可以进行高级设置
这里针对参数值为1的请求设定每秒限流为2次,即当传入id为1时,每秒访问3次会被限流,其他值可以每秒访问10次。
注意:
这里可能会出现参数例外项添加无效情况,可以尝试先添加一组数据,请求接口后再次编辑,添加两组数据,最后点编辑删除一组保存即可。