sleuth+zipkin自定義取樣率(九)
阿新 • • 發佈:2018-12-11
問題背景
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 #個性化取樣率