微服务架构--链路追踪(Nginx篇)
閱讀提示:本文不提供鏈路追蹤的完整解決方案,只提供Nginx層對鏈路追蹤的支持方案!
1 背景介紹
? ? 微服務的誕生,解決了傳統單體應用的很多問題,如可維護性差、擴展性差和靈活性差等問題(粗粒比較)。微服務架構雖好,但同時也帶來了很多挑戰,其中故障排查就是其需要解決的挑戰之一。那么,如何在很多個應用和實例中找到故障發生的根源呢?
? ? 基于以上需求,我們可以將每一筆交易在各個應用中產生的所有日志,進行集中式收集與展示(但前提是你得有:日志中心)。這樣就可以很快看出交易是在哪一步出的故障。如果做得好,還可以直接進行二次開發與數據分析,將收集的日志和出現的故障進行分析后,用圖形界面很直觀的進行展示。
? ? 比如,可以展示出微服務調用的拓撲圖,使用顏色進行區分故障(如常用紅:表示異常、綠:正常、黃:警告)。接著可以將常出現的故障或異常進行分類后做出友好型的展示(說白了就不用直接上堆棧),如:NullPointerException:則界面直接友好型的提示哪一行代碼拋了空指針,輸入參數是什么……(這不是該篇的重點哈,廢話不多說了,后續有機會再詳細介紹)。
? ? 要做整個微服務架構的鏈路追蹤,肯定是希望從交易進入微服務中心的第一個點就開始有一個全局的交易ID來關聯所有日志(鏈路追蹤,這么一個ID肯定是不夠的,但這里只介紹這個哈)。當然最理想的肯定是希望把前端的日志(如操作日志、數據流等)也規劃進行。
2 Nginx
? ? 在大部分的微服務架構中,Nginx基本是常用的接入層設施,所以我們希望請求ID從Nginx層進行校驗填充,并且打印在Nginx的請求日志中。這里只提供三種方式來實現Nginx層的交易ID生產方式。
??2.1 方案二:基于內置變量拼接
? ? ? ? 在1.11.0之前的版本,我們可以采用拼接的方式來組裝請求ID。參考配置如下:
server {# 定義$request_trace_id的值,在1.11.0之前,我們可以使用類似的方式聲明# 只要能確保其值出現重復的可能性盡可能的小即可。set $request_trace_id trace-id-$pid-$connection-$bytes_sent-$msec;location / {# ......# 將此trace_id傳遞給后端的server,通過header方式,此后我們既可以在環境中獲取此headerproxy_set_header X-Request-Id $request_trace_id;} }參數說明:
-
$pid:nginx worker進程號
-
$connection:與upstream server鏈接id數
-
$bytes_sent:發送字節數
-
$msec:當前時間,即此變量獲取的時間,包含秒、毫秒數(中間以.分割)
??2.2 方案三:基于LUA腳本實現
? ? ? 利用系統/dev/urandom生成的隨機UUID。參考腳本如下:
---
--- UUID
--- Created by lry.
--- DateTime: 2018/2/25 下午7.38
--- Describe: 用系統/dev/urandom生成的隨機uuid
---
local template = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
local d = io.open("/dev/urandom", "r"):read(4)
math.randomseed(os.time() + d:byte(1) + (d:byte(2) * 256) + (d:byte(3) * 65536) + (d:byte(4) * 4294967296))
local uuid = string.gsub(template, "x",
? ? function (c)?
? ? ? ? local v = (c == "x") and math.random(0, 0xf) or math.random(8, 0xb)
? ? ? ? return string.format("%x", v)
? ? end)
return uuid
??2.3 方案一:基于?$request_id?實現
? ? ? Nginx在1.11.0版本中就提供了內置變量$request_id,其原理就是生成32位的隨機字符串,雖不能比擬UUID的概率,但32位的隨機字符串的重復概率也是微不足道了,所以一般可視為UUID來使用即可。參考配置如下:
# Nginx代理默認會把header中參數的"_"下劃線去掉,所以后臺服務器后就獲取不到帶"_"線的參數名 underscores_in_headers on;# 設定日志格式 log_format main \'$remote_addr - $remote_user [$time_local] "$request" \'\'$status $body_bytes_sent "$http_referer" $upstream_http_request_id \'\'"$http_user_agent" "$http_x_forwarded_for"\';server {location / {# 如果請求頭中已有該參數,則獲取即可;如果沒有,則使用$request_id進行填充set $temp_request_id $http_x_request_id;if ($temp_request_id = "") {set $temp_request_id $request_id;}# 屏蔽掉原來的請求體參數proxy_set_header x_request_id "";# 設置向后轉發的請求頭參數proxy_set_header X-Request-Id $temp_request_id;} }3 最佳實踐
? ? 生成交易ID的方式有很多種,但希望使用者結合自身實際情況進行合理取舍,而不要盲目的追求ID的唯一性、可讀性和時序性等等。
? ? 比如,ID具有時序性雖然有一定的好處,但實際的架構根本沒有去使用該時序性,則沒必要花大量的精力和做出大量的開發,去實現一個有時序性的交易ID。又比如,覺得UUID可讀性太差,從而花了很多成本去開發一個具有一定含義的交易ID(如前幾位表示什么意思,多少位到多少位又表示什么意思之類的),開發出來后,實際架構根本沒有去解讀該ID的地方,則浪費了成本。
? ?但也不是所有人都直接使用UUID就能滿足的,比如我需要考慮日志的容量,則可以考慮適當縮減ID的長度(每個ID縮減10個字符串,每筆交易就可能少幾百或幾千個字符串,再往上規劃,還是可以減少一些日志容量的)。
? ? 最后,如果有考慮想收集前端的日志的童鞋,建議交易ID就不要使用Long型,因為前端可能會有損失精度的問題。同時也建議使用$request_id來填充交易ID。
?
?
轉自:https://my.oschina.net/yu120/blog/1790419
總結
以上是生活随笔為你收集整理的微服务架构--链路追踪(Nginx篇)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 查看当前系统的glibc版本
- 下一篇: Nginx开启/关闭Core文件及调试