1. 程式人生 > 其它 >有了這款工具,定位線上問題事半功倍|雲效工程師指北

有了這款工具,定位線上問題事半功倍|雲效工程師指北

大家好,我叫劉玄,負責雲效流水線的開發。程式設計師在日常工作中經常會遇到一些線上問題需要排查,本文的主人公程式設計師小張也不例外。但排查的過程卻時常令他困擾不已。讓我們一起看看他遇到了哪些問題,又是怎麼解決的。

焦頭爛額的一天

那是一個陽光明媚的上午,小張來到工位,開啟電腦,備上咖啡,精神滿滿的開始了一天的工作。正在小張噼裡啪啦的敲著鍵盤,認真Coding之時,釘釘群裡的一個釘,打破了寧靜。客服人員反饋,有客戶遇到了一個問題,需要開發人員排查。小張排查了線上日誌,發現使用者的請求比較多,日誌也比較多,沒有定位到關鍵資訊。小張只能又讓客服找使用者提供更具體的資訊。在和使用者反覆進行溝通之後,小張最終花了半個多小時才定位到了問題。

忙碌的一天很快結束,正當小張準備下班,籌劃著下班之後怎麼happy時,電話報警的聲音,又把他拉回了現實。小張收到後端服務RT高的告警後,趕緊排查多個後臺應用的監控資訊和日誌。雖然很快從Nginx日誌定位到了有問題的請求資訊,但小張很難精確的找到這個請求對應的應用日誌,所以花費了很長時間才定位到問題:一個第三方服務異常,導致部分功能受影響。定位到原因後,及時採取了降級手段,系統恢復正常。

尋求解決問題的方案

過完了焦頭爛額的一天,小張覺得現在處理問題的效率太低,大把的時間花在了問題定位上。而之所以排查的這麼慢,是因為系統採用微服務架構,一個請求會涉及到多個服務,並且每個服務還會呼叫DB、快取以及其他第三方服務。大致鏈路如下:

 

 

小張想,應該有成熟的技術方案,能夠標識整個請求鏈路,將異常服務節點清晰標註。小張藉助搜尋工具,發現有一種解決方案,鏈路追蹤,剛好適合自己的場景。

鏈路追蹤工具可以將一次分散式請求還原成完整的呼叫鏈路,將整個請求的呼叫情況進行展示,比如各個服務上的耗時、各個服務的請求狀態、具體排程到各個服務的哪臺機器上等資訊。

改造系統

期望的效果

根據前面遇到的兩個問題,小張期望:

  1. 使用者請求遇到問題時候,可以獲取到一個traceId,只要提供了這個traceId,就可以看到這個請求在各個服務之間的呼叫路徑。並且可以通過這個traceId查到所有應用中相關的日誌。
  2. 當收到RT告警時,也能夠從Nginx的日誌中找到這個traceId。

接入鏈路追蹤

經過技術選型,小張選擇阿里雲的產品鏈路追蹤Tracing Analysis作為自己鏈路追蹤的服務端。

阿里雲鏈路追蹤Tracing Analysis提供了完整的呼叫鏈路還原、呼叫請求量統計、鏈路拓撲、應用依賴分析等工具,可以幫助使用者快速分析和診斷分散式應用架構下的效能瓶頸,提高微服務時代下的開發診斷效率。

阿里雲鏈路追蹤Tracing Analysis支援多種常見的鏈路追蹤工具,例如Zipkin、Skywalking、Jaeper等。小張選擇使用Skywalking作為鏈路追蹤資料埋點。

在阿里雲上開通完鏈路追蹤Tracing Analysis 產品之後,就可以在叢集配置中獲取到Skywalking的接入點。更詳細的接入指南參考阿里雲官方文件

由於小張的系統是基於spring boot,所以只需要在啟動命令中加入以下內容即可。

 

-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=<Tracing Analysis上的接入點>"
-DSW_AGENT_NAME=<Tracing Analysis的應用名稱>"
-javaagent:skywalking-agent.jar=<Tracing Analysis的authentication資訊>"

重新啟動應用後,鏈路追蹤埋點資料就會收集到鏈路追蹤Tracing Analysis 上了。

日誌中列印traceId

為了能夠通過traceId搜尋到所有的日誌,也需要在的應用的日誌中展示traceId資訊,具體方式如下:

在應用中引入以下依賴: 

<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-logback-1.x</artifactId>
    <version>6.6.0</version>
</dependency>

 

修改logback配置檔案,例如:tid即為 Skywalking 的traceId。

 

<property name="LOG_PATTERN" value="[%d{'yyyy-MM-dd HH:mm:ss,SSS',GMT+8:00}] %-5p [%.10t][%X{CU}][%X{tid}]    %logger{36}[%L] - %m%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
        <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
            <pattern>${LOG_PATTERN}</pattern>
        </layout>
    </encoder>
