sleuth+zipkin自定义采样率(九)

问题背景

zipkin原生提供的采样率设置,仅能针对全局进行设置,无法做到精细化设置,比如,有些接口我们觉得比较重要,所以想采样率高一点,有些接口很简单,我们希望不采集或者采集率设置低一点,原生是没办法做到的,需要完成这个功能,需要我们重写他的采样率计算器。

配置重写

下面的是他原生的配置,由于它是使用了@ConditionalOnMissingBean注解的,也就是容器中不存在这个Bean的时候,才初始化他自己默认的配置,因此我们可以重写他的配置。

@Bean
@ConditionalOnMissingBean
public Sampler defaultTraceSampler(SamplerProperties config) {
	return new PercentageBasedSampler(config);
}

重写配置如下

@Bean
Sampler percentageLocalSampler(SamplerLocalProperties samplerLocalProperties){

   return new PercentageLocalSampler(samplerLocalProperties);
}

在Config类上面添加@EnableConfigurationProperties(SamplerLocalProperties.class) 用来创建我们自己的配置属性对象

代码书写

创建SamplerLocalProperties属性类

/**
 * @Author 张云和
 * @Date 2018/8/14
 * @Time 17:13
 */
@ConfigurationProperties("spring.sleuth.sampler")
@Data
public class SamplerLocalProperties {


    private List<UriSampleProperties> uriSample = new ArrayList<>(0);

    private float percentage = 0.1f;

}

创建UriSampleProperties类

/**
 * @Author 张云和
 * @Date 2018/8/14
 * @Time 17:39
 */
@Data
public class UriSampleProperties {

    /**
     * 自定义的采样率的接口uri正则表达式
     */
    private String uriRegex;

    private float uriPercentage = 0.1f;


}

编写percentageLocalSampler

public class PercentageLocalSampler implements Sampler {

    private final Map<String, BitSet> sampleDecisionsMap;
    private final SamplerLocalProperties configuration;
    private final String all = "all";
    private final Map<String, AtomicInteger> concurrentSampleCount;

    public Map<String, AtomicInteger> getConcurrentSampleCount(){
        return this.concurrentSampleCount;
    }

    public PercentageLocalSampler(SamplerLocalProperties configuration) {
        this.configuration = configuration;
        sampleDecisionsMap = buildRandomBit();
        concurrentSampleCount = new ConcurrentHashMap<>();
        // 设置全局的上报次数
        concurrentSampleCount.put(all, new AtomicInteger(0));
    }


    @Override
    public boolean isSampled(Span currentSpan) {
        if (currentSpan == null) {
            return false;
        }

        String uri = currentSpan.getName(); // 获取span中的请求uri
        uri = uri.replace("http://", "");
        AtomicInteger count = this.concurrentSampleCount.get(all); // 获取全局的访问率
        BitSet bitSet =  this.sampleDecisionsMap.get(all);  // 获取全局的bitSet
        float percentage = this.configuration.getPercentage();  // 获取全局的采样率
        for (UriSampleProperties sampleProperties : configuration.getUriSample()) {
            if (uri.matches(sampleProperties.getUriRegex())) {  // 正则匹配
                //匹配上了自定义采样率的正则
                synchronized (this){ // 多个线程会有并发问题,这里加个局部锁
                    if (!concurrentSampleCount.containsKey(uri)) { // 判断当前uri是否在map中
                        concurrentSampleCount.put(uri, new AtomicInteger(0));
                    }
                }
                count = concurrentSampleCount.get(uri); // 获取当前URI对应的访问次数
                bitSet = sampleDecisionsMap.get(sampleProperties.getUriRegex()); // 获取当前URI对应的bitSet
                percentage = sampleProperties.getUriPercentage(); // 获取当前URI对应的采样率
            }
        }


         if(percentage == 0.0f){ // 如果采样率是0 ,直接返回false
            return false;
        }else if (percentage == 1.0f){ // 如果采样率是1 ,那么直接返回true
             return true;
         }
        synchronized (this) {
            final int i = count.getAndIncrement(); // f访问次数加1
            boolean result = bitSet.get(i); // 判断当前的访问 次数是否在 bitSet中,存在则返回true
            if (i == 99) { // 等于99的时候,重新设置为0 
                count.set(0);
            }
            return result;
        }
    }

    /**
     * Reservoir sampling algorithm borrowed from Stack Overflow.
     * <p>
     * http://stackoverflow.com/questions/12817946/generate-a-random-bitset-with-n-1s
     */
    static BitSet randomBitSet(int size, int cardinality, Random rnd) {
        BitSet result = new BitSet(size);
        int[] chosen = new int[cardinality];
        int i;
        for (i = 0; i < cardinality; ++i) {
            chosen[i] = i;
            result.set(i);
        }
        for (; i < size; ++i) {
            int j = rnd.nextInt(i + 1);
            if (j < cardinality) {
                result.clear(chosen[j]);
                result.set(i);
                chosen[j] = i;
            }
        }
        return result;
    }

    private Map<String, BitSet> buildRandomBit() {
        Map<String, BitSet> map = new ConcurrentHashMap<>();
        // 设置全局的采样率
        int outOf100 = (int) (configuration.getPercentage() * 100.0f);
        map.put(all, randomBitSet(100, outOf100, new Random()));
        if (CollectionUtils.isNotEmpty(configuration.getUriSample())) {
            for (UriSampleProperties sampleProperties : configuration.getUriSample()) {
                // 设置个性化的采样率
                map.put(sampleProperties.getUriRegex(), randomBitSet(100,
                        (int) (sampleProperties.getUriPercentage() * 100.0f), new Random()));
            }
        }
        return map;
    }
}

客户端接入

spring
   sleuth:
    sampler:
      percentage: 1  # 全局采样率
      uri-sample:
      - uri-regex: "test|consumer"  # 正则匹配
        uri-percentage: 0.1   #个性化采样率

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u012394095/article/details/82785745