Sentinel 使用-单机版
- pom.xml
<!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-core -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.6.3</version>
</dependency>
- @SentinelResource标记,blockHandler指定熔断后的处理方法
- 注意:创建熔断规则时指定的资源名称必须和@SentinelResource#value的值对应,不然被拒掉的请求没有降级的业务服务
@GetMapping("/test/sentinel")
@SentinelResource(value = "createOrder", blockHandler = "blockMethod")
public Object testSentinel(@AuthenticationPrincipal String username) {
log.info("user is " + username);
return username;
}
public Object blockMethod(@AuthenticationPrincipal String username, BlockException exception) {
log.info("block by " + exception.getClass().getSimpleName());
return username;
}
- 配置限流和熔断规则
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.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhang3
* className org._33web.api.order.sentinel.SentinelConfig
* date 2019/12/29 0029 22:59
* version 1.0
* description (this is an class) Sentinel 资源管理
*/
@Component
public class SentinelConfig implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
//限流规则
FlowRule rule = new FlowRule();
rule.setResource("createOrder");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(1);
List<FlowRule> rules = new ArrayList<>();
rules.add(rule);
FlowRuleManager.loadRules(rules);
//熔断规则
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource("createOrder");
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
degradeRule.setCount(10);
degradeRule.setTimeWindow(10);
List<DegradeRule> degradeRules = new ArrayList<>();
degradeRules.add(degradeRule);
DegradeRuleManager.loadRules(degradeRules);
}
}
Sentinel-dashboard
-
简述: Sentinel 是面向分布式服务架构的轻量级流量控制组件
-
可视化、集中化、动态化管理分布式服务架构下微服务的限流、熔断、降级规则
github 下载源代码
选择非master分支的1.7版本
sentinel-dashboard.jar 执行命令
- 可以自定义成docker 镜像
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.6.3.jar
web控制台
- sentinel-dashboard web 控制台: http://ip:8080/#/login ,默认账号密码:sentinel/sentinel
controller改造
-
将FlowControllerV1.java 的请求导向FlowControllerV2.java
-
将DegradeController.java 的请求导向DegradeControllerV2.java
-
参考代码下载 http://119.23.50.122/ms/sentinel-dashboard/v2.zip
Sentinel-dashboard Docker安装
docker run -d \
-p 8000:8000 \
-v /etc/localtime:/etc/localtime:ro \
--name group_sentinel-dashboard-server_1 docker.io/sentinel-dashboard-server:1.0
配置中心 zookeeper 篇
修改sentinel-dashboard
基本配置
- 修改pom.xml 注释掉 scope#test
<!--for Zookeeper rule publisher sample-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
<!--<scope>test</scope>-->
</dependency>
- 修改application.properties
#登录用户名和密码
# If auth.enabled=false, Sentinel console disable login
auth.username=root
auth.password=root
# 端口
server.port=8000
- 复制代码
将/test/java/com.alibaba.csp.sentinel.dashboard.rule.zookeeper包整个
复制到
/main/java/com.alibaba.csp.sentinel.dashboard.rule.zookeeper
改造zookeeper的默认实现
- 修改代码参考 http://119.23.50.122/ms/sentinel-dashboard/zookeeper.zip
流控规则 FlowControllerV2
- 在FlowControllerV2.java中,改为flowRuleZookeeperProvider和flowRuleZookeeperPublisher
@Autowired
@Qualifier("flowRuleZookeeperProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleZookeeperPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
降级规则 DegradeControllerV2
- 在DegradeControllerV2.java中,改为degradeRuleZookeeperProvider和degradeRuleZookeeperPublisher
@Autowired
@Qualifier("degradeRuleZookeeperProvider")
private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
@Autowired
@Qualifier("degradeRuleZookeeperPublisher")
private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;
Sentinel 客户端整合
- 1、限流 (拒绝掉该请求,不做其他处理)
- 2、熔断 (配置熔断,并指定熔断后的处理方法)
- 3、热点数据 (通过请求参数配置)
组件选择
- sentinel、sentinel-dashboard、zookeeper、spring boot
基本配置
- pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>0.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-zookeeper</artifactId>
<version>1.5.2</version>
</dependency>
- application.yml
spring:
application:
name: api-service-order
cloud:
sentinel:
transport:
dashboard: localhost:8000
port: 8100
sentinel:
zookeeper:
address: 192.168.0.10:2181
path: /sentinel_rule_config
加载规则
- sentinel客户端配置,加载流控规则、熔断规则等
import javax.annotation.PostConstruct;
import java.util.List;
/**
* @author zhang3
* description sentinel 客户端配置,用于加载流控规则、熔断规则等
*/
@Component
public class SentinelClientConfig {
@Value("${sentinel.zookeeper.address}")
private String zkServer;
@Value("${sentinel.zookeeper.path}")
private String zkPath;
@Value("${spring.application.name}")
private String appName;
@PostConstruct
public void loadRules() {
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ZookeeperDataSource<>(zkServer, buildAppName("flow") ,
source -> JSON.parseArray(source, FlowRule.class));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
ReadableDataSource<String, List<DegradeRule>> degradeRuleDataSource = new ZookeeperDataSource<>(zkServer, buildAppName("degrade") ,
source -> JSON.parseArray(source, DegradeRule.class));
DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty());
}
public String buildAppName(String ruleName) {
return zkPath+"/" + appName + "_" + ruleName;
}
}
示例
- @SentinelResource标记,blockHandler指定熔断后的处理方法
- 注意:创建熔断规则时指定的资源名称必须和@SentinelResource#value的值对应,不然被拒掉的请求没有降级的业务服务
@GetMapping("/test/sentinel")
@SentinelResource(value = "createOrder", blockHandler = "blockMethod")
public Object testSentinel(@AuthenticationPrincipal String username) {
log.info("user is " + username);
return username;
}
public Object blockMethod(@AuthenticationPrincipal String username, BlockException exception) {
log.info("block by " + exception.getClass().getSimpleName());
return username;
}
- 关于@SentinelResource
@SentinelResource注解最主要的两个用法:限流控制和熔断降级的具体使用案例介绍完了。
另外,该注解还有一些其他更精细化的配置,比如忽略某些异常的配置、默认降级函数等等,
具体可见如下说明:
1、value:资源名称,必需项(不能为空)
2、entryType:entry 类型,可选项(默认为 EntryType.OUT)
3、blockHandler / blockHandlerClass: blockHandler对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
4、fallback:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
5、defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
6、exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
配置中心 Nacos 篇
修改sentinel-dashboard
基本配置
- 修改pom.xml 注释掉 scope#test
<!--for Nacos rule publisher sample-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<!--<scope>test</scope>-->
</dependency>
- 修改application.properties
#登录用户名和密码
# If auth.enabled=false, Sentinel console disable login
auth.username=root
auth.password=root
# 端口
server.port=8000
- 复制代码
将/test/java/com.alibaba.csp.sentinel.dashboard.rule.nacos包整个
复制到
/main/java/com.alibaba.csp.sentinel.dashboard.rule.nacos
改造Nacos的默认实现
- 修改代码参考 http://119.23.50.122/ms/sentinel-dashboard/nacos.zip
流控规则 FlowControllerV2
- 在FlowControllerV2.java中,改为flowRuleNacosProvider和flowRuleNacosPublisher
@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
降级规则 DegradeControllerV2
- 在DegradeControllerV2.java中,改为degradeRuleNacosProvider和degradeRuleNacosPublisher
@Autowired
@Qualifier("degradeRuleNacosProvider")
private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
@Autowired
@Qualifier("degradeRuleNacosPublisher")
private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;
sentinel 网关整合
- spring-cloud-gateway 官方文档 https://cloud.spring.io/spring-cloud-gateway/reference/html/
- gateway routes配置 https://www.jianshu.com/p/c8ac84e820cc
组件选择
- sentinel、sentinel-dashboard 、nacos、spring-cloud-gateway
搭建网关
- pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- sentinel/sentinel-dashboard nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
- application.yml
server:
port: 9080
spring:
application:
name: test-gateway
cloud:
gateway:
routes:
- id: host_route
uri: http://192.168.0.2:9090/
predicates:
- Host=app-web.33web.org:${server.port}
- id: host2_route
uri: http://192.168.0.2:9091/
predicates:
- Host=user.33web.org:${server.port}
sentinel:
transport:
dashboard: localhost:8000
port: 8100
加载规则
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.PropertyKeyConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
/**
* @author zhang3
* description 初始化Sentinel 网关 相关配置
*/
@Configuration
@Slf4j
public class SentinelConfig {
@Value("${spring.application.name}")
private String appName;
public static final String GROUP_ID = "SENTINEL_GROUP";
// private static final String NACOS_SERVER_ADDR = "172.17.0.1:8848";
// private static final String NACOS_NAMESPACE = "146fdeb6-b082-42d6-9d9b-47cdf79ec55d";
private static final String NACOS_SERVER_ADDR = "192.168.0.10:8848";
private static final String NACOS_NAMESPACE = "d400a63f-301b-4951-acfb-d98cc0b0a425";
@PostConstruct
public void loadRules() {
Properties properties = new Properties();
//指定ip地址
properties.put(PropertyKeyConst.SERVER_ADDR, NACOS_SERVER_ADDR);
//设置命名空间(namespace)
properties.put(PropertyKeyConst.NAMESPACE, NACOS_NAMESPACE);
//网关熔断
ReadableDataSource<String, List<DegradeRule>> degradeRuleDataSource = new NacosDataSource<>(properties, SentinelConfig.GROUP_ID, buildAppName("degrade") ,
source -> JSON.parseArray(source, DegradeRule.class));
DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty());
//网关流控
ReadableDataSource<String, Set<GatewayFlowRule>> gatewayRuleDataSource = new NacosDataSource<>(properties, SentinelConfig.GROUP_ID, buildAppName("gateway_flow") ,
source -> new HashSet<>(JSON.parseArray(source, GatewayFlowRule.class)));
GatewayRuleManager.register2Property(gatewayRuleDataSource.getProperty());
//Api流控
// ReadableDataSource<String, Set<ApiDefinition>> ApiDataSource = new NacosDataSource<>(properties, SentinelConfig.GROUP_ID, buildAppName("gateway_api") ,
// source -> new HashSet<>(JSON.parseArray(source, ApiDefinition.class)));
ReadableDataSource<String, Set<ApiDefinition>> ApiDataSource = new NacosDataSource<>(properties, SentinelConfig.GROUP_ID, buildAppName("gateway_api") ,
source -> {
List<String> apis = JSON.parseArray(source, String.class);
HashSet<ApiDefinition> sets = new HashSet<>();
for(String api : apis) {
ApiDefinition entity = JSON.parseObject(api, ApiDefinition.class);
entity.getPredicateItems().clear();
JSONObject jsonObject = JSON.parseObject(api);
String items = jsonObject.getString("predicateItems");
List<ApiPathPredicateItem> list = JSON.parseArray(items, ApiPathPredicateItem.class);
entity.getPredicateItems().addAll(list);
sets.add(entity);
}
return sets;
});
GatewayApiDefinitionManager.register2Property(ApiDataSource.getProperty());
}
private String buildAppName(String ruleName) {
return appName + "_" + ruleName;
}
}
启动参数
- 增加启动参数(VM),指定网关启动参数,即app.type=1
-Dcsp.sentinel.app.type=1
- 其他启动配置 https://github.com/alibaba/Sentinel/wiki/启动配置项
sentinel 客户端整合
组件选择
- sentinel、sentinel-dashboard、nacos、spring boot
集成sentinel
- pom.xml
<!-- sentinel/sentinel-dashboard nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.7.1</version>
</dependency>
- application.yml
server:
port: 9090
spring:
application:
name: test-web
cloud:
sentinel:
transport:
dashboard: localhost:8000
port: 8100
加载规则
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
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.nacos.api.PropertyKeyConst;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Properties;
/**
* @author zhang3
* description 初始化Sentinel 应用 相关配置
*/
@Configuration
public class SentinelConfig {
@Value("${spring.application.name}")
private String appName;
@PostConstruct
public void loadRules() {
Properties properties = new Properties();
//指定ip地址
properties.put(PropertyKeyConst.SERVER_ADDR, "192.168.0.10:8848");
//设置命名空间(namespace)
properties.put(PropertyKeyConst.NAMESPACE, "d400a63f-301b-4951-acfb-d98cc0b0a425");
String groupId = "SENTINEL_GROUP";
//应用流控
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(properties, groupId, buildAppName("flow") ,
source -> JSON.parseArray(source, FlowRule.class));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
//应用熔断
ReadableDataSource<String, List<DegradeRule>> degradeRuleDataSource = new NacosDataSource<>(properties, groupId, buildAppName("degrade") ,
source -> JSON.parseArray(source, DegradeRule.class));
DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty());
}
private String buildAppName(String ruleName) {
return appName + "_" + ruleName;
}
}
示例
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zhang3
* description TODO
*/
@RestController
@RequestMapping
@Slf4j
public class TestController {
@Autowired
TestService testService;
@GetMapping("test/say")
public void testSay(String msg, @RequestParam(defaultValue = "0") Integer error) {
if (error == 1) {
throw new RuntimeException("测试异常");
}
testService.testSentinel();
log.info("invoke test-say: " + msg);
}
}
@Component
@Slf4j
class TestService {
@SentinelResource(value = "testSay")
public void testSentinel() {
log.info("invoke testSentinel method");
}
}