Sentinel-开源版本Dashboard集成Apollo配置中心


Sentinel由于之前接的云上版本的控制台,在集群限流及功能上有限制,现打算新接入开源版本Sentinel,做一些定制化的修改

针对开源版本Sentinel,整体来说要做的就是两步

  1. 第一步要做的就是搭建Sentinel Dashboard
  2. 第二步要做的就是客户端接入Sentinel

官方版本Sentinel Dashboard,有一个明显缺点,所有的限流规则配置都存储在内存中,重启会丢失,正好公司采用了Apollo做配置中心,本文介绍Dashboard中如何修改才能将限流规则推送到Apollo的配置中心,以及客户端如何从Apollo拉取对应限流规则,也会附上我修改后的Dashboard源码,大牛直接看代码就行

先贴上项目地址,觉得可以的,给个star

https://github.com/hosaos/apollo-sentinel-dashboard

要针对Dashboard做修改,关键在于两点

  1. Dashboard中规则查询接口,要改为从配置中心拉取(规则拉取)
  2. Dashboard中规则保存接口,要改为推送配置到配置中心(规则推送)

在具体上代码之前,先介绍下Sentinel官方针对配置中心规则推送及拉取,封装的两个接口DynamicRuleProviderDynamicRulePublisher

DynamicRuleProvider是规则拉取接口,实现getRules方法从指定配置中心拉取规则

public interface DynamicRuleProvider<T> {
    T getRules(String appName) throws Exception;
}

DynamicRulePublisher是规则推送接口,实现publish方法将规则推送到指定配置中心

public interface DynamicRulePublisher<T> {

    /**
     * Publish rules to remote rule configuration center for given application name.
     *
     * @param app app name
     * @param rules list of rules to push
     * @throws Exception if some error occurs
     */
    void publish(String app, T rules) throws Exception;
}

Apollo规则推送、拉取的实现均基于这两个接口

相关配置

先贴上两个Apollo配置相关的类ApolloConfigApolloConfigUtil

@Configuration
public class ApolloConfig {

    @Bean
    public ApolloOpenApiClient apolloOpenApiClient() {
        ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()
            .withPortalUrl("")
            .withToken("")
            .build();
        return client;
    }
}

其中ApolloConfig保存Apollo OpenAPI相关信息,要实现规则推送到Apollo配置中心,必须调用Apollo的OpenApi,ApolloOpenApiClient初始化时需要两个参数

  1. portalUrl:Apollo配置中心的访问地址
  2. token:通过Apollo中开放平台配置相关第三方应用获取,如下图所示

在这里插入图片描述
新建自己的第三方应用,别设置能够操作的appId,即可生成token
在这里插入图片描述

第二个配置类ApolloConfigUtil,保存不同的规则在配置中心里的key值,以及保存到Apollo中的默认namespace名称

public final class ApolloConfigUtil {
    /**
     * 流控规则id
     */
    public static final String FLOW_DATA_ID_POSTFIX = "sentinel-flow-rules";
    /**
     * 降级规则id
     */
    public static final String DEGRADE_DATA_ID_POSTFIX = "sentinel-degrade-rules";
    /**
     * 热点规则id
     */
    public static final String PARAM_FLOW_DATA_ID_POSTFIX = "sentinel-param-flow-rules";
    /**
     * 系统规则id
     */
    public static final String SYSTEM_DATA_ID_POSTFIX = "sentinel-system-rules";
    /**
     * 授权规则id
     */
    public static final String AUTHORITY_DATA_ID_POSTFIX = "sentinel-authority-rules";
    /**
     * 规则存储nameSpace
     */
    public static final String NAMESPACE_NAME = "application";

    private ApolloConfigUtil() {
    }

    public static String getFlowDataId() {
        return FLOW_DATA_ID_POSTFIX;
    }

    public static String getDegradeDataId() {
        return DEGRADE_DATA_ID_POSTFIX;
    }

    public static String getParamFlowDataId() {
        return PARAM_FLOW_DATA_ID_POSTFIX;
    }

    public static String getSystemDataId() {
        return SYSTEM_DATA_ID_POSTFIX;
    }

    public static String getAuthorityDataId() {
        return AUTHORITY_DATA_ID_POSTFIX;
    }

    public static String getNamespaceName() {
        return NAMESPACE_NAME;
    }
}

配置中默认namespace为application,即每个应用只在application中管理自己的限流规则,先看个Apollo中规则存储的效果图,我新建了一个Sentinel的应用,其在application默认namespace中管理自己的限流规则
在这里插入图片描述

规则推送

下面以FlowRule为例子,介绍如何将规则推送到Apollo
新建BaseApolloRulePublisher,这是个所有规则推送公用的抽象基类,代码如下,核心代码在pushRulesToApollo方法中,根据appName,及dataId(dataId即为ApolloConfigUtil配置的不同规则的key值)将规则推送到Apollo,我给Dashboard也加上了环境变量,针对不同环境推送到不同配置到Apollo环境集群中

有一个注意点是,这边默认取了Sentinel中的projectName(appName)来当做apollo的appId去拉取/推送相关配置,所以要保证客户端注册到Sentinel时的projectName与Apollo配置中心中的appId一致

setReleasedBy方法中的名称为Apollo中用户名,需要配置为有相关appId操作权限的用户名,否则会报错

@Component
public abstract class BaseApolloRulePublisher implements DynamicRulePublisher {

