大家好,我叫劉玄,負責云效流水線的開發,程式員在日常作業中經常會遇到一些線上問題需要排查,本文的主人公程式員小張也不例外,但排查的程序卻時常令他困擾不已,讓我們一起看看他遇到了哪些問題,又是怎么解決的,
焦頭爛額的一天
那是一個陽光明媚的上午,小張來到工位,打開電腦,備上咖啡,精神滿滿的開始了一天的作業,正在小張噼里啪啦的敲著鍵盤,認真Coding之時,釘釘群里的一個釘,打破了寧靜,客服人員反饋,有客戶遇到了一個問題,需要開發人員排查,小張排查了線上日志,發現用戶的請求比較多,日志也比較多,沒有定位到關鍵資訊,小張只能又讓客服找用戶提供更具體的資訊,在和用戶反復進行溝通之后,小張最侄訓了半個多小時才定位到了問題,
忙碌的一天很快結束,正當小張準備下班,籌劃著下班之后怎么happy時,電話報警的聲音,又把他拉回了現實,小張收到后端服務RT高的告警后,趕緊排查多個后臺應用的監控資訊和日志,雖然很快從Nginx日志定位到了有問題的請求資訊,但小張很難精確的找到這個請求對應的應用日志,所以花費了很長時間才定位到問題:一個第三方服務例外,導致部分功能受影響,定位到原因后,及時采取了降級手段,系統恢復正常,
尋求解決問題的方案
過完了焦頭爛額的一天,小張覺得現在處理問題的效率太低,大把的時間花在了問題定位上,而之所以排查的這么慢,是因為系統采用微服務架構,一個請求會涉及到多個服務,并且每個服務還會呼叫DB、快取以及其他第三方服務,大致鏈路如下:

小張想,應該有成熟的技術方案,能夠標識整個請求鏈路,將例外服務節點清晰標注,小張借助搜索工具,發現有一種解決方案,鏈路追蹤,剛好適合自己的場景,
鏈路追蹤工具可以將一次分布式請求還原成完整的呼叫鏈路,將整個請求的呼叫情況進行展示,比如各個服務上的耗時、各個服務的請求狀態、具體調度到各個服務的哪臺機器上等資訊,
改造系統
期望的效果
根據前面遇到的兩個問題,小張期望:
- 用戶請求遇到問題時候,可以獲取到一個traceId,只要提供了這個traceId,就可以看到這個請求在各個服務之間的呼叫路徑,并且可以通過這個traceId查到所有應用中相關的日志,
- 當收到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="https://www.cnblogs.com/yyds114/p/[%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" >
<encoder >
<layout >
<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?

轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/457556.html
標籤:其他
