ApiBoot Logging使用RestTemplate透傳鏈路資訊

  • 2019 年 11 月 11 日
  • 筆記

在上一篇文章【ApiBoot Logging使用SpringCloud Openfeign透傳鏈路資訊】中我們詳細的講解了ApiBoot Logging整合SpringCloud通過Openfeign進行透傳鏈路資訊,包括traceId(鏈路編號)、parentSpanId(上級單元編號)等資訊。 ApiBoot Logging不僅僅可以使用Openfeign傳遞鏈路資訊,還支援RestTemplate方式,本篇文章來詳細的講解下具體的使用方式。

部落格原文地址:http://blog.yuqiyu.com/apiboot-logging-using-resttemplate-transparent-traceid.html

搭建Logging Admin

我們需要搭建Logging Admin服務,用於接收業務服務上報的請求日誌資訊,請參考【將ApiBoot Logging採集的日誌上報到Admin】文章內容.

添加ApiBoot統一版本

由於本章採用是Maven 多模組的方式構建源碼,所以我們只需要將ApiBoot統一版本的依賴配置在root項目的pom.xml內,如下所示:

<properties>    <java.version>1.8</java.version>    <!--ApiBoot版本號-->    <api.boot.version>2.1.5.RELEASE</api.boot.version>  </properties>  <dependencyManagement>    <dependencies>      <dependency>        <groupId>org.minbox.framework</groupId>        <artifactId>api-boot-dependencies</artifactId>        <version>${api.boot.version}</version>        <type>pom</type>        <scope>import</scope>      </dependency>    </dependencies>  </dependencyManagement>

接下來我們營造本篇文章的模擬場景,查詢用戶基本資訊時一併查詢出用戶的帳號餘額。

創建賬戶服務

創建一個名為account-service的SpringBoot項目。

添加相關依賴

在項目pom.xml配置文件內添加相關依賴,如下所示:

<dependencies>    <!--SpringBoot Web-->    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <!--ApiBoot Logging-->    <dependency>      <groupId>org.minbox.framework</groupId>      <artifactId>api-boot-starter-logging</artifactId>    </dependency>  </dependencies>

配置上報的Logging Admin

在application.yml配置文件內添加請求日誌上報的Logging Admin地址,如下所示:

spring:    application:      name: account-service  server:    port: 9090    api:    boot:      logging:        # 控制台列印請求日誌        show-console-log: true        # 美化請求日誌        format-console-log-json: true        # Logging Admin地址        admin:          server-address: 127.0.0.1:8081

注意:server-address配置參數不需要添加http://前綴

啟用Logging Client

添加完成依賴後我們通過@EnableLoggingClient註解來啟用ApiBoot Logging,在AccountServiceApplication類上添加如下所示:

/**   * 賬戶服務   *   * @author 恆宇少年   */  @SpringBootApplication  @EnableLoggingClient  public class AccountServiceApplication {      /**       * logger instance       */      static Logger logger = LoggerFactory.getLogger(AccountServiceApplication.class);        public static void main(String[] args) {          SpringApplication.run(AccountServiceApplication.class, args);          logger.info("{}服務啟動成功.", "賬戶");      }  }

@EnableLoggingClient註解就實例化部分ApiBoot Logging內部所需要的類,將實例放置到Spring IOC容器內。

查詢賬戶餘額程式碼實現

我們創建一個名為AccountController的控制器來提供查詢賬戶的餘額資訊,程式碼實現如下所示:

/**   * 賬戶服務實現   *   * @author 恆宇少年   */  @RestController  @RequestMapping(value = "/account")  public class AccountController {        /**       * 示例,記憶體賬戶列表       */      static final HashMap<Integer, Double> ACCOUNTS = new HashMap() {{          put(1, 1233.22);          put(2, 69269.22);      }};        /**       * 獲取指定賬戶的餘額       *       * @param accountId       * @return       */      @GetMapping(value = "/{accountId}")      public Double getBalance(@PathVariable("accountId") Integer accountId) {          return ACCOUNTS.get(accountId);      }  }

至此我們的賬戶服務已經編寫完成,下面我們來編寫用戶服務。

創建用戶服務

我們來創建一個名為user-service的SpringBoot項目。

添加相關依賴

在項目pom.xml配置文件內添加相關依賴,如下所示:

<dependencies>    <!--SpringBoot Web-->    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <!--ApiBoot Logging-->    <dependency>      <groupId>org.minbox.framework</groupId>      <artifactId>api-boot-starter-logging</artifactId>    </dependency>  </dependencies>

配置上報的Logging Admin

本章我們使用指定Logging Admin地址的方式配置,修改application.yml配置文件如下所示:

spring:    application:      name: user-service  server:    port: 9091    api:    boot:      logging:        # 控制台列印請求日誌        show-console-log: true        # 美化請求日誌        format-console-log-json: true        # Logging Admin地址        admin:          server-address: 127.0.0.1:8081

啟用Logging Client

添加完依賴後我們需要在XxxApplication入口類上添加@EnableLoggingClient註解來啟用ApiBoot Logging,如下所示:

/**   * 用戶服務   *   * @author 恆宇少年   */  @SpringBootApplication  @EnableLoggingClient  public class UserServiceApplication {      /**       * logger instance       */      static Logger logger = LoggerFactory.getLogger(UserServiceApplication.class);        public static void main(String[] args) {          SpringApplication.run(UserServiceApplication.class, args);          logger.info("{}服務啟動成功.", "用戶");      }  }

實例化RestTemplate對象

