文章目录
一、概述
1.1 在实际开发过程中,能用起来!
1.2 常遇到的一些问题解答
1.2.1 sentinel是什么?
1.2.2 sentinel类似的产品有哪些?hystrix
1.2.3 限流算法?优点? 缺点?
1.2.4 sentinel默认的限流算法?特性?滑动窗口
1.2.5 sentinel能解决突发流量吗?不能,为什么? 因为它底层是滑动窗口
1.2.6 sentinel的阀值应用类型? QPS, 线程数,调用关系
1.2.7 sentinel可以做集群限流吗? 可以
1.2.8 Sentinel限流规则中有哪些限流模式? 直连,关联,链路
1.3 Sentinel是什么?
【官网】: sentinelguard.io/zh-cn/
Sentinel是面向分布式服务架构的高可用防护组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助用户保障微服务的稳定性。
1.4 Sentinel基本概念?
资源:(sentinel都是针对资源进行限流, 资源可以是方法名)
资源是Sentinel的关键概念。它可以是java应用程序中任何内容(例如,由应用程序提供的服务, 或由应用程序调用其他应用提供的服务,甚至可以是一段代码。)
只要通过Sentinel API 定义的代码,就是资源,能够被Sentinel保护起来,大部分情况下,可以使用方法签名URL,甚至服务名称作为资源名来标识资源
规则: 围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整;
二、sentinel使用和限流规则
2.1 限流
2.1.1 为什么要限流??
微服务侧重两个维度:高可用、数据一致性
保证高可用措施:
- 缓存
- 降级(将某个服务业务功能简单化) 举例:一个请求过来,处理链路多且复杂,遇到服务器
宕机,不能直接返回给客户看不懂的信息,给出
友好的错误提示;比如try-catch()就可以实现降级- 限流:在某一个时间段内只允许指定数量的请求过来, 与熔断降级(hystrix, 当请求量特别大的时候,可能会造成服务的宕机)需要区分清楚
2.2 限流算法
- 计数器(固定窗口)
思想:计数器算法是使用计数器在周期内累加访问次数, 当达到设置的限流值时, 触发限流策略; 下一个周期开始时,进行清零, 重新计数。
此算法实现起来不管是单机环境还是分布式环境都是非常的简单,使用redis的incr原子自增性和线程安全即可轻松实现;
如上图所示:1分钟内限制100个请求,超过则触发限流策略
问题: 它无法解决临界问题(假设1分钟,1分钟之内阈值100,若前0:59秒之内没有什么流量,但是最后1秒钟突发100个请求, 过了1分钟之后清零,后面1:00-2:00前1秒钟突发100个请求),也就是2秒钟来了200个请求,这对服务器压力会非常的大(因为我的系统可能能承受2秒之内200个请求,但是如果这两百个请求都集中在2秒钟之内,则我的系统可能就承受不住);
- 滑动窗口(sentinel限流算法默认的算法)
它是在固定窗口之上的优化;
思想: 滑动窗口算法是将时间周期分为N个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期
如下图,假设时间周期为1min, 将1min再分为2个小周期,统计每个小周期的访问数量,则可以看到,第一个时间周期内,访问量为75, 第二个时间周期内,访问数量为100, 超过100的访问则被限流掉了;
此算法就可以解决临界问题(因为滑动窗口,它根据时间滑动删除过期的小周期,比如上图中的0:00-0:30, 而只看0:30-current time时间范围之内是否请求数是否超过最大请求数100)
- 漏桶算法
跟令牌算法刚好相反, 设计思想跟阻塞队列一致
**解析:**上游可以不限制流量,设置漏桶的大小,当请求数超过漏桶大小时溢出(拒绝请求),而消费端以匀速消费;
思想:漏桶算法是首先想象有一个木桶,桶的容积是固定的,当有请求到来时,先放到木桶中,处理请求的worker以固定的速度从木桶中取出请求进行处理。
若请求量太大超过木桶的容积,则直接返回请求频率超限的错误码或者页面
问题: 木桶流入请求的速率是不固定的,但是流出的速率是恒定的。这样的话能保护系统资源不被打满,但是面对突发流量时会有大量请求失败,不适合电商抢购和微博出现热点事件等场景的限流;
- 令牌桶算法
跟漏桶算法刚好相反
解析: 从客户端拿到的请求需要拿到令牌才会去处理请求,请求流量是可以控制(每次请求都需要拿到令牌token),但是处理请求的速率不会控制(开启多少个线程去消费取决于使用方)
思想:令牌桶是反向的“漏桶”, 它是以恒定的速度往木桶里面加入令牌,木桶满了则不再加入令牌。服务收到请求时尝试从木桶中取出一个令牌,如果能够得到令牌则继续执行后续的业务逻辑,如果没有得到令牌,等待获取token,不继续执行后续的业务逻辑;
能解决突发流量问题;
三、限流框架基本使用
guava底层用的是令牌桶算法
前置一段代码热身,如下:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
/**
* 令牌桶算法
*/
public class RateLimiterDemo {
private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/*线程数*/
private static final int THREAD_COUNT = 25;
public static void main(String[] args) {
RateLimiterDemo rateLimiterDemo = new RateLimiterDemo();
rateLimiterDemo.testRateLimiter1();
}
public void testRateLimiter1(){
/*令牌桶算法 TPS:1*/
RateLimiter rateLimiter = RateLimiter.create(1);
Thread[] ts = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++){
ts[i] = new Thread(new RateLimiterThread(rateLimiter), "rateLimitThread-");
}
for (int i = 0; i < THREAD_COUNT; i++){
ts[i].start();
}
}
public class RateLimiterThread implements Runnable{
private RateLimiter rateLimiter;
public RateLimiterThread(RateLimiter rateLimiter){
this.rateLimiter = rateLimiter;
}
@Override
public void run() {
rateLimiter.acquire(1);
System.out.println(Thread.currentThread().getName() + "获取到了令牌,时间 = "+ FORMATTER.format(new Date()));
}
}
}
演示结果:
上述是使用了令牌桶对其进行限流的控制
3.1、Sentinel控制台
【参见】https://sentinelguard.io/zh-cn/docs/dashboard.html
这里下载:sentinel-dashboard-1.8.0.jar
- 启动服务
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
- 操作过程阐述
在使用sentinel的时候, 首先你要初始化sentinel的流控规则 ;
- 首先设置资源名
- 设置限流规则:QPS、线程数
- 设置阈值
- 设置控制策略
- 针对具体的客户端进行限流
- 流控模型
API操作则一定要通过try{}catch(Exception){} 才能实现限流
3.2、Sentinel流控规则设置
3.2.1 流控规则设置1
流控规则设置代码演示:
- OrderController
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class OrderController {
@Autowired
private OrderQueryService orderQueryService;
@RequestMapping("/getOrder1")
public String queryOrder1(@RequestParam("orderId") String orderId){
return orderQueryService.queryOrderInfo(orderId);
}
/**
* 限流实现方式一:抛出异常的方式定义资源
* @param orderId
* @return
*/
@RequestMapping("/getOrder2")
public String queryOrder2(@RequestParam("orderId") String orderId){
Entry entry = null;
/*资源名*/
String resourceName = "zyping";
try{
/*entry 可以理解成入口登记,这一步就进行限流*/
entry = SphU.entry(resourceName);
/*被保护的逻辑, 这里为订单查询接口*/
return orderQueryService.queryOrderInfo(orderId);
}catch (BlockException blockException){
/*接口被限流的时候,会进入到这里*/
blockException.printStackTrace();
return "接口限流,返回空";
}finally {
//SphU.entry(xxx) 需要与 entry.exit() 成对出现,否则会导致调用链记录异常
if (entry != null){
entry.exit();
}
}
}
/**
* 限流实现方式二:注解定义资源
* @param orderId
* @return
*/
@RequestMapping("/getOrder3")
public String queryOrder3(@RequestParam("orderId") String orderId){
return orderQueryService.queryOrderInfo3(orderId);
}
}
- OrderQueryService
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.stereotype.Component;
@Component
public class OrderQueryService {
public String queryOrderInfo(String orderId) {
System.out.println("获取订单信息:"+ orderId);
return "return OrderInfo : "+ orderId;
}
/**
* 订单查询接口, 使用Sentinel 注解实现限流
* @param orderId
* @SentinelResource的value是资源名
* @return
*/
@SentinelResource(value = "queryOrderInfo3", blockHandler = "handleFlowQpsException", fallback = "queryOrderInfo3Fallback")
public String queryOrderInfo3(String orderId){
/*模拟接口运行时抛出代码异常*/
if ("000".equals(orderId)){
throw new RuntimeException();
}
System.out.println("获取订单信息:" + orderId);
return "return OrderInfo3 :" + orderId;
}
/**
* 订单查询接口抛出限流或降级时的处理逻辑
*
* 注意:方法参数、返回值要与原函数保持一致
* @return
*/
public String handleFlowQpsException(String orderId, BlockException e){
e.printStackTrace();
return "handleFlowQpsException for queryOrderInfo3: "+ orderId;
}
/**
* 订单查询接口运行时抛出的异常提供fallback处理
*
* 注意: 方法参数、返回值要与原函数保持一致
* @return
*/
public String queryOrderInfo3Fallback(String orderId, Throwable e){
e.printStackTrace();
return "queryOrderInfo3Fallback for queryOrderInfo3: "+ orderId;
}
}
- SpringBootSentinelApplication
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.ArrayList;
import java.util.List;
@SpringBootApplication
public class SpringBootSentinelApplication {
public static void main(String[] args) {
/*启动时初始化流控规则*/
initFlowQpsRule();
SpringApplication.run(SpringBootSentinelApplication.class, args);
}
public static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource("queryOrderInfo3");
// QPS限流
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
// QPS控制在2以内
rule1.setCount(2);
rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
rule1.setLimitApp("default");
rule1.setStrategy(RuleConstant.STRATEGY_DIRECT);
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
// public static void initFlowQpsRule(){
// List<FlowRule> rules = new ArrayList<>();
// FlowRule rule1 = new FlowRule();
// rule1.setResource("zyping");
// /*QPS 限制 设置限流规则*/
// rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
// /*QPS 控制在2s以内 设置阈值
// * QPS=2:表示1秒钟之内可以接入2个请求*/
// rule1.setCount(2);
// /*设置控制策略*/
// rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
// /*针对具体的客户端进行限流*/
// rule1.setLimitApp("default");
// /*流控模型*/
// rule1.setStrategy(RuleConstant.STRATEGY_DIRECT);
// rules.add(rule1);
// /*核心代码: 加载规则*/
// FlowRuleManager.loadRules(rules);
// }
}
- 演示结果:
1秒钟请求数超过2个则:
3.2.2 流控规则设置2
集成SpringBoot 使用到了SPI机制
- InitFunc 接口源码
package com.alibaba.csp.sentinel.init;
/**
* @author Eric Zhao
*/
public interface InitFunc {
void init() throws Exception;
}
- 引入Jar包
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.2.0.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
<version>1.8.0</version>
</dependency>
<!--<dependency>
<!–单机版本–>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
- nacos配置
[
{
//对应下文中TestServer中doTest方法上的@SentinelSource的value值
"resource":"doTest",
//grade = 1以QPS进行限流
//QPS = 5(1秒钟最多5次)
"grade":1,
"count":5,
"clusterMode":false,
"controlBehavior":0,
"strategy":0
},{
"resource":"/hello/{name}",
"grade":1,
"count":5
}
]
配置详情
- FlowRuleInitFunc
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.util.List;
public class FlowRuleInitFunc implements InitFunc {
private final String nacosAddress = "196.168.65.224:8848";
private final String groupId = "SENTINEL_GROUP";
private final String dataId = "--flow-rules";
private final String clusterServerHost = "localhost";
private final int clusterServerPort = 9999;
private final int requestTimeOut = 20000;
private final String appName = "springboot-sentinel";
/**
* 参数初始化
* @throws Exception
*/
@Override
public void init() throws Exception {
// loadClusterConfig();
registerFlowRule();
}
/**
* 集群模式
*/
// private void loadClusterConfig(){
// ClusterClientAssignConfig assignConfig = new ClusterClientAssignConfig();
// //放到配置中心
// assignConfig.setServerHost(clusterServerHost);
// assignConfig.setServerPort(clusterServerPort);
//
// ClusterClientConfigManager.applyNewAssignConfig(assignConfig);
//
// ClusterClientConfig clientConfig = new ClusterClientConfig();
// /*放到配置中心*/
// clientConfig.setRequestTimeout(requestTimeOut);
// ClusterClientConfigManager.applyNewConfig(clientConfig);
// }
private void registerFlowRule(){
/**
* 动态配置文件
*/
ReadableDataSource<String, List<FlowRule>> flowRuleDs =
new NacosDataSource<List<FlowRule>>(nacosAddress, groupId, appName + dataId,
source-> JSON.parseObject(source, new TypeReference<List<FlowRule>>(){}));
/*将配置中心的配置注册到property中*/
FlowRuleManager.register2Property(flowRuleDs.getProperty());
}
}
- SentienlController
@RestController
public class SentinelController {
@Autowired
TestService testService;
@GetMapping("/hello/{name}")
public String sayHello(@PathVariable("name") String name){
return testService.doTest(name);
}
}
- TestService
@Service
public class TestService {
@SentinelResource(value = "doTest",blockHandler ="blockHandler",fallback = "fallback") //声明限流的资源
public String doTest(String name){
return "hello , "+name;
}
public String blockHandler(String name, BlockException e){ //降级,限流触发的
return "被限流了";
}
public String fallback(String name){ //熔断触发的
return "被降级了";
}
}
演示结果:
3.2.3 流控规则设置3
初始化文件在配置文件中 , 不用SPI
- application.properties
# 应用名称
spring.application.name=springboot-sentinel
# 应用服务 WEB 访问端口
server.port=8080
# spring 静态资源扫描路径
spring.resources.static_locations=classpath:/static/
# Sentinel 控制台地址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:7777
# 取消Sentinel控制台懒加载
# 默认情况下 Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包
# 配置 sentinel.eager=true 时,取消Sentinel控制台懒加载功能
spring.cloud.sentinel.eager=true
spring.cloud.sentinel.datasource.ds1.nacos.serverAddr=119.91.65.224:8848
spring.cloud.sentinel.datasource.ds1.nacos.dataId=springboot-sentinel--flow-rules
spring.cloud.sentinel.datasource.ds1.nacos.groupId=SENTINEL_GROUP
# FlowRule , json
spring.cloud.sentinel.datasource.ds1.nacos.dataType=json
spring.cloud.sentinel.datasource.ds1.nacos.ruleType=flow
spring.cloud.sentinel.datasource.ds1.nacos.username=nacos
spring.cloud.sentinel.datasource.ds1.nacos.password=nacos
- 依赖
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.2.0.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
</properties>
<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>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.0</version>
</dependency>
<!--<dependency>
<!–单机版本–>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<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.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- Nacos配置中心
[
{
"resource":"doTest2",
"grade":1,
"count":2,
"clusterMode":false,
"controlBehavior":0,
"strategy":0
},{
"resource":"/hello/{name}",
"grade":1,
"count":5
}
]
- SentinelController
@RestController
public class SentinelController {
@Autowired
TestService testService;
@GetMapping("/hello/{name}")
public String sayHello(@PathVariable("name") String name){
return testService.doTest(name);
}
}
- TestService
@Service
public class TestService {
@SentinelResource(value = "doTest2",blockHandler ="blockHandler",fallback = "fallback") //声明限流的资源
public String doTest(String name){
return "hello , "+name;
}
/*public String blockHandler(String name, BlockException e){ //降级,限流触发的
return "被限流了";
}
public String fallback(String name){ //熔断触发的
return "被降级了";
}*/
public String blockHandler(String name, BlockException e){ //降级,限流触发的
return "被限流了";
}
public String fallback(String name){ //熔断触发的
return "被降级了";
}
}
演示结果:
四、动态限流
- poll方式
- push方式
4.1 、集群限流
sentinel可以做集群限流,但是不建议使用sentinel做集群限流(引入token-server(需要单独部署 或者嵌入代码));
token-server官网都提供了代码;
- 抽象解析:
- token-server代码实现
利用SPI 机制
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-cluster-server-default</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies>
- FlowRuleInitFunc
public class FlowRuleInitFunc implements InitFunc{
private final String nacosAddress="192.168.8.139:8848";
private final String groupId="SENTINEL_GROUP";
private String dataId="-flow-rules";
@Override
public void init() throws Exception {
ClusterFlowRuleManager.setPropertySupplier(namespace->{
ReadableDataSource<String,List<FlowRule>> flowRuleDs=
new NacosDataSource<List<FlowRule>>(nacosAddress,groupId,namespace+dataId,
source-> JSON.parseObject(source,new TypeReference<List<FlowRule>>(){}));
return flowRuleDs.getProperty();
});
}
}
- ClusterServer
import com.alibaba.csp.sentinel.cluster.server.ClusterTokenServer;
import com.alibaba.csp.sentinel.cluster.server.SentinelDefaultTokenServer;
import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager;
import com.alibaba.csp.sentinel.cluster.server.config.ServerTransportConfig;
import java.util.Collections;
public class ClusterServer {
public static void main(String[] args) throws Exception {
ClusterTokenServer tokenServer=new SentinelDefaultTokenServer();
//手动载入namespace和serverTransportConfig的配置到ClusterServerConfigManager
//集群限流服务端通信相关配置
ClusterServerConfigManager.loadGlobalTransportConfig(
new ServerTransportConfig().setIdleSeconds(600).setPort(9999));
//加载namespace集合列表() , namespace也可以放在配置中心
ClusterServerConfigManager.loadServerNamespaceSet(Collections.singleton("App-Test"));
tokenServer.start();
//Token-client会上报自己的project.name到token-server。Token-server会根据namespace来统计连接数
}
}
五、熔断、限流
熔断针对的是后端的请求,限流是针对前端的请求
/**
* blockHandler :限流之后执行的方法;
* fallback :熔断之后回调的方法
*/
@SentinelResource(value = "doTest3",blockHandler ="blockHandler",fallback = "fallback")
5.1 代码演示
- GoodsQueryService
调用接口出现异常次数超过N次的时候, 进行熔断;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@Component
@Slf4j
public class GoodsQueryService {
private static final String KEY = "queryGoodsInfo2";
/**
* 模拟商品查询接口
*
* @param spuId
* @return
*/
@SentinelResource(value = KEY, blockHandler = "blockHandlerMethod", fallback = "queryGoodsInfoFallback")
public String queryGoodsInfo(String spuId) {
// 模拟调用服务出现异常
if ("0".equals(spuId)) {
throw new RuntimeException();
}
return "query goodsinfo success, " + spuId;
}
public String blockHandlerMethod(String spuId, BlockException e) {
log.warn("queryGoodsInfo222 blockHandler", e.toString());
return "queryGoodsInfo error, blockHandlerMethod res: " + spuId;
}
public String queryGoodsInfoFallback(String spuId, Throwable e) {
log.warn("queryGoodsInfo222 fallback", e.toString());
return "queryGoodsInfo error, return fallback res: " + spuId;
}
/**
* GoodQueryService执行方法前需要执行的方法
*/
@PostConstruct
public void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource(KEY);
// 80s内调用接口出现异常次数超过5的时候, 进行熔断
rule.setCount(5);
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
rule.setTimeWindow(80);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
}
熔断:DegradeRuleManager
限流:FlowRuleManager
六、总结
6.1 限流规则有哪几个?
- QPS数进行限流 grade = 1 ;count=5
- 线程数进行限流, grade =0;
- 可以根据调用关系
flowRule
设置关联性的资源
rule.setRefResource(“xxx”)
6.2 控制策略
rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
- 直接拒绝(默认的)(CONTROL_BEHAVIOR_DEFAULT = 0;)
- 慢启动:(CONTROL_BEHAVIOR_WARM_UP = 1)当限流规则设置的QPS数量是10000,但是平常处理的请求数都是很小, 需要逐步增加;
- 匀速器模式(匀速排队CONTROL_BEHAVIOR_RATE_LIMITER = 2)超出阈值不拒绝,而是等待
6.3 限流模式
rule1.setStrategy(”“);
- 直连(STRATEGY_DIRECT = 0)(rule1.setResource= “queryOrderInfos”)
只有调用这个资源时才会进行限流,即只有进入到
@SentinelResource(value=“queryOrderInfos”)
public String query(){}
方法时才进行限流- 关联(STRATEGY_RELATE = 1)
当关联的资源达到限流条件时,开启限流,适合做应用让步。例如为了一个查询的接口添加关联流控,关联资源为一个更新的接口,当更新的接口达到阈值时,开启查询接口的限流,为更好接口让步服务器资源- 链路(STRATEGY_CHAIN = 2)
当从某个接口过来的资源达到限流条件时,开启限流
sentinel主要是用来做限流,熔断用的不多