1. 程式人生 > >sleuth+zipkin自定義取樣率(九)

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   #個性化取樣率

在這裡插入圖片描述