Sentinel基于Apollo的持久化改造
sentinel默认是将用户在dashboard中编辑过的流控信息保存在内存中,所以在重启后,所有之前配置过的流控规则也就不见了。但是sentinel给用户提供了可扩展的接口,用户可以根据自己熟悉的持久化引擎来做一定的相应的改造(apollo、nacos、zookeeper)。这里首先要了解下sentinel的规则管理和推送方式
规则管理和推送
一般来说,规则的管理和推送有以下三种方式:
推送模式
说明
优点
缺点
默认模式
API 将规则推送至客户端并直接更新到内存中,扩展写数据源
简单,无任何依赖
不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境
pull 模式
扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件(数据库保存) 等
简单,无任何依赖;规则持久化
不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。
push 模式
扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。
规则持久化;一致性;快速
引入第三方依赖
所以简单的归纳来说,sentinel(基于push)的工作原理是:
通过dashboard来配置相关的流控信息。
配置好后的规则信息通过扩展数据源推送到与之连接到客户端。
客户端在启动的过程中读取这些扩展数据源的相关规则信息,并将它们保存到内存当中。(如果客户端在运行的过程当中,规则发生变动,那么数据源会实时推送已发生变动的规则信息到正在运行的客户端内存当中)。
有请求过来的时候,客户端会读取内存中的规则信息,来判断是否做限流操作。
对数据持久化的扩展
接口描述
sentinel提供了可扩展的读写接口,在重写这块逻辑的时候,只要重新实现这两个接口就好。
Provider
Publisher
Apollo操作的封装。
不管用zk、nacos还是apollo,我们要做的就是在数据变动的时候,给这些引擎推送相关信息来驱动这些引擎去做操作。所以,可以把这些操作都抽象出来。以AbstractRuleApolloProvider为例。这个抽象类的作用有:
启动的时候初始化apollo环境。
@Autowired
private ApolloOpenApiClient apolloOpenApiClient;
@Value("${auth.apollo.appId}")
private String appid;
private String env;
private String clusterName;
private String namespaceName;
@PostConstruct
public void init() {
env = System.getProperty("env", ApolloEnvConst.APOLLO_ENV_DEV);
clusterName = System.getProperty("cluster", ApolloEnvConst.APOLLO_DEFAULT_CLUSTER_NAME);
namespaceName = System.getProperty("namespace", ApolloEnvConst.APOLLO_DEFAULT_NAMESPACE);
}
封装对apollo通用的增删改查的方法。
/**
* 获取配置
*
* @param app
* @return
*/
public String getRulesByApollo(String app, String ruleType) {
String flowDataId = getRuleType(app, ruleType);
OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appid, env, clusterName, namespaceName);
return openNamespaceDTO
.getItems()
.stream()
.filter(p -> p.getKey().equals(flowDataId))
.map(OpenItemDTO::getValue)
.findFirst()
.orElse("");
}
/**
* 新增修改配置
*
* @param app
* @param rules
*/
public void publish2Apollo(String app, String ruleType, String rules) {
// Increase the configuration
String flowDataId = getRuleType(app, ruleType);
OpenItemDTO openItemDTO = new OpenItemDTO();
openItemDTO.setKey(flowDataId);
openItemDTO.setValue(rules);
openItemDTO.setComment("Program auto-join");
openItemDTO.setDataChangeCreatedBy("apollo");
apolloOpenApiClient.createOrUpdateItem(appid, env, clusterName, namespaceName, openItemDTO);
// Release configuration
NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
namespaceReleaseDTO.setEmergencyPublish(true);
namespaceReleaseDTO.setReleaseComment("Modify or add configurations");
namespaceReleaseDTO.setReleasedBy("apollo");
namespaceReleaseDTO.setReleaseTitle("Modify or add configurations");
apolloOpenApiClient.publishNamespace(appid, env, clusterName, namespaceName, namespaceReleaseDTO);
}
apollo数据序列化(按实际情况传递相应的实体)
@Bean
public Converter, String> flowRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
规则保存与推送
以降级(Degrade)为例
获取规则信息
@Component("degradeRuleApolloProvider")
public class DegradeRuleApolloProvider extends AbstractRuleApolloProvider implements DynamicRuleProvider> {
@Autowired
private Converter> converter;
@Override
public List getRules(String app) throws Exception {
String rules = super.getRulesByApollo(app, DEGRADE);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
获取、推送规则信息
@Component("degradeRuleApolloPublisher")
public class DegradeRuleApolloPublisher extends AbstractRuleApolloProvider implements DynamicRulePublisher> {
@Autowired
private Converter, String> converter;
@Override
public void publish(String app, List rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
super.publish2Apollo(app, DEGRADE, converter.convert(rules));
}
}
控制层改造
@Autowired
@Qualifier("sentinelApolloApiImpl")
private SentinelPersistenceApiService sentinelApiClient;