目標
skywalking 默認情況會采集大量 trace 數據,這樣可以比較全的追蹤所有請求調用鏈路的請求,但同時對 ES 存儲資源要求非常高,需要我們投入很大的存儲節點才可以。那么有沒有一種采樣的請求上報的機制呢?答案是有的,通過設置采樣數據的比例,我們就可以在資源成本和采集條目之前取得一個平衡。
現狀
日常排查問題的現實情況往往是,我們只需要能采集到出現問題的請求鏈路即可,而不需要能夠采集所有請求的鏈路數據,在加了采樣條目設置后,有一部分 trace 數據會被丟掉,當然如果正好丟掉了我們出問題的 trace 請求,那么就需要我們穩定復現請求,從而控制概率采集到該問題數據。
skywalking 里面配置采樣條目
在 skwalking 的 agent 目錄下的 agent.conf 文件有采樣參數的設置 sample_n_per_3_secs,默認情況下該值 是 0 或者 -1 的情況下是關閉采樣功能,如果大于 0,則代表agent 3 秒內采集多少條數據上報 oap
agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE:40}
注意:該值的修改需要每次進行 agent 打包后生效
采樣功能的原理分析
SamplingService 類里面的 handleSamplingRateChanged 方案,會啟動一個線程,每隔 3 秒定時重置采樣值的計數器:
void handleSamplingRateChanged() {if (getSamplingRate() > 0) {if (!on) {on = true;this.resetSamplingFactor();ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(new DefaultNamedThreadFactory("SamplingService"));scheduledFuture = service.scheduleAtFixedRate(new RunnableWithExceptionProtection(this::resetSamplingFactor, t -> LOGGER.error("unexpected exception.", t)), 0, 3, TimeUnit.SECONDS);LOGGER.debug("Agent sampling mechanism started. Sample {} traces in 3 seconds.",getSamplingRate());}} else {if (on) {if (scheduledFuture != null) {scheduledFuture.cancel(true);}on = false;}}
}
計數器采用 java 并發包下面的原子類計數,從而確保多線程環境下該值的并發更新問題:
private void resetSamplingFactor() {samplingFactorHolder = new AtomicInteger(0);}
然后提供了一個方法,用于判斷是否到達采樣閾值:
public boolean trySampling(String operationName) {if (on) {int factor = samplingFactorHolder.get();if (factor < getSamplingRate()) {return samplingFactorHolder.compareAndSet(factor, factor + 1);} else {return false;}}return true;}
在這個方法里面可以看到,如果原子類 AtomicInteger 實例的 get 方法的值小于閾值,然后就進行一次 CAS 更新操作,當 CAS 成功時代表該 trace context 數據允許上報 oap,否則就代表達到了采樣閾值,該 trace context 數據丟棄。
上報還是不上報的邏輯在 ContextManagerExtendService 類的 createTraceContext 方法中可以找到:
public AbstractTracerContext createTraceContext(String operationName, boolean forceSampling) {AbstractTracerContext context;/** Don't trace anything if the backend is not available.*/if (!Config.Agent.KEEP_TRACING && GRPCChannelStatus.DISCONNECT.equals(status)) {return new IgnoredTracerContext();}int suffixIdx = operationName.lastIndexOf(".");if (suffixIdx > -1 && Arrays.stream(ignoreSuffixArray).anyMatch(a -> a.equals(operationName.substring(suffixIdx)))) {context = new IgnoredTracerContext();} else {SamplingService samplingService = ServiceManager.INSTANCE.findService(SamplingService.class);//如果該條打上了強制采樣標簽 或 滿足滿足采樣條件就可以直接上報 oapif (forceSampling || samplingService.trySampling(operationName)) {context = new TracingContext(operationName);} else {//否則就忽略該次 trace context, 不做任何處理context = new IgnoredTracerContext();}}return context;}
總結
通過合理的 skywalking 的采樣機制的設置,可以大大減輕服務端 ES 的存儲壓力,以及減少 agent 端消耗應用的的 cpu 和內存的資源,也包括減少上報 oap 網絡帶寬的占用等,從而達到在資源成本和采集請求覆蓋面得到一個平衡
參考
Table of Agent Configuration Properties | Apache SkyWalking