sentinel限流二开(1)—伪造服务存活状态,dashboa
2021-08-19 本文已影响0人
小胖学编程
对sentinel的二开,本着少改动源码的规则,实现规则数据的持久化。
1. 系列文章
【consul入门篇-1】JAVA如何使用consul作为KV存储中心
sentinel开源限流方案—企业级二次开发方案
2. 难点分析
client端会向dashboard发送心跳。dashboard会在内存中维护上一次心跳时间来达到判断机器存活的目的。
sentinel设计图设计思路:client发送心跳,心跳包含版本号、ip信息、服务名等,dashboard进行校验。当发现client版本号过低时,dashboard根据版本号中的ip信息,推送最新的配置。
在该设计思路中:我们可以看出,不需要维护机器列表的存活状态,达到一个被动式的配置同步。
但是:在目前sentinel(版本1.8.1)设计中,控制台是否显示服务名称,就是通过后端是计算出dead
和shown
字段(不是直接赋值),前端根据这两个字段显示下拉列表。
3. 设计思路
- dashboard集群化后,依旧在内存中维护心跳信息,其中包含项目名。
- 回显列表时,去读取consul中的规则信息
appRules/项目名/规则类型
,解析出规则发送过持久化的项目名
。 - 当请求经nginx负载到dashboard的某一台机器上时,dashboard取出内存维护的项目名,然后获取到consul中维护的项目名,两个set去并集。
- 为并集中的项目名伪造存活状态,使得页面上可以正常回显。
4. 代码实现
为了最大程度上不修改sentinel的源码,采用继承的方式,子类去重写父类的回显方法,完成存活状态的伪造。
@Service
public class SimpleMachineDiscoveryDecorator extends SimpleMachineDiscovery {
private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class);
private ConsulClient consulClient;
public SimpleMachineDiscoveryDecorator(ConsulClient consulClient) {
this.consulClient = consulClient;
}
@Override
public Set<AppInfo> getBriefApps() {
Set<String> consulSet = getAppNameFromConsul();
//原始请求
Set<AppInfo> apps = super.getBriefApps();
Set<String> memSet = apps.stream().map(AppInfo::getApp).collect(Collectors.toSet());
//集合合并
consulSet.addAll(memSet);
//伪造请求
return consulSet.stream().map(this::forgeAppInfo).collect(Collectors.toSet());
}
@Override
public AppInfo getDetailApp(String app) {
AppInfo appInfo = super.getDetailApp(app);
//伪造存活
if(appInfo==null){
appInfo = forgeAppInfo(app);
}
return appInfo;
}
/**
* 在consul中获取应用名
*/
private Set<String> getAppNameFromConsul() {
Response<List<String>> appRules = consulClient.getKVKeysOnly("appRules");
List<String> results = appRules.getValue();
//规则的截取-appRules/项目名/规则名
Set<String> consulSet = new HashSet<>();
if (results != null) {
for (String result : results) {
try {
String[] arr = result.split("/");
consulSet.add(arr[1]);
} catch (Exception e) {
logger.error("", e);
}
}
}
return consulSet;
}
/**
* 伪造appInfo的请求
*/
private AppInfo forgeAppInfo(String appName) {
AppInfo appInfo = new AppInfo();
appInfo.setApp(appName);
appInfo.setAppType(0);
MachineInfo machineInfo = new MachineInfo();
machineInfo.setApp(appName);
machineInfo.setAppType(0);
machineInfo.setHeartbeatVersion(System.currentTimeMillis());
machineInfo.setLastHeartbeat(System.currentTimeMillis() - 1);
machineInfo.setIp("127.0.0.1");
machineInfo.setHostname("localhost");
machineInfo.setVersion("1.8.1");
machineInfo.setPort(8011);
appInfo.setMachines(Collections.singleton(machineInfo));
return appInfo;
}
}
上面代码解决了分布式环境下dashboard如何处理心跳问题。依旧如何在对sentinel改动量下的情况下,还原dashboard前后端交互。