在user-service需要訪問賬戶服務獲取當前用戶的餘額,所以我們需要在user-service內實例化RestTemplate,這樣我們才可以通過RestTemplate訪問獲取用戶賬戶餘額資訊,我們直接在UserServiceApplication類內添加實例,如下所示:

    /**       * 實例化RestTemplate       *       * @return {@link RestTemplate}       */      @Bean      @ConditionalOnMissingBean      public RestTemplate restTemplate() {          return new RestTemplate();      }

註解解釋:

  • @ConditionalOnMissingBean:這是SpringBoot條件注入其中的一個註解,表示當IOC容器內不存在RestTemplate類型的實例時才會去執行restTemplate()方法創建對象。

查詢用戶資訊程式碼實現

/**   * 用戶基本資訊控制器   *   * @author 恆宇少年   */  @RestController  @RequestMapping(value = "/user")  public class UserController {      /**       * 示例,用戶列表       */      static final HashMap<Integer, User> USERS = new HashMap() {{          put(1, new User(1, "恆宇少年"));          put(2, new User(2, "於起宇"));      }};      /**       * 注入RestTemplate       */      @Autowired      private RestTemplate restTemplate;        /**       * 獲取用戶基本資訊       *       * @param userId 用戶編號       * @return       */      @GetMapping(value = "/{userId}")      public User getUserInfo(@PathVariable("userId") Integer userId) {          ResponseEntity<Double> responseEntity = restTemplate.getForEntity("http://localhost:9090/account/{accountId}", Double.class, userId);          Double balance = responseEntity.getBody();          User user = USERS.get(userId);          if (ObjectUtils.isEmpty(user)) {              throw new RuntimeException("用戶:" + userId + ",不存在.");          }          user.setBalance(balance);          return user;      }        @Data      public static class User {          private Integer id;          private String name;          private Double balance;            public User(Integer id, String name) {              this.id = id;              this.name = name;          }      }  }

我們所需要的兩個服務都已經編寫完成,下面我們來測試RestTemplate是可以透傳ApiBoot Logging的鏈路資訊?

運行測試

依次啟動logging-admin > user-service > account-service。

測試點:透傳鏈路資訊

我們使用curl命令訪問user-service提供的地址/user,如下所示:

➜ ~ curl http://localhost:9091/user/1  {"id":1,"name":"恆宇少年","balance":1233.22}

下面我看來看下logging-admin控制台接收到的請求日誌。

接收user-service請求日誌

Receiving Service: 【user-service -> 127.0.0.1】, Request Log Report,Logging Content:[      {          "endTime":1573032865311,          "httpStatus":200,          "requestBody":"",          "requestHeaders":{              "host":"localhost:9091",              "user-agent":"curl/7.64.1",              "accept":"*/*"          },          "requestIp":"0:0:0:0:0:0:0:1",          "requestMethod":"GET",          "requestParam":"{}",          "requestUri":"/user/1",          "responseBody":"{"id":1,"name":"恆宇少年","balance":1233.22}",          "responseHeaders":{},          "serviceId":"user-service",          "serviceIp":"127.0.0.1",          "servicePort":"9091",          "spanId":"f8cff018-42d5-481f-98df-c19b7196b3c3",          "startTime":1573032865130,          "timeConsuming":181,          "traceId":"16ad1dd4-beaa-4110-b4b7-fc7d952d9a57"      }  ]

接收account-service請求日誌

Receiving Service: 【account-service -> 127.0.0.1】, Request Log Report,Logging Content:[      {          "endTime":1573032865309,          "httpStatus":200,          "parentSpanId":"f8cff018-42d5-481f-98df-c19b7196b3c3",          "requestBody":"",          "requestHeaders":{              "minbox-logging-x-parent-span-id":"f8cff018-42d5-481f-98df-c19b7196b3c3",              "minbox-logging-x-trace-id":"16ad1dd4-beaa-4110-b4b7-fc7d952d9a57",              "host":"localhost:9090",              "connection":"keep-alive",              "accept":"application/json, application/*+json",              "user-agent":"Java/1.8.0_211"          },          "requestIp":"127.0.0.1",          "requestMethod":"GET",          "requestParam":"{}",          "requestUri":"/account/1",          "responseBody":"1233.22",          "responseHeaders":{},          "serviceId":"account-service",          "serviceIp":"127.0.0.1",          "servicePort":"9090",          "spanId":"63b18b40-5718-431c-972f-78956ce78380",          "startTime":1573032865307,          "timeConsuming":2,          "traceId":"16ad1dd4-beaa-4110-b4b7-fc7d952d9a57"      }  ]
  • 當我們訪問user-service服務內的/user路徑時,因為是第一次訪問ApiBoot Logging會主動創建traceId(鏈路編號)、spanId(單元編號),因為沒有上級單元所以parentSpanId為null.
  • 而通過查看account-service服務上報的請求日誌時,可以看到ApiBoot Logging相關的鏈路資訊是通過HttpHeader的方式進行傳遞的
    • minbox-logging-x-trace-id -> 鏈路編號
    • minbox-logging-x-parent-span-id -> 上級單元編號

敲黑板,劃重點

ApiBoot Logging在內部自動化實現了RestTemplate的攔截器配置,所以我們只需要創建實例就可以,而不需要主動去配置攔截器資訊,具體源碼請訪問org.minbox.framework.logging.client.http.rest.LoggingRestTemplateInterceptor查看。

不管你一次請求跨度幾個服務,都可以將請求入口生成的鏈路資訊進行依次傳遞,而上下級關係則是根據parentSpanId、spanId進行綁定的。

程式碼示例 本篇文章示例源碼可以通過以下途徑獲取,目錄為SpringBoot2.x/apiboot-logging-using-resttemplate-transparent-traceid:

Tags: