针对开源版本Sentinel,整体来说要做的就是两步
官方版本Sentinel Dashboard,有一个明显缺点,所有的限流规则配置都存储在内存中,重启会丢失,正好公司采用了Apollo做配置中心,本文介绍Dashboard中如何修改才能将限流规则推送到Apollo的配置中心,以及客户端如何从Apollo拉取对应限流规则,也会附上我修改后的Dashboard源码,大牛直接看代码就行
先贴上项目地址,觉得可以的,给个star
https://github.com/hosaos/apollo-sentinel-dashboard
要针对Dashboard做修改,关键在于两点
在具体上代码之前,先介绍下Sentinel官方针对配置中心规则推送及拉取,封装的两个接口DynamicRuleProvider
及DynamicRulePublisher
DynamicRuleProvider是规则拉取接口,实现getRules方法从指定配置中心拉取规则
public interface DynamicRuleProvider {
T getRules(String appName) throws Exception;
}
DynamicRulePublisher是规则推送接口,实现publish方法将规则推送到指定配置中心
public interface DynamicRulePublisher {
/**
* 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配置相关的类ApolloConfig
,ApolloConfigUtil
@Configuration
public class ApolloConfig {
@Bean
public ApolloOpenApiClient apolloOpenApiClient() {
ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()
.withPortalUrl("")
.withToken("")
.build();
return client;
}
}
其中ApolloConfig保存Apollo OpenAPI相关信息,要实现规则推送到Apollo配置中心,必须调用Apollo的OpenApi,ApolloOpenApiClient初始化时需要两个参数
新建自己的第三方应用,别设置能够操作的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 flowRuleEntityList = (List) 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 getRules(String appName) throws Exception {
return super.getRules(appName);
}
@Override
protected String getDataId() {
return ApolloConfigUtil.getFlowDataId();
}
@Override
protected Class getRuleClazz() {
return FlowRuleEntity.class;
}
}
规则配置完了,还需要修改对应Controller,flowRule对应Controller为FlowControllerV1
1、先注入flowRule对应类型的规则拉取,推送实现类
2、修改规则查询接口,改为从ruleProvider获取Apollo中限流配置
4、删除Controller中关于ip、port的限制,基于Apollo的配置只和appName有关,和ip、port无关,故删除所有相关判断逻辑
Sentinel中除了FlowRule,还有其他几种类型限流,分别为ParamFlowRule(热点参数规则),SystemRule(系统规则),DegradeRule(降级规则),AuthorityRule(授权规则),修改思路都类似,总体分为几步
下篇文章将会介绍客户端如何集成spring-cloud-starter-alibaba-sentinel实现与sentinel的集成,并介绍客户端如何监听Apollo中的规则,实现实时规则更新