</appender>

   

以上改動就可以在日誌中看到traceId了。如下圖所示:圖中標紅的TID值即為traceId。

 

 

同時小張也在系統出現異常資訊時,將traceId透出給使用者,使用者反饋問題時只需要提供traceId即可。相應的,需要在程式碼中把traceId寫入到響應體中,如下所示: 

 

String traceId = TraceContext.traceId();
result.setTraceId(traceId);

   

Nginx日誌中列印traceId

為了在收到系統RT告警時,也可以獲得traceId,需要修改Nginx配置。

接入Skywalking之後,系統間呼叫的請求都會帶上名稱為sw6的header (其中6為對應的Skywalking版本號),Header值的的格式為:1-TRACEID-SEGMENTID-3-PARENT_SERVICE-PARENT_INSTANCE-PARENT_ENDPOINT-IPPORT從這個值中提取出TRACEID,也就是第一個和第二個橫槓之間的部分,再進行BASE64 decode就可以獲取到traceId。

然後需要在Nginx 的log_format 配置新增對應的Header,如下如下:

 

log_format main 'http_sw6:$http_sw6; http_ns_client_ip:$http_ns_client_ip; time_local:$time_local; request_time:$request_time; upstream_response_time:$upstream_response_time; request:$request_method http://$host$request_uri; request_length:$request_length; upstream_cache_status:$upstream_cache_status; httpStatus:$status; body_bytes_sent:$body_bytes_sent; http_referer:$http_referer; http_user_agent:$http_user_agent; http_x_forwarded_for:$http_x_forwarded_for; remote_addr:$remote_addr;';

  

然後就可以在Nginx日誌中看到了相應的值了,如圖:

 

 

解決鏈路追蹤多執行緒丟失traceId 的問題

小張在測試鏈路追蹤時,發現在多執行緒的使用場景中,只有一個子執行緒能夠正確獲取到traceId,而其它執行緒中的traceId會出現丟失。為了解決上述問題,小張使用@TraceCrossThread註解對Callable和Runnable進行增強,@TraceCrossThread為Skywalking提供的註解,Skywalking通過增強被此註解註釋的類,以此來實現traceId的跨執行緒傳遞。示例程式碼如下。

 

import org.apache.skywalking.apm.toolkit.trace.TraceCrossThread;

@TraceCrossThread
public class TraceableRunnable implements Runnable {
    
    private final Runnable runnable;
    
    public TraceableRunnable(final Runnable runnable) {
        this.runnable = runnable;
    }
    
    @Override
    public void run() {
       runnable.run();
    }
}

  

import java.util.concurrent.Callable;
import org.apache.skywalking.apm.toolkit.trace.TraceCrossThread;

@TraceCrossThread
public class TraceableCallable<T> implements Callable<T> {
    
    private final Callable<T> callable;
    
    public TraceableCallable(final Callable<T> callable) {
        this.callable = callable;    
    }
    
    @Override
    public T call() throws Exception {
         return callable.call();
    }
}

 

後續提交執行緒任務時使用改造後的TraceableCallable和TraceableRunnable 即可解決多執行緒丟失traceId 的問題。

 

完成以上改造後,以下圖為例,使用者每一次的請求都會有對應的traceId,便於將整個請求鏈路展示出來。

 

 

輕鬆應對線上問題

又一個陽光明媚的早上,小張埋頭工作時,又有客服反饋使用者問題,這個時候小張不慌不忙的根據使用者提供traceId,在阿里鏈路追蹤https://tracing.console.aliyun.com/上檢視呼叫鏈路,很快定位到異常節點,示例如下:圖中狀態為紅色的節點就是異常節點,圖中示例表示由於某個sql執行出現異常。

 

 

輕鬆應對一天工作後,小張在下班前又收到應用RT過高的告警, 由於Nginx日誌中列印了traceId資訊,很快就可以定位到耗時的請求,示例如下:

 

 

 

 

圖中耗時比較長的節點,是由於呼叫第三方服務造成,小張根據情況,對服務進行降級,很快就解決RT過高的問題,防止問題擴散。

接入了鏈路追蹤以後,小張在定位線上問題的耗時大大縮短,可以有更多的時間專注其他工作。

以上就是小張是如何通過使用鏈路追蹤從焦頭爛額的排查線上問題到從容定位線上問題的轉變,希望對仍未使用鏈路追蹤技術的同學有些幫助。本故事純屬虛構,如有雷同,純屬巧合。


點選下方連結,即可免費體驗雲效流水線Flow。

https://www.aliyun.com/product/yunxiao/flow?