说明:
Sentinel是控制组件,可实现流量控制、熔断降级、系统自适应保护等多个维度保护微服务的稳定性。
一、启动Sentinel控制台Dashboard(192.168.233.129):
1.使用官方默认sentinel-dashboard(退出后在管理页创建的规则会丢失):
(1)下载sentinel-dashboard-1.8.3.jar到D盘根目录:
https://github.com/alibaba/Sentinel/releases
(2)打开cmd启动控制台(设置用户和密码都为root):
D:\>java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=192.168.233.129:8080 -Dsentinel.dashboard.auth.username=root -Dsentinel.dashboard.auth.password=root -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.3.jar
2.搭建sentinel-dashboard(Push模式,能将管理页增加的规则持久化到Apollo):
Apollo安装(一、二章):
https://blog.youkuaiyun.com/a526001650a/article/details/123188040
(1)下载Sentinel-1.8.3.zip,解压后用IntelliJ IDEA打开:
https://github.com/alibaba/Sentinel/releases/tag/1.8.3
(2)删除apollo-openapi中<scope>test</scope>,在sentinel-dashboard模块pom.xml中(后面所有操作均在sentinel-dashboard模块中进行):
<!-- for Apollo rule publisher sample -->
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-openapi</artifactId>
<version>1.2.0</version>
</dependency>
(3)配置加载类,修改ApolloConfig(从test目录移到main目录):
package com.alibaba.csp.sentinel.dashboard.rule.apollo;
...
@Configuration
@ComponentScan("com.alibaba.csp.sentinel.dashboard.rule.apollo.*")//扫描自定义包下所有规则类
public class ApolloConfig implements InitializingBean {
public static String url = "http://192.168.233.128:8070"; //apollo地址
public static String appId = "apollo"; //应用id
public static String token = "76e317c68505c32a4d4adab1e8abf84ff5bf1f1a"; //token
public static String env = "DEV"; //环境
public static String nameSpace = "application"; //名称空间
public static String account = "apollo"; //管理员账号
public static String clusterName = "default";
@Override
public void afterPropertiesSet() throws Exception {
}
@Bean
public ApolloOpenApiClient apolloOpenApiClient() { //供外部使用
return ApolloOpenApiClient.newBuilder()
.withPortalUrl(url) //apollo服务器地址
.withToken(token) //token
.build();
}
@Bean
public Converter<List<FlowRuleEntity>, String> flowRuleEncoder() { //限流规则列表转json
return JSON::toJSONString;
}
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleDecoder() {//限流规则json转列表
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
@Bean
public Converter<List<DegradeRuleEntity>, String> degradeRuleEncoder() { //熔断规则列表转json
return JSON::toJSONString;
}
@Bean
public Converter<String, List<DegradeRuleEntity>> degradeRuleDecoder() { //熔断规则json转列表
return s -> JSON.parseArray(s, DegradeRuleEntity.class);
}
@Bean
public Converter<List<SystemRuleEntity>, String> systemRuleEncoder() { //系统保护规则列表转json
return JSON::toJSONString;
}
@Bean
public Converter<String, List<SystemRuleEntity>> systemRuleDecoder() { //系统保护规则json转列表
return s -> JSON.parseArray(s, SystemRuleEntity.class);
}
@Bean
public Converter<List<AuthorityRuleEntity>, String> authorityRuleEncoder() { //访问控制规则列表转json
return JSON::toJSONString;
}
@Bean
public Converter<String, List<AuthorityRuleEntity>> authorityRuleDecoder() { //访问控制规则json转列表
return s -> JSON.parseArray(s, AuthorityRuleEntity.class);
}
@Bean
public Converter<List<ParamFlowRuleEntity>, String> paramFlowRuleEncoder() { //热点限流规则列表转json
return JSON::toJSONString;
}
@Bean
public Converter<String, List<ParamFlowRuleEntity>> paramFlowRuleDecoder() { //热点参数限流规则json转列表
return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);
}
}
(4)flowDataId工具类,修改ApolloConfigUtil(从test目录移到main目录):
public final class ApolloConfigUtil { //flowDataId工具类
public static final String POSTFIX = "rules";
private ApolloConfigUtil() {
}
public static String getFlowDataId(String appName) { //限流DataId
return String.format("%s-%s-%s", appName, "flow", POSTFIX);
}
public static String getDegradeDataId(String appName) { //熔断DataId
return String.format("%s-%s-%s", appName, "degrade", POSTFIX);
}
public static String getSystemDataId(String appName) { //系统保护DataId
return String.format("%s-%s-%s", appName, "system", POSTFIX);
}
public static String getAuthorityDataId(String appName) { //访问控制DataId
return String.format("%s-%s-%s", appName, "authority", POSTFIX);
}
public static String getParamFlowDataId(String appName) { //热点参数限流DataId
return String.format("%s-%s-%s", appName, "paramFlow", POSTFIX);
}
}
(5)配置限流规则发布,修改FlowRuleApolloPublisher(从test目录移到main目录,熔断等规则需要创建新的DynamicRulePublisher实现类):
package com.alibaba.csp.sentinel.dashboard.rule.apollo;
...
@Component("flowRuleApolloPublisher")
public class FlowRuleApolloPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> { //限流规则发布
@Autowired
private ApolloOpenApiClient apolloOpenApiClient;
@Autowired
private Converter<List<FlowRuleEntity>, String> converter;
private FastDateFormat dateFormat = FastDateFormat.getInstance("yyyyMMdd HH:mm:ss");
@Override
public void publish(String appName, List<FlowRuleEntity> rules) {
if (rules == null) {
return;
}
String flowDataId = ApolloConfigUtil.getFlowDataId(appName);
//(1)修改
OpenItemDTO itemDTO = new OpenItemDTO();
itemDTO.setKey(flowDataId);
itemDTO.setValue(converter.convert(rules));
itemDTO.setComment("修改时间: " + dateFormat.format(new Date())); //描述
itemDTO.setDataChangeCreatedBy(ApolloConfig.userId); //修改人
apolloOpenApiClient.createOrUpdateItem(ApolloConfig.appId, //应用id
ApolloConfig.env, //环境
ApolloConfig.clusterName,
ApolloConfig.nameSpace, //名称空间
itemDTO);
//(2)发布
NamespaceReleaseDTO releaseDTO = new NamespaceReleaseDTO();
releaseDTO.setEmergencyPublish(true);
releaseDTO.setReleaseComment("发布时间: " + dateFormat.format(new Date())); //描述
releaseDTO.setReleasedBy(ApolloConfig.userId); //发布人
releaseDTO.setReleaseTitle("发布Key-Value"); //标题
apolloOpenApiClient.publishNamespace(ApolloConfig.appId, //应用id
ApolloConfig.env, //环境
ApolloConfig.clusterName,
ApolloConfig.nameSpace, //名称空间
releaseDTO);
}
}
(6)配置限流规则获取,修改FlowRuleApolloProvider(从test目录移到main目录,熔断等规则需要创建新DynamicRuleProvider实现类):
package com.alibaba.csp.sentinel.dashboard.rule.apollo;
...
@Component("flowRuleApolloProvider")
public class FlowRuleApolloProvider implements DynamicRuleProvider<List<FlowRuleEntity>> { //限流规则获取
@Autowired
private ApolloOpenApiClient apolloOpenApiClient;
@Autowired
private Converter<String, List<FlowRuleEntity>> converter;
@Override
public List<FlowRuleEntity> getRules(String appName) throws Exception {
String flowDataId = ApolloConfigUtil.getFlowDataId(appName);
OpenNamespaceDTO namespaceDTO = apolloOpenApiClient.getNamespace(ApolloConfig.appId, //Apollo网页上创建的应用appId
ApolloConfig.env, //环境
ApolloConfig.clusterName,
ApolloConfig.nameSpace); //名称空间
String rules = namespaceDTO
.getItems()
.stream()
.filter(p -> p.getKey().equals(flowDataId)) //过滤
.map(OpenItemDTO::getValue)
.findFirst()
.orElse("");
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
(7)配置限流Controller类,修改FlowControllerV1中使用Provider与Publisher实现类(其他规则修改其他Controller):
package com.alibaba.csp.sentinel.dashboard.controller;
... //保持原有代码
@RestController
@RequestMapping(value = "/v1/flow")
public class FlowControllerV1 {
... //保持原有代码
@Autowired
private FlowRuleApolloProvider flowRuleApolloProvider;
@Autowired
private FlowRuleApolloPublisher flowRuleApolloPublisher;
@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<FlowRuleEntity>> apiQueryMachineRules(...) {
... //保持原有代码
try {
List<FlowRuleEntity> rules = flowRuleApolloProvider.getRules(app); //修改获取规则的代码为自已的
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("Error when querying flow rules", throwable);
return Result.ofThrowable(-1, throwable);
}
}
... //保持原有代码
@PostMapping("/rule")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<FlowRuleEntity> apiAddFlowRule(...) {
... //保持原有代码
try {
entity = repository.save(entity);
publishRules(entity.getApp()); //替换成自已的publishRules方法
return Result.ofSuccess(entity);
} catch (Throwable t) {
Throwable e = t instanceof ExecutionException ? t.getCause() : t;
logger.error("Failed to add new flow rule, app={}, ip={}", entity.getApp(), entity.getIp(), e);
return Result.ofFail(-1, e.getMessage());
}
}
@PutMapping("/save.json")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<FlowRuleEntity> apiUpdateFlowRule(...) {
... //保持原有代码
try {
entity = repository.save(entity);
if (entity == null) {
return Result.ofFail(-1, "save entity fail: null");
}
publishRules(entity.getApp()); //替换成自已的publishRules方法
return Result.ofSuccess(entity);
} catch (Throwable t) {
Throwable e = t instanceof ExecutionException ? t.getCause() : t;
logger.error("Error when updating flow rules, app={}, ip={}, ruleId={}", entity.getApp(),
entity.getIp(), id, e);
return Result.ofFail(-1, e.getMessage());
}
}
@DeleteMapping("/delete.json")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<Long> apiDeleteFlowRule(Long id) {
... //保持原有代码
try {
publishRules(oldEntity.getApp()); //替换成自已的publishRules方法
return Result.ofSuccess(id);
} catch (Throwable t) {
Throwable e = t instanceof ExecutionException ? t.getCause() : t;
logger.error("Error when deleting flow rules, app={}, ip={}, id={}", oldEntity.getApp(),
oldEntity.getIp(), id, e);
return Result.ofFail(-1, e.getMessage());
}
}
private void publishRules(String app) {//自定义发布规则方法
List<FlowRuleEntity> rules = repository.findAllByApp(app);
flowRuleApolloPublisher.publish(app, rules);
}
}
修改其他规则Controller同样代码:
熔断规则Controller:
com.alibaba.csp.sentinel.dashboard.controller.DegradeController
系统保护规则Controller:
com.alibaba.csp.sentinel.dashboard.controller.SystemController
访问控制规则Controller:
com.alibaba.csp.sentinel.dashboard.controller.AuthController
热点参数限流规则Controller:
com.alibaba.csp.sentinel.dashboard.controller.ParamFlowRuleController
(8)jar包方式运行,展开IntelliJ IDEA右侧Maven -> 找到sentinel-dashboard模块Lifecycle菜单,分别双击clean、install,将target目录下生成的sentinel-dashboard.jar上传到centos7,并使用java命令启动:
[root@localhost ~]# java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=192.168.233.129:8080 -Dsentinel.dashboard.auth.username=root -Dsentinel.dashboard.auth.password=root -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
3.访问首页看效果:
http://192.168.233.129:8080/#/dashboard/home
二、使用Sentinel:
1.添加sentinel依赖:
<dependencies>
<!-- sentinel依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.3</version>
</dependency>
<!-- 接入控制台实时监控 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.3</version>
</dependency>
<!-- 注解埋点支持 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.3</version>
</dependency>
<!-- 热点参数限流 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.8.3</version>
</dependency>
</dependencies>
2.定义规则,在Application中加载:
@EnableAsync //开启异步调用
@SpringBootApplication
public class SentinelApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelApplication.class);
loadFlowRule(); //(1)加载流量控制规则
//loadDegradeRule(); //(2)加载熔断降级规则
//loadSystemProtectRule(); //(3)系统保护规则
//loadAuthorityRule(); //(4)访问控制规则
//loadParamFlowRule(); //(5)热点参数限流规则
}
}
(1)流量控制规则:
private static void loadFlowRule() { //(1)流量控制规则
List<FlowRule> rules = new ArrayList<>();
FlowRule fr = new FlowRule();
fr.setResource("资源名1"); //指定资源名
fr.setGrade(RuleConstant.FLOW_GRADE_QPS); //限流阈值类型,QPS模式(默认) / 线程数模式FLOW_GRADE_THREAD(统计线程数,超过拒绝)
fr.setCount(20); //限流阈值,此处QPS最大为20
fr.setLimitApp("default"); //调用来源,default为不区分调用来源
fr.setStrategy(RuleConstant.STRATEGY_DIRECT); //调用关系限流策略:直接(默认)、关联STRATEGY_RELATE、链路STRATEGY_CHAIN
//fr.setRefResource("来源名称"); //setStrategy为关联或链路时需要设置
fr.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); //QPS超过阈值时的流控效果(0直接拒绝(默认) / 1冷启动缓慢增加 / 2匀速通过)
rules.add(fr);
FlowRuleManager.loadRules(rules);
}
(2)熔断降级规则:
private static void loadDegradeRule() { //(2)熔断降级规则
List<DegradeRule> rules = new ArrayList<>();
DegradeRule dr = new DegradeRule("资源名1"); //指定资源名
dr.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType()); //熔断策略:慢调用比例SLOW_REQUEST_RATIO(默认) /异常比例ERROR_RATIO / 异常数ERROR_COUNT
dr.setCount(0.7); //阈值为70%错误率,超出时熔断。必须MinRequestAmount同时满足才会熔断
dr.setMinRequestAmount(100); //熔断触发的最小请求数,数小于该值时Count超出阈值也不会熔断
dr.setStatIntervalMs(30000); //统计时长,单位毫秒
dr.setTimeWindow(10); //熔断时长,单位为秒
//dr.setSlowRatioThreshold(1.0D); //慢调用比例阈值
rules.add(dr);
DegradeRuleManager.loadRules(rules);
//监听熔断开关事件
EventObserverRegistry.getInstance().addStateChangeObserver("logging",
(prevState, newState, rule, snapshotValue) -> {
if (newState == CircuitBreaker.State.OPEN) { //开启时
} else { //半开或关闭时
}
});
}
(3)系统保护规则:
private static void loadSystemProtectRule() { //(3)系统保护规则
List<SystemRule> rules = new ArrayList<>();
SystemRule sr = new SystemRule();
sr.setHighestSystemLoad(10); //系统load1超过阈值(推荐值=CPU核心数*2.5),且并发数超过容量时才会触发保护,仅对Linux/Unix-like机器生效
sr.setAvgRt(2000); //入口流量平均响应时间,单位毫秒
sr.setMaxThread(100); //入口流量最大并发数
sr.setQps(20); //入口请求最大QPS数量
sr.setHighestCpuUsage(0.9); //CPU使用率,值范围:0.0-1.0
rules.add(sr);
SystemRuleManager.loadRules(rules);
}
(4)访问控制规则:
private static void loadAuthorityRule() { //(4)访问控制规则
List<AuthorityRule> rules = new ArrayList<>();
AuthorityRule ar = new AuthorityRule();
ar.setResource("资源名1"); //指定资源名
ar.setLimitApp("appA,appB"); //配置请求来源名单,多个时用逗号分隔
ar.setStrategy(RuleConstant.AUTHORITY_WHITE); //限制模式:白名单AUTHORITY_WHITE(默认) / 黑名单AUTHORITY_BLACK
AuthorityRuleManager.loadRules(rules);
}
(5)热点参数限流规则:
private static void loadParamFlowRule() { //(5)热点参数限流规则
List<ParamFlowRule> rules = new ArrayList<>();
ParamFlowRule pfr = new ParamFlowRule("资源名1") //指定资源名(必填)
.setGrade(RuleConstant.FLOW_GRADE_QPS) //限流阈值类型,默认QPS模式
.setParamIdx(0) //热点参数的索引(必填),对应SphU.entry(xxx, 参数0, 参数1)的参数索引
.setCount(5) //限流阈值(必填)
.setDurationInSec(1) //统计窗口时间长度(单位秒),默认1秒
.setControlBehavior(1) //流控效果:快速失败(默认) / 匀速排队
.setMaxQueueingTimeMs(0) //最大排队等待时长(单位毫秒)(仅在匀速排队模式生效),默认0毫秒
.setClusterMode(false); //是否是集群参数流控规则,默认false
//.setClusterConfig(...) //集群流控相关配置
//对指定类型的参数,设置独立的限流(覆盖全局)
ParamFlowItem item = new ParamFlowItem().setObject("参数0") //使用SphU.entry("资源名1", EntryType.IN, 1, "参数0")传入热点参数
.setClassType(int.class.getName())
.setCount(10);
pfr.setParamFlowItemList(Collections.singletonList(item)); //例外项,对指定参数值单独设置限流阈值
rules.add(pfr);
ParamFlowRuleManager.loadRules(rules);
}
3.使用规则(定义资源),保护业务逻辑:
(1)方式1,try-catch方式:
@RequestMapping("/user1")
public String user1(String userNo) {
try (Entry entry = SphU.entry("资源名1")) { //热点参数限流时使用SphU.entry("资源名1", EntryType.IN, 1, "参数1")传入热点参数
//此处为被保护的逻辑
} catch (BlockException ex) {
Tracer.trace(ex); //记录业务异常
//此处处理被限流/降级的逻辑
} //传入热点参数后,结束时需要手动调用entry.exit()
return "...";
}
(2)方式2,if-else方式:
@RequestMapping("/user2")
public String user2(String userNo) {
if (SphO.entry("资源名1")) {
try {
//此处为被保护的逻辑
} finally {
SphO.exit();
}
} else {
//此处处理被限流/降级的逻辑
}
return "user2";
}
(3)方式3,注解方式:
@RequestMapping("/user3")
@SentinelResource(blockHandler = "blockHandler") //需要传热点参数时,放在形参上,同SphU.entry("资源名1", EntryType.IN, 1, "userNo")
public String user3(String userNo) {
//此处为被保护的逻辑
return "...";
}
public String blockHandler(String id, BlockException ex) { //此函数会因user3函数被限流/降级/系统保护时触发
Tracer.trace(ex); //记录业务异常
//此处处理被限流/降级的逻辑
return "...";
}
(4)方式4,异步调用方式:
@RequestMapping("/user4")
public String user4(String userNo) {
try {
AsyncEntry entry = SphU.asyncEntry("资源名1");
doAsync(userNo, entry); //执行异步方法
} catch (BlockException ex) {
//此处处理被限流/降级的逻辑
}
return "...";
}
@Async
private void doAsync(String userNo, AsyncEntry entry) {
try {
//此处为被保护的逻辑
} finally {
entry.exit(); //异步结束时调用exit方法
}
}
4.IntelliJ IDEA方式启动,Edit Configurations -> 左侧Application中选中ApolloApplication,右侧Configuration选项卡中,在VM options中填入:
-Dproject.name=sentinel -Dcsp.sentinel.dashboard.server=192.168.233.129:8080 -Dcsp.sentinel.api.port=8719
三、开源框架集成Sentinel,详细见如下官方文档:
https://sentinelguard.io/zh-cn/docs/open-source-framework-integrations.html
1.Dubbo集成Sentinel:
(1)添加sentinel+dubbo组合依赖(控),在pom文件中:
<!-- ...省略控制台依赖+注解埋点依赖 -->
<!-- sentinel+dubbo依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
<version>1.8.3</version>
</dependency>
(2)配置限流粒度(可配置为接口或方法级别),在Application.main中加载:
private static void loadFlowRule() { //流量控制规则
List<FlowRule> rules = new ArrayList<>();
FlowRule fr = new FlowRule();
fr.setResource("com.yyh.dubbosentinel.UserService:getUser(java.lang.String)"); //方法级,资源名规则:接口全名.方法名(形参类型)
//fr.setResource("com.yyh.dubbosentinel.UserService"); //类级,资源名规则:接口全名
... //省略其他配置
FlowRuleManager.loadRules(rules);
}
(3)注册dubbo限流/降级回调,在Application.main中加载:
private static void registerDubboFallback() { //注册dubbo全局回调
DubboAdapterGlobalConfig.setConsumerFallback(new DubboFallback(){
@Override
public Result handle(Invoker<?> invoker, Invocation invocation, BlockException e) {
//此处处理被限流/降级的逻辑
return null;
}
});
}
(4)关闭某个Filter,在Application中:
@Bean
public ConsumerConfig consumerConfig() {
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setFilter("-sentinel.dubbo.consumer.filter");
return consumerConfig;
}
2.RocketMQ集成Sentinel:
(1)添加sentinel依赖(见第二章),添加rocketmq-client依赖:
<!-- 导入rocketmq依赖包 -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.2.0</version>
</dependency>
(2)配置限流规则,在Application.main中加载:
private static void loadFlowRule() { //(1)流量控制规则
FlowRule rule = new FlowRule();
rule.setResource("组名:topic名称"); //资源名规则:组名:topic名称
rule.setCount(5); //限流阈值,此处QPS最大为每秒允许通过5个
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); //限流模式为QPS模式
rule.setLimitApp("default"); //调用来源,default为不区分调用来源
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER); //QPS超过阈值时的流控效果为匀速通过
rule.setMaxQueueingTimeMs(5 * 1000); //最大排队等待时长为5秒
FlowRuleManager.loadRules(Collections.singletonList(rule));
}
(3)处理消息的时候手动埋点,在Application.main中加载(官方例子):
private static Map<MessageQueue, Long> mMap = new HashMap<>();
private static void pullMessage() throws MQClientException {
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("组名");
consumer.setNamesrvAddr("192.168.233.128:9876"); //RocketMQ服务器地址
consumer.start();
Set<MessageQueue> mqs = new HashSet<>();
try {
mqs = consumer.fetchSubscribeMessageQueues("Topic名");
} catch (Exception e) {
e.printStackTrace();
}
for (MessageQueue mq : mqs) {
SINGLE_MQ:
while (true) {
try {
PullResult pullResult = consumer.pullBlockIfNotFound(mq, null, getMessageQueueOffset(mq), 32);
if (pullResult.getMsgFoundList() != null) {
for (MessageExt msg : pullResult.getMsgFoundList()) {
doMessage(msg);
}
}
long nextOffset = pullResult.getNextBeginOffset();
putMessageQueueOffset(mq, nextOffset);
consumer.updateConsumeOffset(mq, nextOffset);
switch (pullResult.getPullStatus()) {
case NO_NEW_MSG:
break SINGLE_MQ;
case FOUND:
case NO_MATCHED_MSG:
case OFFSET_ILLEGAL:
default:
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
consumer.shutdown();
}
private static void doMessage(MessageExt message) {
Entry entry = null;
try {
ContextUtil.enter("组名:topic名");
entry = SphU.entry("组名:topic名", EntryType.OUT);
//此处为被保护的逻辑,如处理消息
} catch (BlockException ex) {
//此处处理被限流/降级的逻辑
} finally {
if (entry != null) {
entry.exit();
}
ContextUtil.exit();
}
}
private static long getMessageQueueOffset(MessageQueue mq) {
Long offset = mMap.get(mq);
if (offset != null) {
return offset;
}
return 0;
}
private static void putMessageQueueOffset(MessageQueue mq, long offset) {
mMap.put(mq, offset);
}
四、集群流控:
1.Server端(独立部署):
(1)添加Servert端依赖:
<!-- 添加Servert端依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-cluster-server-default</artifactId>
<version>1.8.3</version>
</dependency>
(2)启动服务端,在Application.main中加载:
private static void startClusterServer() { //启动集群流控服务端
try {
ClusterTokenServer tokenServer = new SentinelDefaultTokenServer();
ClusterServerConfigManager.loadGlobalTransportConfig(new ServerTransportConfig()
.setIdleSeconds(600) //闲置时间
.setPort(9999)); //服务端口
ClusterServerConfigManager.loadServerNamespaceSet(Collections.singleton("sentinel-cluster-server")); //服务名称
tokenServer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
2.Client端:
(1)添加Client端依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-cluster-client-default</artifactId>
<version>1.8.3</version>
</dependency>
(2)可以通过API将当前模式置为客户端模式:
http://192.168.233.128:9999/setClusterMode?mode=0
五、Web应用从Apollo拉取Sentinel规则:
1.添加Apollo+Sentinel依赖:
<dependencies>
<!-- 从Apollo拉取Sentinel规则的依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-apollo</artifactId>
<version>1.8.3</version>
</dependency>
<!-- 接入控制台实时监控 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.3</version>
</dependency>
</dependencies>
2.配置连接Apollo:
...
app:
id: apollo #apollo应用ID(必须与Apollo网页上创建项目时定义的应用Id一致)
apollo:
meta: http://192.168.233.128:8080 #apollo服务器地址
bootstrap:
enabled: true #注入默认application命名空间
namespaces: application #可以注入多个命名空间,以逗号分隔
3.Application中注册Apollo创建规则的回调事件,接收并加载规则:
@SpringBootApplication
public class SentinelApolloApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelApolloApplication.class);
String namespaceName = "application"; //application为Apollo命名空间
String ruleKey = "sentinelapollo-flow-rules"; //sentinelapollo-flow-rules为Apollo页面Key/Value列表的Key名(value为规则json,格式见defaultRuleValue)
String defaultRuleValue = "\"[{\\\"grade\\\":1,\\\"count\\\":1,\\\"resource\\\":\\\"资源名1\\\",\\\"limitApp\\\":\\\"default\\\",\\\"strategy\\\":\\\"0\\\"}]\""; //Apollo连接失败时使用的默认规则
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ApolloDataSource<>(namespaceName, ruleKey, defaultRuleValue, new Converter() {
@Override
public List<FlowRule> convert(Object ruleJson) { //Apollo页面上创建规则时,此方法会收到回调
System.out.println("ruleJson: " + ruleJson);
return JSON.parseObject((String) ruleJson, new TypeReference<List<FlowRule>>() {}); //将规则json解析为列表
}
});
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
}
4.使用拉取的规则,保护业务逻辑:
@RestController
public class UserController {
@RequestMapping("/user1")
public String user1(String userNo) { //(1)方式1,try-catch方式
try (Entry entry = SphU.entry("资源名1")) {
//此处为被保护的逻辑
} catch (BlockException ex) {
Tracer.trace(ex); //记录业务异常
//此处处理被限流/降级的逻辑
}
return "测试完成";
}
}
5.也可以手动添加规则,在Apollo应用Key/Value中:
(1)进入创建的应用,点击Key/Value列表的右上角"新增配置",添加Key和Value后,点发布生效:
(2)在Key中输入创建ApolloDataSource时ruleKey名(此处为myRule),在Value中输入规则json(json所有key必须与所属规则对象的属性名一致):
6.IntelliJ IDEA方式启动Web应用,Edit Configurations -> 左侧Application中选中ApolloApplication,右侧Configuration选项卡中,在VM options中填入:
-Dapollo.meta=http://192.168.233.128:8080 -Denv=dev -Dproject.name=sentinelapollo -Dcsp.sentinel.dashboard.server=192.168.233.129:8080 -Dcsp.sentinel.api.port=8719