<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>

          從 1.5 開始搭建一個(gè)微服務(wù)框架——鏈路追蹤 traceId

          共 5050字,需瀏覽 11分鐘

           ·

          2022-07-26 16:11


          前言

          最近在搭一個(gè)基礎(chǔ)版的項(xiàng)目框架,基于 SpringCloud 微服務(wù)框架。

          如果把 SpringCloud 這個(gè)框架當(dāng)做 1,那么現(xiàn)在已經(jīng)有的基礎(chǔ)組件比如 swagger/logback 等等就是 0.5 然后我在這 1.5 礎(chǔ)上進(jìn)行組裝,完成一個(gè)微服務(wù)項(xiàng)目框架。

          為什么要造二代輪子呢?市面上現(xiàn)成的項(xiàng)目框架不香嗎?

          因?yàn)轫?xiàng)目組不允許用外部的現(xiàn)成框架,比如 Ruoyi。另外因?yàn)槲覀兊捻?xiàng)目需求具有自身的特色,技術(shù)選型也會(huì)選擇我們自己熟悉的框架,所以自己來造二代輪子也是一個(gè)不錯(cuò)的選擇。

          核心功能

          需要包含以下核心功能:

          • 多個(gè)微服務(wù)模塊拆分,抽取出一個(gè) demo 微服務(wù)模塊供擴(kuò)展,已完成

          • 提取核心框架模塊,已完成

          • 注冊(cè)中心 Eureka,已完成

          • 遠(yuǎn)程調(diào)用 OpenFeign,已完成

          • 日志 logback,包含 traceId 跟蹤,已完成

          • Swagger API 文檔,已完成

          • 配置文件共享,已完成

          • 日志檢索,ELK Stack,已完成

          • 自定義 Starter,待定

          • 整合緩存 Redis,Redis 哨兵高可用,已完成

          • 整合數(shù)據(jù)庫(kù) MySQL,MySQL 高可用,已完成

          • 整合 MyBatis-Plus,已完成

          • 鏈路追蹤組件,待定

          • 監(jiān)控,待定

          • 工具類,待開發(fā)

          • 網(wǎng)關(guān),技術(shù)選型待定

          • 審計(jì)日志進(jìn)入 ES,待定

          • 分布式文件系統(tǒng),待定

          • 定時(shí)任務(wù),待定

          • 等等

          本篇要介紹的內(nèi)容是關(guān)于日志鏈路追蹤的。

          一、痛點(diǎn)

          痛點(diǎn)一:進(jìn)程內(nèi)的多條日志無法追蹤

          一個(gè)請(qǐng)求調(diào)用,假設(shè)會(huì)調(diào)用后端十幾個(gè)方法,打印十幾次日志,無法將這些日志串聯(lián)起來。

          如下圖所示:客戶端調(diào)用訂單服務(wù),訂單服務(wù)中方法 A 調(diào)用方法 B,方法 B 調(diào)用方法 C。

          方法 A 打印第一條日志和第五條日志,方法 B 打印第二條日志,方法 C 打印第三條日志和第四條日志,但是這 5 條日志并沒有任何聯(lián)系,唯一的聯(lián)系就是時(shí)間是按照時(shí)間循序打印的,但是如果有其他并發(fā)的請(qǐng)求調(diào)用,則會(huì)干擾日志的連續(xù)性。

          痛點(diǎn)二:跨服務(wù)的日志如何進(jìn)行關(guān)聯(lián)

          每個(gè)微服務(wù)都會(huì)記錄自己這個(gè)進(jìn)程的日志,跨進(jìn)程的日志如何進(jìn)行關(guān)聯(lián)?

          如下圖所示:訂單服務(wù)和優(yōu)惠券服務(wù)屬于兩個(gè)微服務(wù),部署在兩臺(tái)機(jī)器上,訂單服務(wù)的 A 方法遠(yuǎn)程調(diào)用優(yōu)惠券服務(wù)的 D 方法。

          方法 A 將日志打印到日志文件 1 中,記錄了 5 條日志,方法 D 將日志打印到日志文件 2 中,記錄了 5 條日志。但是這 10 條日志是無法關(guān)聯(lián)起來的。

          痛點(diǎn)三:跨線程的日志如何關(guān)聯(lián)

          主線程和子線程的日志如何關(guān)聯(lián)?

          如下圖所示:主線程的方法 A 啟動(dòng)了一個(gè)子線程,子線程執(zhí)行方法 E。

          方法 A 打印了第一條日志,子線程 E 打印了第二條日志和第三條日志。

          痛點(diǎn)四:第三方調(diào)用我們的服務(wù),如何追蹤?

          本篇要解決的核心問題是第一個(gè)和第二個(gè)問題,多線程目前還未引入,目前也沒有第三方來調(diào)用,后期再來優(yōu)化第三個(gè)和第四個(gè)問題。

          二、方案

          1.1 解決方案

          ① 使用 Skywalking traceId 進(jìn)行鏈路追蹤

          ② 使用 Elastic APM 的 traceId 進(jìn)行鏈路追蹤

          ③ MDC 方案:自己生成 traceId 并 put 到 MDC 里面。

          項(xiàng)目初期,先不引入過多的中間件,用簡(jiǎn)單可行的方案先嘗試,所以這里用第三種方案 MDC。

          1.2 MDC 方案

          MDC(Mapped Diagnostic Context)用于存儲(chǔ)運(yùn)行上下文的特定線程的上下文數(shù)據(jù)。因此,如果使用 log4j 進(jìn)行日志記錄,則每個(gè)線程都可以擁有自己的MDC,該 MDC 對(duì)整個(gè)線程是全局的。屬于該線程的任何代碼都可以輕松訪問線程的 MDC 中存在的值。

          三、原理和實(shí)戰(zhàn)

          2.1 追蹤一個(gè)請(qǐng)求的多條日志

          我們先來看第一個(gè)痛點(diǎn),如何在一個(gè)請(qǐng)求中,將多條日志串聯(lián)起來。

          該方案的原理如下圖所示:

          (1)在 logback 日志配置文件中的日志格式中添加 %X{traceId} 配置。

          <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %X{traceId} %-5level %logger - %msg%n</pattern>

          (2)自定一個(gè)攔截器,從請(qǐng)求的 header 中獲取 traceId ,如果存在則放到 MDC 中,否則直接用 UUID 當(dāng)做 traceId,然后放到 MDC 中。

          (3)配置攔截器。

          當(dāng)我們打印日志的時(shí)候,會(huì)自動(dòng)打印 traceId,如下所示,多條日志的 traceId 相同。

          示例代碼

          攔截器代碼:

          /**
           * @author www.passjava.cn,公眾號(hào):悟空聊架構(gòu)
           * @date 2022-07-05 
           */

          @Service
          public class LogInterceptor extends HandlerInterceptorAdapter {

              private static final String TRACE_ID = "traceId";

              @Override
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                  String traceId = request.getHeader(TRACE_ID);
                  if (StringUtils.isEmpty(traceId)) {
                      MDC.put("traceId", UUID.randomUUID().toString());
                  } else {
                      MDC.put(TRACE_ID, traceId);
                  }

                  return true;
              }

              @Override
              public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                  //防止內(nèi)存泄露
                  MDC.remove("traceId");
              }
          }

          配置攔截器:

          /**
           * @author www.passjava.cn,公眾號(hào):悟空聊架構(gòu)
           * @date 2022-07-05 
           */

          @Configuration
          public class InterceptorConfig implements WebMvcConfigurer {

              @Resource
              private LogInterceptor logInterceptor;

              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  registry.addInterceptor(logInterceptor).addPathPatterns("/**");
              }
          }

          2.2 跨服務(wù)跟蹤多條日志

          解決方案的原理圖如下所示:

          訂單服務(wù)遠(yuǎn)程調(diào)用優(yōu)惠券服務(wù),需要在訂單服務(wù)中添加 OpenFeign 的攔截器,攔截器里面做的事就是往 請(qǐng)求的 header 中添加 traceId,這樣調(diào)用到優(yōu)惠券服務(wù)時(shí),就能從 header 中拿到這次請(qǐng)求的 traceId。

          代碼如下所示:

          /**
           * @author www.passjava.cn,公眾號(hào):悟空聊架構(gòu)
           * @date 2022-07-05 
           */

          @Configuration
          public class FeignInterceptor implements RequestInterceptor {
              private static final String TRACE_ID = "traceId";

              @Override
              public void apply(RequestTemplate requestTemplate) {
                  requestTemplate.header(TRACE_ID, (String) MDC.get(TRACE_ID));
              }
          }

          兩個(gè)微服務(wù)打印的日志中,兩條日志的 traceId 一致。

          當(dāng)然這些日志都會(huì)導(dǎo)入到 Elasticsearch 中的,然后通過 kibana 可視化界面搜索 traceId,就可以將整個(gè)調(diào)用鏈路串起來了!

          四、總結(jié)

          本篇通過攔截器、MDC 功能,全鏈路加入了 traceId,然后將 traceId 輸出到日志中,就可以通過日志來追蹤調(diào)用鏈路。不論是進(jìn)程內(nèi)的方法級(jí)調(diào)用,還是跨進(jìn)程間的服務(wù)調(diào)用,都可以進(jìn)行追蹤。

          另外日志還需要通過 ELK Stack 技術(shù)將日志導(dǎo)入到 Elasticsearch 中,然后就可以通過檢索 traceId,將整個(gè)調(diào)用鏈路檢索出來了。

          瀏覽 46
          點(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>
                  超碰色色色 | 日一区二区三区四区视频 | 日本艹逼网站 | 在线不卡视频 | 一卡二卡国产在线 |