<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go - 實(shí)現(xiàn)項(xiàng)目內(nèi)鏈路追蹤

          共 13786字,需瀏覽 28分鐘

           ·

          2021-02-02 09:35

          為什么項(xiàng)目內(nèi)需要鏈路追蹤?

          當(dāng)一個(gè)請求中,請求了多個(gè)服務(wù)單元,如果請求出現(xiàn)了錯(cuò)誤或異常,很難去定位是哪個(gè)服務(wù)出了問題,這時(shí)就需要鏈路追蹤。

          這個(gè)圖畫的比較簡單,從圖中可以清晰的看出他們之間的調(diào)用關(guān)系,通過一個(gè)例子說明下鏈路的重要性,比如對(duì)方調(diào)我們一個(gè)接口,反饋在某個(gè)時(shí)間段這接口太慢了,在排查代碼發(fā)現(xiàn)邏輯比較復(fù)雜,不光調(diào)用了多個(gè)三方接口、操作了數(shù)據(jù)庫,還操作了緩存,怎么快速定位是哪塊執(zhí)行時(shí)間很長?

          不賣關(guān)子,先說下本篇文章最終實(shí)現(xiàn)了什么,如果感興趣再繼續(xù)往下看。

          實(shí)現(xiàn)了通過記錄如下參數(shù),來進(jìn)行問題定位,關(guān)于每個(gè)參數(shù)的結(jié)構(gòu)在下面都有介紹。

          //?Trace?記錄的參數(shù)
          type?Trace?struct?{
          ????mux????????????????sync.Mutex
          ????Identifier?????????string????`json:"trace_id"`?????????????//?鏈路?ID
          ????Request????????????*Request??`json:"request"`??????????????//?請求信息
          ????Response???????????*Response?`json:"response"`?????????????//?響應(yīng)信息
          ????ThirdPartyRequests?[]*Dialog?`json:"third_party_requests"`?//?調(diào)用第三方接口的信息
          ????Debugs?????????????[]*Debug??`json:"debugs"`???????????????//?調(diào)試信息
          ????SQLs???????????????[]*SQL????`json:"sqls"`?????????????????//?執(zhí)行的?SQL?信息
          ????Redis??????????????[]*Redis??`json:"redis"`????????????????//?執(zhí)行的?Redis?信息
          ????Success????????????bool??????`json:"success"`??????????????//?請求結(jié)果?true?or?false
          ????CostSeconds????????float64???`json:"cost_seconds"`?????????//?執(zhí)行時(shí)長(單位秒)
          }

          參數(shù)結(jié)構(gòu)

          鏈路 ID

          String 例如:4b4f81f015a4f2a01b00。如果請求 Header 中存在 TRACE-ID,就使用它,反之,重新創(chuàng)建一個(gè)。將 TRACE_ID 放到接口返回值中,這樣就可以通過這個(gè)標(biāo)示查到這一串的信息。

          請求信息

          Object,結(jié)構(gòu)如下:

          type?Request?struct?{
          ?TTL????????string??????`json:"ttl"`?????????//?請求超時(shí)時(shí)間
          ?Method?????string??????`json:"method"`??????//?請求方式
          ?DecodedURL?string??????`json:"decoded_url"`?//?請求地址
          ?Header?????interface{}?`json:"header"`??????//?請求?Header?信息
          ?Body???????interface{}?`json:"body"`????????//?請求?Body?信息
          }

          響應(yīng)信息

          Object,結(jié)構(gòu)如下:

          type?Response?struct?{
          ?Header??????????interface{}?`json:"header"`??????????????????????//?Header?信息
          ?Body????????????interface{}?`json:"body"`????????????????????????//?Body?信息
          ?BusinessCode????int?????????`json:"business_code,omitempty"`?????//?業(yè)務(wù)碼
          ?BusinessCodeMsg?string??????`json:"business_code_msg,omitempty"`?//?提示信息
          ?HttpCode????????int?????????`json:"http_code"`???????????????????//?HTTP?狀態(tài)碼
          ?HttpCodeMsg?????string??????`json:"http_code_msg"`???????????????//?HTTP?狀態(tài)碼信息
          ?CostSeconds?????float64?????`json:"cost_seconds"`????????????????//?執(zhí)行時(shí)間(單位秒)
          }

          調(diào)用三方接口信息

          Object,結(jié)構(gòu)如下:

          type?Dialog?struct?{
          ?mux?????????sync.Mutex
          ?Request?????*Request????`json:"request"`??????//?請求信息
          ?Responses???[]*Response?`json:"responses"`????//?返回信息
          ?Success?????bool????????`json:"success"`??????//?是否成功,true?或?false
          ?CostSeconds?float64?????`json:"cost_seconds"`?//?執(zhí)行時(shí)長(單位秒)
          }

          這里面的 RequestResponse 結(jié)構(gòu)與上面保持一致。

          細(xì)節(jié)來了,為什么 Responses 結(jié)構(gòu)是 []*Response ?

          是因?yàn)?HTTP 可以進(jìn)行重試請求,比如當(dāng)請求對(duì)方接口的時(shí)候,HTTP 狀態(tài)碼為 503 http.StatusServiceUnavailable,這時(shí)需要重試,我們也需要把重試的響應(yīng)信息記錄下來。

          調(diào)試信息

          Object 結(jié)構(gòu)如下:

          type?Debug?struct?{
          ?Key?????????string??????`json:"key"`??????????//?標(biāo)示
          ?Value???????interface{}?`json:"value"`????????//?值
          ?CostSeconds?float64?????`json:"cost_seconds"`?//?執(zhí)行時(shí)間(單位秒)
          }

          SQL 信息

          Object,結(jié)構(gòu)如下:

          type?SQL?struct?{
          ?Timestamp???string??`json:"timestamp"`?????//?時(shí)間,格式:2006-01-02 15:04:05
          ?Stack???????string??`json:"stack"`?????????//?文件地址和行號(hào)
          ?SQL?????????string??`json:"sql"`???????????//?SQL?語句
          ?Rows????????int64???`json:"rows_affected"`?//?影響行數(shù)
          ?CostSeconds?float64?`json:"cost_seconds"`??//?執(zhí)行時(shí)長(單位秒)
          }

          Redis 信息

          Object,結(jié)構(gòu)如下:

          type?Redis?struct?{
          ?Timestamp???string??`json:"timestamp"`???????//?時(shí)間,格式:2006-01-02 15:04:05
          ?Handle??????string??`json:"handle"`??????????//?操作,SET/GET?等
          ?Key?????????string??`json:"key"`?????????????//?Key
          ?Value???????string??`json:"value,omitempty"`?//?Value
          ?TTL?????????float64?`json:"ttl,omitempty"`???//?超時(shí)時(shí)長(單位分)
          ?CostSeconds?float64?`json:"cost_seconds"`????//?執(zhí)行時(shí)間(單位秒)
          }

          請求結(jié)果

          Bool,這個(gè)和統(tǒng)一定義返回值有點(diǎn)關(guān)系,看下代碼:

          //?錯(cuò)誤返回
          c.AbortWithError(code.ErrParamBind.WithErr(err))

          //?正確返回
          c.Payload(code.OK.WithData(data))

          當(dāng)錯(cuò)誤返回時(shí) 且 ctx.Writer.Status() != http.StatusOK 時(shí),為 false,反之為 true。

          執(zhí)行時(shí)長

          Float64,例如:0.041746869,記錄的是從請求開始到請求結(jié)束所花費(fèi)的時(shí)間。

          如何收集參數(shù)?

          這時(shí)有老鐵會(huì)說了:“規(guī)劃的稍微還行,使用的時(shí)候會(huì)不會(huì)很麻煩?”

          “No,No,使用起來一丟丟都不麻煩”,接著往下看。

          無需關(guān)心的參數(shù)

          鏈路 ID、請求信息、響應(yīng)信息、請求結(jié)果、執(zhí)行時(shí)長,這 5 個(gè)參數(shù),開發(fā)者無需關(guān)心,這些都在中間件封裝好了。

          調(diào)用第三方接口的信息

          只需多傳遞一個(gè)參數(shù)即可。

          在這里厚臉皮自薦下 httpclient 包 。

          • 支持設(shè)置失敗時(shí)重試,可以自定義重試次數(shù)、重試前延遲等待時(shí)間、重試的滿足條件;
          • 支持設(shè)置失敗時(shí)告警,可以自定義告警渠道(郵件/微信)、告警的滿足條件;
          • 支持設(shè)置調(diào)用鏈路;

          調(diào)用示例代碼:

          //?httpclient?是項(xiàng)目中封裝的包
          api?:=?"http://127.0.0.1:9999/demo/post"
          params?:=?url.Values{}
          params.Set("name",?name)
          body,?err?:=?httpclient.PostForm(api,?params,
          ????httpclient.WithTrace(ctx.Trace()),??//?傳遞上下文
          )

          調(diào)試信息

          只需多傳遞一個(gè)參數(shù)即可。

          調(diào)用示例代碼:

          // p 是項(xiàng)目中封裝的包
          p.Println("key",?"value",
          ?p.WithTrace(ctx.Trace()),?//?傳遞上下文
          )

          SQL 信息

          稍微復(fù)雜一丟丟,需要多傳遞一個(gè)參數(shù),然后再寫一個(gè) GORM 插件。

          使用的 GORM V2 自帶的 CallbacksContext 知識(shí)點(diǎn),細(xì)節(jié)不多說,可以看下這篇文章:基于 GORM 獲取當(dāng)前請求所執(zhí)行的 SQL 信息

          調(diào)用示例代碼:

          //?原來查詢這樣寫
          err?:=?u.db.GetDbR().
          ????First(data,?id).
          ????Where("is_deleted?=??",?-1).
          ????Error

          //?現(xiàn)在只需這樣寫
          err?:=?u.db.GetDbR().
          ????WithContext(ctx.RequestContext()).
          ????First(data,?id).
          ????Where("is_deleted?=??",?-1).
          ????Error
          ????
          // .WithContext 是 GORM V2 自帶的。????
          //?插件的代碼就不貼了,去上面的文章查看即可。

          Redis 信息

          只需多傳遞一個(gè)參數(shù)即可。

          調(diào)用示例代碼:

          //?cache?是基于?go-redis?封裝的包
          d.cache.Get("name",?
          ????cache.WithTrace(c.Trace()),
          )

          核心原理是啥?

          在這沒關(guān)子可賣,看到這相信老鐵們都知道了,就兩個(gè):一個(gè)是 攔截器,另一個(gè)是 Context。

          如何記錄參數(shù)?

          將以上數(shù)據(jù)轉(zhuǎn)為 JSON 結(jié)構(gòu)記錄到日志中。

          JSON 示例

          {
          ????"level":"info",
          ????"time":"2021-01-30?22:32:48",
          ????"caller":"core/core.go:444",
          ????"msg":"core-interceptor",
          ????"domain":"go-gin-api[fat]",
          ????"method":"GET",
          ????"path":"/demo/trace",
          ????"http_code":200,
          ????"business_code":1,
          ????"success":true,
          ????"cost_seconds":0.054025302,
          ????"trace_id":"2cdb2f96934f573af391",
          ????"trace_info":{
          ????????"trace_id":"2cdb2f96934f573af391",
          ????????"request":{
          ????????????"ttl":"un-limit",
          ????????????"method":"GET",
          ????????????"decoded_url":"/demo/trace",
          ????????????"header":{
          ????????????????"Accept":[
          ????????????????????"application/json"
          ????????????????],
          ????????????????"Accept-Encoding":[
          ????????????????????"gzip,?deflate,?br"
          ????????????????],
          ????????????????"Accept-Language":[
          ????????????????????"zh-CN,zh;q=0.9,en;q=0.8"
          ????????????????],
          ????????????????"Authorization":[
          ????????????????????"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEsIlVzZXJOYW1lIjoieGlubGlhbmdub3RlIiwiZXhwIjoxNjEyMTAzNTQwLCJpYXQiOjE2MTIwMTcxNDAsIm5iZiI6MTYxMjAxNzE0MH0.2yHDdP7cNT5uL5xA0-j_NgTK4GrW-HGn0KUxcbZfpKg"
          ????????????????],
          ????????????????"Connection":[
          ????????????????????"keep-alive"
          ????????????????],
          ????????????????"Referer":[
          ????????????????????"http://127.0.0.1:9999/swagger/index.html"
          ????????????????],
          ????????????????"Sec-Fetch-Dest":[
          ????????????????????"empty"
          ????????????????],
          ????????????????"Sec-Fetch-Mode":[
          ????????????????????"cors"
          ????????????????],
          ????????????????"Sec-Fetch-Site":[
          ????????????????????"same-origin"
          ????????????????],
          ????????????????"User-Agent":[
          ????????????????????"Mozilla/5.0?(Macintosh;?Intel?Mac?OS?X?10_15_6)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/88.0.4324.96?Safari/537.36"
          ????????????????]
          ????????????},
          ????????????"body":""
          ????????},
          ????????"response":{
          ????????????"header":{
          ????????????????"Content-Type":[
          ????????????????????"application/json;?charset=utf-8"
          ????????????????],
          ????????????????"Trace-Id":[
          ????????????????????"2cdb2f96934f573af391"
          ????????????????],
          ????????????????"Vary":[
          ????????????????????"Origin"
          ????????????????]
          ????????????},
          ????????????"body":{
          ????????????????"code":1,
          ????????????????"msg":"OK",
          ????????????????"data":[
          ????????????????????{
          ????????????????????????"name":"Tom",
          ????????????????????????"job":"Student"
          ????????????????????},
          ????????????????????{
          ????????????????????????"name":"Jack",
          ????????????????????????"job":"Teacher"
          ????????????????????}
          ????????????????],
          ????????????????"id":"2cdb2f96934f573af391"
          ????????????},
          ????????????"business_code":1,
          ????????????"business_code_msg":"OK",
          ????????????"http_code":200,
          ????????????"http_code_msg":"OK",
          ????????????"cost_seconds":0.054024874
          ????????},
          ????????"third_party_requests":[
          ????????????{
          ????????????????"request":{
          ????????????????????"ttl":"5s",
          ????????????????????"method":"GET",
          ????????????????????"decoded_url":"http://127.0.0.1:9999/demo/get/Tom",
          ????????????????????"header":{
          ????????????????????????"Authorization":[
          ????????????????????????????"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEsIlVzZXJOYW1lIjoieGlubGlhbmdub3RlIiwiZXhwIjoxNjEyMTAzNTQwLCJpYXQiOjE2MTIwMTcxNDAsIm5iZiI6MTYxMjAxNzE0MH0.2yHDdP7cNT5uL5xA0-j_NgTK4GrW-HGn0KUxcbZfpKg"
          ????????????????????????],
          ????????????????????????"Content-Type":[
          ????????????????????????????"application/x-www-form-urlencoded;?charset=utf-8"
          ????????????????????????],
          ????????????????????????"TRACE-ID":[
          ????????????????????????????"2cdb2f96934f573af391"
          ????????????????????????]
          ????????????????????},
          ????????????????????"body":null
          ????????????????},
          ????????????????"responses":[
          ????????????????????{
          ????????????????????????"header":{
          ????????????????????????????"Content-Length":[
          ????????????????????????????????"87"
          ????????????????????????????],
          ????????????????????????????"Content-Type":[
          ????????????????????????????????"application/json;?charset=utf-8"
          ????????????????????????????],
          ????????????????????????????"Date":[
          ????????????????????????????????"Sat,?30?Jan?2021?14:32:48?GMT"
          ????????????????????????????],
          ????????????????????????????"Trace-Id":[
          ????????????????????????????????"2cdb2f96934f573af391"
          ????????????????????????????],
          ????????????????????????????"Vary":[
          ????????????????????????????????"Origin"
          ????????????????????????????]
          ????????????????????????},
          ????????????????????????"body":"{"code":1,"msg":"OK","data":{"name":"Tom","job":"Student"},"id":"2cdb2f96934f573af391"}",
          ????????????????????????"http_code":200,
          ????????????????????????"http_code_msg":"200?OK",
          ????????????????????????"cost_seconds":0.000555089
          ????????????????????}
          ????????????????],
          ????????????????"success":true,
          ????????????????"cost_seconds":0.000580202
          ????????????},
          ????????????{
          ????????????????"request":{
          ????????????????????"ttl":"5s",
          ????????????????????"method":"POST",
          ????????????????????"decoded_url":"http://127.0.0.1:9999/demo/post",
          ????????????????????"header":{
          ????????????????????????"Authorization":[
          ????????????????????????????"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEsIlVzZXJOYW1lIjoieGlubGlhbmdub3RlIiwiZXhwIjoxNjEyMTAzNTQwLCJpYXQiOjE2MTIwMTcxNDAsIm5iZiI6MTYxMjAxNzE0MH0.2yHDdP7cNT5uL5xA0-j_NgTK4GrW-HGn0KUxcbZfpKg"
          ????????????????????????],
          ????????????????????????"Content-Type":[
          ????????????????????????????"application/x-www-form-urlencoded;?charset=utf-8"
          ????????????????????????],
          ????????????????????????"TRACE-ID":[
          ????????????????????????????"2cdb2f96934f573af391"
          ????????????????????????]
          ????????????????????},
          ????????????????????"body":"name=Jack"
          ????????????????},
          ????????????????"responses":[
          ????????????????????{
          ????????????????????????"header":{
          ????????????????????????????"Content-Length":[
          ????????????????????????????????"88"
          ????????????????????????????],
          ????????????????????????????"Content-Type":[
          ????????????????????????????????"application/json;?charset=utf-8"
          ????????????????????????????],
          ????????????????????????????"Date":[
          ????????????????????????????????"Sat,?30?Jan?2021?14:32:48?GMT"
          ????????????????????????????],
          ????????????????????????????"Trace-Id":[
          ????????????????????????????????"2cdb2f96934f573af391"
          ????????????????????????????],
          ????????????????????????????"Vary":[
          ????????????????????????????????"Origin"
          ????????????????????????????]
          ????????????????????????},
          ????????????????????????"body":"{"code":1,"msg":"OK","data":{"name":"Jack","job":"Teacher"},"id":"2cdb2f96934f573af391"}",
          ????????????????????????"http_code":200,
          ????????????????????????"http_code_msg":"200?OK",
          ????????????????????????"cost_seconds":0.000450153
          ????????????????????}
          ????????????????],
          ????????????????"success":true,
          ????????????????"cost_seconds":0.000468387
          ????????????}
          ????????],
          ????????"debugs":[
          ????????????{
          ????????????????"key":"res1.Data.Name",
          ????????????????"value":"Tom",
          ????????????????"cost_seconds":0.000005193
          ????????????},
          ????????????{
          ????????????????"key":"res2.Data.Name",
          ????????????????"value":"Jack",
          ????????????????"cost_seconds":0.000003907
          ????????????},
          ????????????{
          ????????????????"key":"redis-name",
          ????????????????"value":"tom",
          ????????????????"cost_seconds":0.000009816
          ????????????}
          ????????],
          ????????"sqls":[
          ????????????{
          ????????????????"timestamp":"2021-01-30?22:32:48",
          ????????????????"stack":"/Users/xinliang/github/go-gin-api/internal/api/repository/db_repo/user_demo_repo/user_demo.go:76",
          ????????????????"sql":"SELECT?`id`,`user_name`,`nick_name`,`mobile`?FROM?`user_demo`?WHERE?user_name?=?'test_user'?and?is_deleted?=?-1?ORDER?BY?`user_demo`.`id`?LIMIT?1",
          ????????????????"rows_affected":1,
          ????????????????"cost_seconds":0.031969072
          ????????????}
          ????????],
          ????????"redis":[
          ????????????{
          ????????????????"timestamp":"2021-01-30?22:32:48",
          ????????????????"handle":"set",
          ????????????????"key":"name",
          ????????????????"value":"tom",
          ????????????????"ttl":10,
          ????????????????"cost_seconds":0.009982091
          ????????????},
          ????????????{
          ????????????????"timestamp":"2021-01-30?22:32:48",
          ????????????????"handle":"get",
          ????????????????"key":"name",
          ????????????????"cost_seconds":0.010681579
          ????????????}
          ????????],
          ????????"success":true,
          ????????"cost_seconds":0.054025302
          ????}
          }

          zap 日志組件

          有對(duì)日志收集感興趣的老鐵們可以往下看,trace_info 只是日志的一個(gè)參數(shù),具體日志參數(shù)包括:

          參數(shù)數(shù)據(jù)類型說明
          levelString日志級(jí)別,例如:info,warn,error,debug
          timeString時(shí)間,例如:2021-01-30 16:05:44
          callerString調(diào)用位置,文件+行號(hào),例如:core/core.go:443
          msgString日志信息,例如:xx 錯(cuò)誤
          domainString域名或服務(wù)名,例如:go-gin-api[fat]
          methodString請求方式,例如:POST
          pathString請求路徑,例如:/user/create
          http_codeIntHTTP 狀態(tài)碼,例如:200
          business_codeInt業(yè)務(wù)狀態(tài)碼,例如:10101
          successBool狀態(tài),true or false
          cost_secondsFloat64花費(fèi)時(shí)間,單位:秒,例如:0.01
          trace_idString鏈路ID,例如:ec3c868c8dcccfe515ab
          trace_infoObject鏈路信息,結(jié)構(gòu)化數(shù)據(jù)。
          errorString錯(cuò)誤信息,當(dāng)出現(xiàn)錯(cuò)誤時(shí)才有這字段。
          errorVerboseString詳細(xì)的錯(cuò)誤堆棧信息,當(dāng)出現(xiàn)錯(cuò)誤時(shí)才有這字段。

          日志記錄可以使用 zap,logrus ,這次我使用的 zap,簡單封裝一下即可,比如:

          • 支持設(shè)置日志級(jí)別;
          • 支持設(shè)置日志輸出到控制臺(tái);
          • 支持設(shè)置日志輸出到文件;
          • 支持設(shè)置日志輸出到文件(可自動(dòng)分割);

          總結(jié)

          這個(gè)功能比較常用,使用起來也很爽,比如調(diào)用方發(fā)現(xiàn)接口出問題時(shí),只需要提供 TRACE-ID 即可,我們就可以查到關(guān)于它整個(gè)鏈路的所有信息。

          以上代碼都在 go-gin-api 項(xiàng)目中,地址:https://github.com/xinliangnote/go-gin-api



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲??;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 49
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一级毛片大全 | 亲子乱AⅤ一区二区三区下载 | 翔田千里vs黑人播放 | 一本道无码在线 | 大鸡吧一区 |