文章目录
前言
书接上文
springcloud-alibaba-sentinel(5)sentinel规则持久化至Nacos (一)
我们实现了Nacos编写Sentinel配置规则然后持久化,sentinel-dashboard控制台可以显示
但仍存在一个问题,那就是sentinel-dashboard编写的规则却没有保存到Nacos,导致sentinel客户端(我们的微服务)重启之后,我们的规则全部丢失
那么我们能不能实现 Nacos>sentinel 与 sentinel>nacos双向操作持久化呢?
为了解决这个问题,我们需要Down下Sentinel源码来看看究竟
Sentinel源码下载与编译
下载
源码git地址:https://github.com/alibaba/Sentinel/releases
选择ZIP下载解压后,拖入IDEA
项目结构展示
从源码中可以看到,sentinel的项目结构为单体多模块
项目编译运行
在项目根目录下打开Terminal执行命令
mvn clean package -Dmaven.test.skip=true
源码查看
流控规则控制器完全限定名为:
com.alibaba.csp.sentinel.dashboard.controller.FlowControllerV1
新增流控源码
发现最终是保存到了内存中
因为控制台的一切规则操作,默认都是保存在内存中的,所以其没有持久化到Nacos,以及Sentinel客户端或者DashBoard重启后,规则丢失了!
源码运行
将application.properties修改为YMl配置
auth:
filter:
exclude-url-suffixes: htm,html,js,css,map,ico,ttf,woff,png
exclude-urls: /,/auth/login,/auth/logout,/registry/machine,/version
password: sentinel
username: sentinel
logging:
file:
name: ${user.home}/logs/csp/sentinel-dashboard.log
level:
org:
springframework:
web: INFO
pattern:
file: '%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n'
sentinel:
dashboard:
version: '@project.version@'
server:
servlet:
encoding:
charset: UTF-8
enabled: true
force: true
session:
cookie:
name: sentinel_dashboard_cookie
port: 8080
选择sentinel-dashboard
模块的DashboardApplication
点击启动
访问URL即可完成登陆
Sentinel源码改造
目录
webapp/resources/app/scripts/directives/sidebar/sidebar.html
是sentinel前端页面
目录
webapp/resources/app/scripts/app.js
是sentinel前端页面访问后端控制器的JS配置,里面编写了哪个页面访问后端URL地址
下边我将以流控规则持久化为示例演示
ps: 因为源码中,流控规则持久化,作者已经为我们打了个样,我们只需要按照其逻辑,然后将底层实现修改为Nacos,即可实现控制台操作流控规则持久化到Nacos了
Sentinel—Dashboard控制台实现持久化原理
我们主要是改写Sentinel-dashboard后端接口,使用我们自定义的Nacos数据源,与Nacos进行交互,将我们新增 修改 删除的配置 同时推送到Nacos中
幸运的是,一切都是有轮子的,我们上边引入的sentinel-datasource-nacos
已经是有了相应的接口,我们只需要实现它即可
Sentinel 开发者为Nacos持久化数据打的样
Test目录下,已经有了相应的示例,如何连接与配置Nacos,如何推送Nacos
NacosConfig
:nacos相关配置
FlowRuleNacosProvider
:负责从Nacos读取配置
FlowRuleNacosPublisher
:负责将Sentinel-dashboard配置推送至Nacos服务端
修改流控前端页面
因为项目中流控有V1 V2 两个控制器,V2版本是作者打的样,因此我们需要修改前端访问后端的逻辑 由请求V1 修改为请求V2
修改为
<li ui-sref-active="active" ng-if="!entry.isGateway">
<a ui-sref="dashboard.flow({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 流控规则</a>
</li>
核心修改是 dashboard.flowV1
更改为dashboard.flow
去掉了V1后缀
我们可以Ctrl + 鼠标左键 点击去查看,无V1 后缀的实际是请求的V2控制器
开启Nacos数据源支持
sentinel-dashboard中,默认引入了Nacos数据源,但其作用范围为test,我们需要修改一下
Nacos配置
我们需要根据自己的实际情况配置Nacos相关属性,比如是账号密码,服务地址 命名空间等等
详细的配置项支持请查看源码:(其是一个常量类,里边定义了Nacos的一些可支持配置)
com.alibaba.nacos.api.PropertyKeyConst
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
/**
* @author lei
* @create 2022-04-13 13:57
* @desc nacos配置
**/
@Configuration
public class NacosConfig {
@Bean
public ConfigService nacosConfigService() throws Exception {
Properties properties = new Properties();
// 我这里指明了Nacos地址
properties.put(PropertyKeyConst.SERVER_ADDR, "http://10.50.40.116:8765");
// 指明了命名空间
properties.put(PropertyKeyConst.NAMESPACE, "c0fed0b5-46a6-44b9-b850-44c2ae0d7fe7");
return ConfigFactory.createConfigService(properties);
}
}
需要注意的是,如果指定的命名空间需要在Nacos中先进行创建
公共Nacos推送拉取实现
我们根据开发者为我们打的样,对Nacos读取配置,以及推送配置两个动作做一个抽取,这样的话,其他任何规则需要持久化Nacos都只需要调用公共方法即可
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;
import com.alibaba.csp.sentinel.slots.block.Rule;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author lei
* @create 2022-04-13 15:17
* @desc nacos操作类
**/
@Service
public class NacosSupport {
private final ConfigService configService;
private static final Logger logger = LoggerFactory.getLogger(NacosSupport.class);
private NacosSupport(ConfigService configService) {
this.configService = configService;
}
public static final String GROUP_ID = "DEFAULT_GROUP";
public static final String RULE_PREFIX = "-sentinel";
public static final String FLOW_DATA_ID_POSTFIX = RULE_PREFIX + "-rule-flow";
public static final String DEGRADE_DATA_ID_POSTFIX = RULE_PREFIX + "-rule-degrade";
public static final String SYSTEM_DATA_ID_POSTFIX = RULE_PREFIX + "-rule-system";
public static final String PARAM_FLOW_DATA_ID_POSTFIX = RULE_PREFIX + "-rule-param-flow";
public static final String AUTHORITY_DATA_ID_POSTFIX = RULE_PREFIX + "-rule-authority";
public static final String DASHBOARD_POSTFIX = "-dashboard";
public static final String CLUSTER_MAP_DATA_ID_POSTFIX = RULE_PREFIX + "-cluster-map";
public static final String TYPE = "json";
/**
* 将规则序列化成JSON文本,存储到Nacos-server中 (nacos持久化的数据库,可能是内存,可能是DB,一切看Nacos的持久化配置是否开启Mysql存储)
*
* @param app 应用名称
* @param postfix 规则后缀
* @param rules 规则对象
* @throws NacosException 异常
*/
public <T> void pushRule(String app, String postfix, List<T> rules) throws NacosException {
if (StringUtil.isBlank(app)) {
throw new RuntimeException("位置APP-NAME");
}
if (rules == null) {
return;
}
List<Rule> ruleForApp = rules.stream()
.map(rule -> {
RuleEntity rule1 = (RuleEntity) rule;
System.out.println("RuleEntity " + rule1);
Rule rule2 = rule1.toRule();
System.out.println("Rule " + rule2);
return rule2;
})
.collect(Collectors.toList());
// 规则消息推送至nacos
String dataId = genDataId(app, postfix);
configService.publishConfig(dataId, NacosSupport.GROUP_ID, JSON.toJSONString(ruleForApp), TYPE);
}
/**
* 从Nacos server中查询响应规则,并将其反序列化成对应Rule实体
*
* @param appName 应用名称
* @param postfix 规则后缀
* @param clazz 类
* @param <T> 泛型
* @return 规则对象列表
* @throws NacosException 异常
*/
public <T> List<T> getRule(String appName, String postfix, Class<T> clazz) throws NacosException {
String rules = configService.getConfig(genDataId(appName, postfix), NacosSupport.GROUP_ID, 3000);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return JSON.parseArray(rules, clazz);
}
private static String genDataId(String appName, String postfix) {
return appName + postfix;
}
}
流控规则生产者
从nacos拉取规则
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author lei
* @create 2022-04-13 14:18
* @desc 流控生产者
**/
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
private final NacosSupport nacosSupport;
public FlowRuleNacosProvider(NacosSupport nacosSupport) {
this.nacosSupport = nacosSupport;
}
@Override
public List<FlowRuleEntity> getRules(String appName) throws Exception {
return nacosSupport.getRule(appName, NacosSupport.FLOW_DATA_ID_POSTFIX, FlowRuleEntity.class);
}
}
流控规则推送者
从
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author lei
* @create 2022-04-13 14:20
* @desc 流控规则推送者
**/
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
private final NacosSupport nacosSupport;
public FlowRuleNacosPublisher(NacosSupport nacosSupport) {
this.nacosSupport = nacosSupport;
}
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
nacosSupport.pushRule(app, NacosSupport.FLOW_DATA_ID_POSTFIX, rules);
}
}
修改流控规则控制器
主要是替换规则生产与推送者为我们自己定义的Nacos流控相关服务,其他无需改动
– – – – – – – – – – – ---- – – – – ---- – – – – ---- – – – – ---- – – – – ---- – – – – ---- – – – – ---- – – – – ---- – – – – ---- – – – – ---- – – – – ---- – – – – ---- – – –
以上便是Sentinel-Dashboard规则持久化的全部改造,其他规则可以参照上方逻辑,继续修改,抓住修改核心(nacos的流控生产与推送者)
比如我们的熔断规则修改
熔断规则修改
由于熔断规则前端所用JS 没有像限流一样分为V1 V2,因此无需改动前端页面,只需要实现自己的Nacos数据推送者以及生产者即可
修改控制器
源码代码中使用的都是基于内存操作的数据
我们需要替换为自己的Nacos相关实现
改造结果展示
开始无任何配置
Sentinel-dashboard页面操作规则配置
我们期望的是 Sentinel-dashboard页面操作后,数据能同步到Nacos中
sentinel源码中打印了我们的信息
Nacos页面查看
Nacos页面操作规则配置
我们期望的是Nacos页面修改配置规则后,Sentinel-Dashboard页面能够接受到该配置
修改流控阈值为2
控制台页面查看
sentinel 服务重启或者我们的微服务app-base-center重启后,流控规则依然存在
到此…sentinel源码就修改完毕了!