zipkin+sleuth基于apollo配置中心动态刷新采样率(十四)
前言
前文我们介绍过,自定义采样率,但是为了满足项目的灵活性,我们最好可以提供在程序运行过程中,可以动态的修改 采样率等信息,如此才能应对项目运行当中碰到的各种问题。
项目运行过程中,我们可能会遇到下面这些问题。
1.某个接口的采样率现在比较高? 我想调低一点怎么办?
2.这个接口太频繁,并且没有追踪的意义,我想禁用掉怎么办?
3.全局采样率的动态调整
默认zipkin是不支持上面的这些功能的,但是我们可以基于分布式配置中心进行扩展,我这边使用的是Apollo配置中心。本次样例中,也是以这个为基础的。
PS : 本文讲的这个动态刷新的做法,都是会放到公共的starter包里面去,供其他服务调用的。
增加配置
添加pom文件
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.0.8</version>
<scope>provided</scope>
</dependency>
添加配置
@Bean
@ConditionalOnClass(value = {ConfigService.class})
ApolloRefreshListener apolloRefreshListener() {
return new ApolloRefreshListener();
}
监听类
package zipkin.core.listener;
@Slf4j
public class ApolloRefreshListener implements ApplicationContextAware {
private ApplicationContext applicationContext;
private ConfigUtil m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
@ApolloConfigChangeListener
public void onChange(ConfigChangeEvent changeEvent) {
try {
Config config = ConfigService.getAppConfig();
if (m_configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
Float percentage = null; // 全局采样率
Map<Integer, UriSampleRefreshProperties> uriSamplePropertiesMap = new HashMap<>();
boolean isSampleChange = false;
for (String key : changeEvent.changedKeys()) { // 循环变更的属性key
ConfigChange configChange = changeEvent.getChange(key); // 获取当前属性的前后变化的对象,里面有最新值和旧值
if (ZipkinConfigKeyEnum.PERCENTAGE.getKey().equals(key)) { // 全局采样率变更
// 全局采样率, 需要动态刷新全局采样率
isSampleChange = true;
if(StringUtils.isBlank(configChange.getNewValue())){ // 当最新的值被置空时,全局采样率默认为0
percentage = 0.0f;
continue;
}
percentage = Float.parseFloat(configChange.getNewValue());
} else if (ZipkinConfigKeyEnum.SKIP_PATTERN.getKey().equals(key)) {
// spring.sleuth.web.skipPattern 发生变更
// 禁用的URI,动态刷新
TraceLocalFilter traceLocalFilter = (TraceLocalFilter) applicationContext.getBean("traceFilter");
// 获取traceFilter, 更新里面的skipPattern对象
traceLocalFilter.setSkipPattern(PatternUtil.defaultSkipPattern(configChange.getNewValue()));
} else if (key.startsWith(ZipkinConfigKeyEnum.URI_SAMPLE.getKey())) {
// 自定义的采样率更新,当添加了接口的自定义采样率之后,需要更新
isSampleChange = true;
Integer index = Integer.parseInt(key.substring(key.indexOf("[") + 1, key.lastIndexOf("]")));
if (!uriSamplePropertiesMap.containsKey(index)) {
uriSamplePropertiesMap.put(index, new UriSampleRefreshProperties());
}
if (key.endsWith(ZipkinConfigKeyEnum.URI_LIST.getKey())) {
// uri
uriSamplePropertiesMap.get(index).setUriList(configChange.getNewValue());
} else if (key.endsWith(ZipkinConfigKeyEnum.URI_PERCENTAGE.getKey()) && null!=configChange.getNewValue()) {
// uri采样率
uriSamplePropertiesMap.get(index).setUriPercentage(Float.parseFloat(configChange.getNewValue()));
}
}
}
// 采样率是否发生过变化,
if (isSampleChange) {
// 获取采样率对象
PercentageLocalSampler percentageLocalSampler = (PercentageLocalSampler) applicationContext.
getBean("percentageLocalSampler");
//uriSamplePropertiesMap针对的是接口级别的采样率
for (Map.Entry<Integer, UriSampleRefreshProperties> entry : uriSamplePropertiesMap.entrySet()) {
UriSampleRefreshProperties uriSampleProperties = entry.getValue();
//当采样率修改了,uri没有改,则需要从Apollo中读取uri
if (StringUtils.isBlank(uriSampleProperties.getUriList()) && null != uriSampleProperties.getUriPercentage()) {
String uriListKey = ZipkinConfigKeyEnum.URI_SAMPLE.getKey() + "[" + entry.getKey() + "]." +
ZipkinConfigKeyEnum.URI_LIST.getKey();
String value = config.getProperty(uriListKey, "");
uriSampleProperties.setUriList(value);
}
//当uri修改了,uri对应的采样率没有改,则需要从Apollo中读取对应的采样率配置
if (StringUtils.isNotBlank(uriSampleProperties.getUriList()) && null == uriSampleProperties.getUriPercentage()) {
String uriPercentageKey = ZipkinConfigKeyEnum.URI_SAMPLE.getKey() + "[" + entry.getKey() + "]." +
ZipkinConfigKeyEnum.URI_PERCENTAGE.getKey();
float value = config.getFloatProperty(uriPercentageKey, 0.0f);
uriSampleProperties.setUriPercentage(value);
}
}
// 重置采样率
percentageLocalSampler.initPercentage(percentage,uriSamplePropertiesMap);
}
}
} catch (Exception e) {
log.error("", e);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
PercentageLocalSampler 这个类可以看我之前写过的那边,自定义采样率,此处仅扩展了一个方法。initPercentage
public void initPercentage(Float percentage, Map<Integer, UriSampleRefreshProperties> uriSamplePropertiesMap) {
int size = 0;
// 判断原来是否设置过接口级别的采样率
if (!uriSamplePropertiesMap.isEmpty() && CollectionUtils.isEmpty(configuration.getUriSample())) {
configuration.setUriSample(new ArrayList<>(0));
}else{
// 获取原来的接口list size
size = configuration.getUriSample().size();
}
// 更新configuration对象的信息
for (Map.Entry<Integer, UriSampleRefreshProperties> entry : uriSamplePropertiesMap.entrySet()) {
Integer index = entry.getKey();
UriSampleProperties uriSampleProperties = null;
if(index<=(size-1)){
uriSampleProperties = configuration.getUriSample().get(index);
}else{
uriSampleProperties = new UriSampleProperties();
configuration.getUriSample().add(uriSampleProperties);
}
uriSampleProperties.setUriList(entry.getValue().getUriList());
uriSampleProperties.setUriPercentage(entry.getValue().getUriPercentage());
}
if(null!=percentage){
// 当percentage不为空时,表示全局采样率被修改
configuration.setPercentage(percentage);
}
concurrentUriMap.clear();
// 刷新采样率
buildUriSampleData();
}
至此,当Apollo配置中心的配置有变更的时候,就会被监听类监听到,然后调用刷新方法,动态进行刷新。