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)设计中,控制台是否显示服务名称,就是通过后端是计算出deadshown字段(不是直接赋值),前端根据这两个字段显示下拉列表。

服务列表.png

3. 设计思路

  1. dashboard集群化后,依旧在内存中维护心跳信息,其中包含项目名。
  2. 回显列表时,去读取consul中的规则信息appRules/项目名/规则类型,解析出规则发送过持久化的项目名
  3. 当请求经nginx负载到dashboard的某一台机器上时,dashboard取出内存维护的项目名,然后获取到consul中维护的项目名,两个set去并集。
  4. 并集中的项目名伪造存活状态,使得页面上可以正常回显。

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前后端交互。

上一篇下一篇

猜你喜欢

热点阅读