    @Autowired
    protected ApolloOpenApiClient apolloOpenApiClient;
    @Value("${spring.profiles.active}")
    private String env;

    @Override
    public void publish(String app, Object rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        String dataId = getDataId();

        pushRulesToApollo(app, dataId, rules);
    }

    protected abstract String getDataId();

    /**
     * 推送规则到apollo
     *
     * @param appName
     * @param dataId
     * @return
     */
    protected void pushRulesToApollo(String appName, String dataId, Object rules) {
        String namespaceName = ApolloConfigUtil.getNamespaceName();

        OpenItemDTO openItemDTO = new OpenItemDTO();
        openItemDTO.setKey(dataId);
        openItemDTO.setValue(JSON.toJSONString(rules));
        openItemDTO.setDataChangeCreatedBy("chenyin");
        apolloOpenApiClient.createOrUpdateItem(appName, env, "default", namespaceName, openItemDTO);

        // Release configuration
        NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
        namespaceReleaseDTO.setEmergencyPublish(true);
        namespaceReleaseDTO.setReleasedBy("chenyin");
        namespaceReleaseDTO.setReleaseTitle("Modify or add configurations");
        apolloOpenApiClient.publishNamespace(appName, env, "default", namespaceName, namespaceReleaseDTO);
    }
}

再贴上FlowRuleApolloPublisher代码

@Component("flowRuleApolloPublisher")
public class FlowRuleApolloPublisher extends BaseApolloRulePublisher {

    @Override
    public void publish(String app, Object rules) throws Exception {
        List<FlowRuleEntity> flowRuleEntityList = (List<FlowRuleEntity>) rules;
        //去掉一些无用属性
        for (FlowRuleEntity flowRuleEntity : flowRuleEntityList) {
            flowRuleEntity.setGmtCreate(null);
            flowRuleEntity.setGmtModified(null);
            flowRuleEntity.setIp(null);
            flowRuleEntity.setPort(null);
        }

        super.publish(app, rules);
    }

    @Override
    protected String getDataId() {
        return ApolloConfigUtil.getFlowDataId();
    }
}

其他规则,同理实现,不再贴代码

规则拉取

新建BaseApolloRuleProvider,核心逻辑在getRulesFromApollo中,先从Apollo中根据
appName及namespace名称获取对应限流规则,在过滤出对应类型的限流规则即可

@Component
public abstract class BaseApolloRuleProvider implements DynamicRuleProvider {

    @Autowired
    protected ApolloOpenApiClient apolloOpenApiClient;

    @Value("${spring.profiles.active}")
    private String env;

    @Override
    public List getRules(String appName) throws Exception {
        String flowDataId = getDataId();
        String rules = getRulesFromApollo(appName, flowDataId);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return JSON.parseArray(rules, getRuleClazz());
    }

    /**
     * 获取流控规则在apollo中key值
     *
     * @return
     */
    protected abstract String getDataId();

    /**
     * 获取流控规则对应clazz,JSON转换用
     *
     * @return
     */
    protected abstract Class getRuleClazz();

    /**
     * 从apollo获取配置
     *
     * @param appName
     * @param dataId
     * @return
     */
    protected String getRulesFromApollo(String appName, String dataId) {
        OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appName,
                env, "default", ApolloConfigUtil.getNamespaceName());
        String rules = openNamespaceDTO
                .getItems()
                .stream()
                .filter(p -> p.getKey().equals(dataId))
                .map(OpenItemDTO::getValue)
                .findFirst()
                .orElse("");

        return rules;
    }

}

附上FlowRuleApolloProvider的代码实现,只需要实现getDataId返回该类型限流规则对应的规则dataId,并提供该类型的class供反序列化即可

@Component("flowRuleApolloProvider")
public class FlowRuleApolloProvider extends BaseApolloRuleProvider {

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        return super.getRules(appName);
    }

    @Override
    protected String getDataId() {
        return ApolloConfigUtil.getFlowDataId();
    }

    @Override
    protected Class getRuleClazz() {
        return FlowRuleEntity.class;
    }
}

Controller修改

规则配置完了,还需要修改对应Controller,flowRule对应Controller为FlowControllerV1
1、先注入flowRule对应类型的规则拉取,推送实现类
在这里插入图片描述

2、修改规则查询接口,改为从ruleProvider获取Apollo中限流配置
在这里插入图片描述

3、修改规则推送接口,推送规则到Apollo
在这里插入图片描述

4、删除Controller中关于ip、port的限制,基于Apollo的配置只和appName有关,和ip、port无关,故删除所有相关判断逻辑
在这里插入图片描述

小结

Sentinel中除了FlowRule,还有其他几种类型限流,分别为ParamFlowRule(热点参数规则),SystemRule(系统规则),DegradeRule(降级规则),AuthorityRule(授权规则),修改思路都类似,总体分为几步

  1. 继承BaseApolloRuleProvider,实现对应规则拉取逻辑
  2. 继承BaseApolloRulePublisher,实现规则推送逻辑
  3. 修改对应规则所在Controller,修改拉取,推送代码,从自定义RuleProvider,RulePublisher中拉取、推送规则
  4. 删除port、ip相关限制代码

下篇文章将会介绍客户端如何集成spring-cloud-starter-alibaba-sentinel实现与sentinel的集成,并介绍客户端如何监听Apollo中的规则,实现实时规则更新

发布了43 篇原创文章 · 获赞 134 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/hosaos/article/details/96305016