Sentinel文档地址:
介绍 · alibaba/Sentinel Wiki · GitHub
Sentinel下载地址:
https://github.com/alibaba/Sentinel/releases
SentinelDashboard
当前版本 sentinel-dashboard-1.8.1.jar,启动执行以下命令
java -jar -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=sentinel sentinel-dashboard-1.8.1.jar
浏览器访问http://localhost:8080/即可进入Sentinel控制台界面
整合示例
Nacos依赖版本:2.2.5.RELEASE
Sentinel依赖版本:2.2.5.RELEASE
<!-- Nacos依赖包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Sentinel依赖包 -->
<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>
</dependency>
相关配置如下:
spring.cloud.nacos.discovery.register-enabled=true
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.namespace=public
spring.cloud.nacos.discovery.weight=1
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.encode=utf-8
spring.cloud.nacos.config.group=DEFAULT_GROUP
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.discovery.register-enabled=true
spring.cloud.nacos.discovery.namespace=public
spring.cloud.nacos.discovery.weight=1
## Sentinel配置
# HttpServer地址
spring.cloud.sentinel.transport.port=8719
# SentinelDashBoard地址
spring.cloud.sentinel.transport.dashboard=localhost:8080
# sentinel datasource nacos
spring.cloud.sentinel.datasource.ds1.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds1.nacos.data-id=microservice-platform-nacos-sentinel-flow-rules
spring.cloud.sentinel.datasource.ds1.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds1.nacos.data-type=json
spring.cloud.sentinel.datasource.ds1.nacos.rule-type=flow
# sentinel datasource file
#spring.cloud.sentinel.datasource.ds2.file.file=classpath:scripts/rule.json
#spring.cloud.sentinel.datasource.ds2.file.data-type=json
#spring.cloud.sentinel.datasource.ds2.file.rule-type=flow
# sentinel datasource zookeeper
#spring.cloud.sentinel.datasource.ds3.zk.path=/sentinel-example/example-flow-rules
#spring.cloud.sentinel.datasource.ds3.zk.server-addr=localhost:2181
#spring.cloud.sentinel.datasource.ds3.zk.rule-type=authority
# sentinel datasource apollo
#spring.cloud.sentinel.datasource.ds4.apollo.namespace-name=application
#spring.cloud.sentinel.datasource.ds4.apollo.flow-rules-key=sentinel
#spring.cloud.sentinel.datasource.ds4.apollo.default-flow-rule-value=test
#spring.cloud.sentinel.datasource.ds4.apollo.rule-type=param-flow
Sentinel可以支持多种数据源,数据格式如下:
[
{
"resource":"/user/tmp1",
"limitAPP":"default",
"grade":1,
"count":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":false
},
{
"resource":"/user/tmp2",
"limitAPP":"default",
"grade":1,
"count":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":false
}
]
示例代码:
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.platform.modules.user.service.IUserRemoteService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private Logger LOG = LoggerFactory.getLogger(UserController.class);
@Autowired
@Qualifier("userRemoteService")
private IUserRemoteService userRemoteService = null;
@RequestMapping(value = "/api/v1/user/tmp1", method = RequestMethod.GET)
@SentinelResource(value = "/user/tmp1", blockHandler = "readUserTmp1BlockHandler", fallback = "readUserTmp1FallbackHandler")
public Object readUserTmp1(String account, String password) {
System.out.println(String.format("sentienl user tmp1 account:%s password:%s", account, password));
System.out.println(1/0);
return String.format("sentienl user tmp1 account:%s password:%s", account, password);
}
public Object readUserTmp1BlockHandler(String account, String password, BlockException be) {
LOG.error(String.format("BlockHandler:%s %s %s", account, password, be.getMessage()), be);
return String.format("BlockHandler:%s %s", account, password);
}
public Object readUserTmp1FallbackHandler(String account, String password, Throwable e) {
LOG.error(String.format("FallbackHandler:%s %s %s", account, password, e.getMessage()), e);
return String.format("FallbackHandler:%s %s", account, password);
}
@RequestMapping(value = "/api/v1/user/tmp2", method = RequestMethod.GET)
@SentinelResource(value = "/user/tmp2", blockHandler = "useBlockHandler", blockHandlerClass = UserSentinelExceptionHandler.class,
fallback = "useFallbackHandler", fallbackClass = UserSentinelExceptionHandler.class)
public Object readUserTmp2(String account, String password) {
System.out.println(String.format("sentienl user tmp2 account:%s password:%s", account, password));
System.out.println(1/0);
return String.format("sentienl user tmp2 account:%s password:%s", account, password);
}
@RequestMapping(value = "/api/v1/user/tmp3", method = RequestMethod.GET)
@SentinelResource(value = "/user/tmp3", blockHandler = "useBlockHandler", blockHandlerClass = UserSentinelExceptionHandler.class,
defaultFallback = "useDefaultFallbackHandler", fallbackClass = UserSentinelExceptionHandler.class)
public Object readUserTmp3(String account, String password) {
System.out.println(String.format("sentienl user tmp3 account:%s password:%s", account, password));
System.out.println(1/0);
return String.format("sentienl user tmp3 account:%s password:%s", account, password);
}
@RequestMapping(value = "/api/v1/user/tmp4", method = RequestMethod.GET)
@SentinelResource(value = "/user/tmp4", blockHandler = "useBlockHandler", blockHandlerClass = UserSentinelExceptionHandler.class,
fallback = "useFallbackHandler", fallbackClass = UserSentinelExceptionHandler.class)
public Object readUserTmp4(String account, String password) {
System.out.println(String.format("sentienl user tmp4 account:%s password:%s", account, password));
System.out.println(1/0);
return userRemoteService.readUser(account, password);
}
}
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserSentinelExceptionHandler {
private static Logger LOG = LoggerFactory.getLogger(UserSentinelExceptionHandler.class);
public static Object useBlockHandler(String account, String password, BlockException be) {
LOG.error(String.format("BlockHandler:%s %s %s", account, password, be.getMessage()), be);
return String.format("BlockHandler:%s %s %s", account, password, be.getMessage());
}
public static Object useFallbackHandler(String account, String password, Throwable e) {
LOG.error(String.format("FallbackHandler:%s %s %s", account, password, e.getMessage()), e);
return String.format("FallbackHandler:%s %s %s", account, password, e.getMessage());
}
public static Object useDefaultFallbackHandler(Throwable e) {
LOG.error(String.format("DefaultFallbackHandler:%s", e.getMessage()), e);
return String.format("DefaultFallbackHandler %s", e.getMessage());
}
}
Sentinel规则配置写入Nacos
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Properties;
@Component
public class SentinelDataSourceNacosWriter {
private Logger LOG = LoggerFactory.getLogger(SentinelDataSourceNacosWriter.class);
@Value("${spring.cloud.sentinel.datasource.ds1.nacos.server-addr}")
private String serverAddr = null;
@Value("${spring.cloud.sentinel.datasource.ds1.nacos.group-id}")
private String groupId = null;
@Value("${spring.cloud.sentinel.datasource.ds1.nacos.data-id}")
private String dataId = null;
@PostConstruct
public void postConstruct() throws NacosException {
// 从Nacos配置中心读取限流规则
// 创建NacosDataSource并将其注册至对应的RuleManager上
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<List<FlowRule>>(
serverAddr, groupId, dataId, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>(){}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
LOG.info("flow rule data source property {}", flowRuleDataSource.getProperty());
// 自动写入 Sentinel Dashboard中修改规则同步到Nacos
WritableDataSource writableDataSource = new NacosWritableDataSource<List<FlowRule>>(serverAddr, groupId, dataId);
WritableDataSourceRegistry.registerFlowDataSource(writableDataSource);
}
/**
* 手动写入规则
* "[\n"
* + " {\n"
* + " \"resource\": \"login\",\n"
* + " \"controlBehavior\": 0,\n"
* + " \"count\": 5.0,\n"
* + " \"grade\": 1,\n"
* + " \"limitApp\": \"default\",\n"
* + " \"strategy\": 0\n"
* + " }\n"
* + "]";
* @param rule
*/
public void manualWriteRule(String rule) {
try {
ConfigService configService = NacosFactory.createConfigService(serverAddr);
boolean isPublish = configService.publishConfig(dataId, groupId, rule);
LOG.info("nacos config public {} {}", rule, isPublish);
} catch (NacosException e) {
LOG.error(e.getMessage(), e);
}
}
}
class NacosWritableDataSource<T> implements WritableDataSource<T> {
private Logger LOG = LoggerFactory.getLogger(NacosWritableDataSource.class);
private String serverAddr = null;
private String groupId = null;
private String dataId = null;
public NacosWritableDataSource(String serverAddr, String groupId, String dataId) {
this.serverAddr = serverAddr;
this.groupId = groupId;
this.dataId = dataId;
}
//Sentinel更新的时候
@Override
public void write(T value) throws Exception {
String rule = JSON.toJSONString(value);
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
properties.put(PropertyKeyConst.NAMESPACE, "public");
ConfigService configService = NacosFactory.createConfigService(properties);
boolean isPublish = configService.publishConfig(dataId, groupId, rule);
LOG.info("nacos config public {} {}", rule, isPublish);
}
@Override
public void close() throws Exception {
}
}
注意事项:
@SentinelResource的value值不要和@GetMapping的value值一样,配置Sentinel的流控或者降级的资源名应和@SentinelResource的value值一样,不要再同时配置@GetMapping中value的资源名。
blockHandler的类。对应的处理函数必须static修饰,否则无法解析。
fallback的类。对应的处理函数必须static修饰,否则无法解析。