ApiBoot Logging使用SpringCloud Openfeign透傳鏈路信息

ApiBoot Logging能夠無縫整合SpringCloud來採集請求日誌,目前支持RestTemplateOpenfeign兩種方式,咱們本章來說解下在使用Openfeign完成服務之間請求相互調用的一條鏈路請求日誌是否能夠都採集到。html

博客原文地址:blog.yuqiyu.com/apiboot-log…java

搭建Eureka Server

咱們先來搭建一個Eureka Server,請訪問【搭建服務註冊中心Eureka Server】文章內容查看具體搭建流程。git

搭建Logging Admin

咱們須要搭建一個Logging Admin用於接收Logging Client上報的請求日誌,請訪問【ApiBoot Logging整合SpringCloud Eureka負載均衡上報日誌】查看具體的搭建流程。web

咱們本章來模擬提交訂單的業務邏輯,涉及到兩個服務,分別是:商品服務訂單服務,接下來咱們須要來建立這兩個服務。spring

本章源碼採用Maven多模塊的形式進行編寫,請拉至文章末尾查看下載本章源碼。json

添加ApiBoot & SpringCloud統一版本

因爲是採用Maven 多模塊項目,存在繼承關係,咱們只須要在root模塊添加版本依賴便可,其餘子模塊就能夠直接使用,以下所示:api

<properties>
  <java.version>1.8</java.version>
  <!--ApiBoot版本號-->
  <api.boot.version>2.1.5.RELEASE</api.boot.version>
  <!--SpringCloud版本號-->
  <spring.cloud.version>Greenwich.SR3</spring.cloud.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>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring.cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
複製代碼

建立公共Openfeign接口定義

學習過Openfeign的同窗應該都知道,Openfeign能夠繼承實現,咱們只須要建立一個公共的服務接口定義,在實現該接口的服務進行業務實現,在調用該接口的地方直接注入便可。 下面咱們建立一個名爲common-openfeign的公共依賴項目,pom.xml添加依賴以下所示:bash

<dependencies>
  <!--SpringBoot Web-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <optional>true</optional>
  </dependency>
  <!--Openfeign-->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <optional>true</optional>
  </dependency>
</dependencies>
複製代碼

在提交訂單時咱們簡單模擬須要獲取商品的單價,因此在common-openfeign項目內咱們要提供一個查詢商品單價的服務接口,建立一個名爲GoodClient的接口以下所示:架構

package org.minbox.chapter.common.openfeign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/** * 商品服務接口定義 * * @author 恆宇少年 */
@FeignClient(name = "good-service")
@RequestMapping(value = "/good")
public interface GoodClient {
    /** * 獲取商品價格 * * @param goodId 商品編號 * @return */
    @GetMapping(value = "/{goodId}")
    Double getGoodPrice(@PathVariable("goodId") Integer goodId);
}
複製代碼

註解解釋:app

  • @FeignClientSpringCloud Openfeign提供的接口客戶端定義註解,經過value或者name來指定GoodClient訪問的具體ServiceID,這裏咱們配置的value值爲good-service項目spring.application.name配置參數(ServiceID = spring.application.name)。

這樣當咱們經過注入GoodClient接口調用getGoodPrice方法時,底層經過OpenfeignHttp代理訪問good-service的對應接口。

建立商品服務

下面咱們再來建立一個名爲good-serviceSpringBoot項目。

添加相關依賴

pom.xml項目配置文件內添加以下依賴:

<dependencies>
  <!--ApiBoot Logging Client-->
  <dependency>
    <groupId>org.minbox.framework</groupId>
    <artifactId>api-boot-starter-logging</artifactId>
  </dependency>

  <!--SpringBoot Web-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <!--Eureka Client-->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  </dependency>

  <!--公共Openfeign接口定義依賴-->
  <dependency>
    <groupId>org.minbox.chapter</groupId>
    <artifactId>common-openfeign</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </dependency>
</dependencies>
複製代碼

能夠看到咱們在good-service項目依賴內添加了咱們在上面建立的common-openfeign依賴模塊,由於GoodClient服務接口的實現是在good-service項目內,咱們須要添加common-openfeign依賴後建立對應的XxxController並實現GoodClient接口完成對應的業務邏輯實現。

商品業務實現

這裏咱們簡單作個示例,將價格固定返回,實現GoodClient的控制器以下所示:

package org.minbox.chapter.good.service;

import org.minbox.chapter.common.openfeign.GoodClient;
import org.springframework.web.bind.annotation.RestController;

/** * 商品服務接口實現 * * @author 恆宇少年 * @see GoodClient */
@RestController
public class GoodController implements GoodClient {
    @Override
    public Double getGoodPrice(Integer goodId) {
        if (goodId == 1) {
            return 15.6;
        }
        return 0D;
    }
}
複製代碼

註冊到Eureka Server

咱們須要將good-service註冊到Eureka Server,修改application.yml配置文件以下所示:

# ServiceID
spring:
 application:
 name: good-service
# 端口號
server:
 port: 8082
# Eureka Config
eureka:
 client:
 service-url:
 defaultZone: http://127.0.0.1:10000/eureka/
 instance:
 prefer-ip-address: true
複製代碼

配置上報的Logging Admin

咱們須要將good-service的請求日誌上報到Logging Admin,採用SpringCloud ServiceID的方式配置,修改application.yml配置文件以下所示:

api:
 boot:
 logging:
      # 控制檯打印日誌
 show-console-log: true
      # 美化打印日誌
 format-console-log-json: true
      # 配置Logging Admin 服務編號
 discovery:
 service-id: logging-admin
複製代碼

啓用Eureka Client & Logging

最後咱們在XxxApplication入口類添加註解來啓用Eureka Client以及Logging Client,以下所示:

/** * 商品服務 * * @author 恆宇少年 */
@SpringBootApplication
@EnableLoggingClient
@EnableDiscoveryClient
public class GoodServiceApplication {
    /** * logger instance */
    static Logger logger = LoggerFactory.getLogger(GoodServiceApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(GoodServiceApplication.class, args);
        logger.info("{}服務啓動成功.", "商品");
    }
}
複製代碼

至此咱們的商品服務已經準備完成.

建立訂單服務

建立一個名爲order-serviceSpringBoot項目(建議參考源碼,本章採用Maven多模塊建立)。

添加相關依賴

修改pom.xml添加相關依賴以下所示:

<dependencies>
  <!--ApiBoot Logging Client-->
  <dependency>
    <groupId>org.minbox.framework</groupId>
    <artifactId>api-boot-starter-logging</artifactId>
  </dependency>

  <!--SpringBoot Web-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <!--Eureka Client-->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  </dependency>

  <!--Openfeign-->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
  </dependency>

  <!--公共Openfeign接口定義依賴-->
  <dependency>
    <groupId>org.minbox.chapter</groupId>
    <artifactId>common-openfeign</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </dependency>
</dependencies>
複製代碼

訂單業務實現

咱們來模擬一個提交訂單的場景,建立一個名爲OrderController的控制器,以下所示:

/** * 訂單控制器 * * @author 恆宇少年 */
@RestController
@RequestMapping(value = "/order")
public class OrderController {
    /** * 商品接口定義注入 * {@link GoodClient} */
    @Autowired
    private GoodClient goodClient;

    @PostMapping
    public String submit(Integer goodId, Integer buyCount) {
        Double goodPrice = goodClient.getGoodPrice(goodId);
        Double orderAmount = goodPrice * buyCount;
        //...
        return "訂單提交成功,訂單總金額:" + orderAmount;
    }
}
複製代碼

註冊到Eureka Server

將咱們建立的order-service註冊到Eureka Server,修改application.yml配置文件以下所示:

spring:
 application:
 name: order-service
server:
 port: 8081
# Eureka Config
eureka:
 client:
 service-url:
 defaultZone: http://127.0.0.1:10000/eureka/
 instance:
 prefer-ip-address: true
複製代碼

配置上報的Logging Admin

咱們須要將order-service的請求日誌上報到Logging Admin,採用SpringCloud ServiceID的方式配置,修改application.yml配置文件以下所示:

api:
 boot:
 logging:
      # 控制檯打印日誌
 show-console-log: true
      # 美化打印日誌
 format-console-log-json: true
      # 配置Logging Admin 服務編號
 discovery:
 service-id: logging-admin
複製代碼

啓用Eureka Client & Logging

修改order-service入口類OrderServiceApplication,添加啓用Eureka ClientLogging Client的註解,以下所示:

/** * 訂單服務 * * @author 恆宇少年 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableLoggingClient
@EnableFeignClients(basePackages = "org.minbox.chapter.common.openfeign")
public class OrderServiceApplication {
    /** * logger instance */
    static Logger logger = LoggerFactory.getLogger(OrderServiceApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
        logger.info("{}服務啓動成功.", "");
    }
}
複製代碼

註解解釋:

  • @EnableFeignClients:該註解是Openfeign提供的啓用自動掃描Client的配置,咱們經過basePackages(基礎包名)的方式進行配置掃描包下配置@FeignClient註解的接口,併爲每一個接口生成對應的代理實現並添加到Spring IOC容器。

    org.minbox.chapter.common.openfeign包名在common-openfeign項目內。

運行測試

依次啓動項目,eureka-server > logging-admin > good-service > order-service

經過curl命令訪問order-service內的提交訂單地址:/order,以下所示:

➜ ~ curl -X POST http://localhost:8081/order\?goodId\=1\&buyCount\=3
訂單提交成功,訂單總金額:46.8
複製代碼

能夠看到咱們已經能夠成功的獲取訂單的總金額,咱們在/order請求方法內調用good-service獲取商品的單價後計算獲得訂單總金額。

測試點:鏈路信息傳遞

咱們經過控制檯輸出的日誌信息來確認下鏈路信息(traceId、spanId)的透傳是否正確。

收到order-service上報的日誌

Receiving Service: 【order-service -> 127.0.0.1】, Request Log Report,Logging Content:[
	{
		"endTime":1573009439840,
		"httpStatus":200,
		"requestBody":"",
		"requestHeaders":{
			"host":"localhost:8081",
			"user-agent":"curl/7.64.1",
			"accept":"*/*"
		},
		"requestIp":"0:0:0:0:0:0:0:1",
		"requestMethod":"POST",
		"requestParam":"{\"buyCount\":\"3\",\"goodId\":\"1\"}",
		"requestUri":"/order",
		"responseBody":"訂單提交成功,訂單總金額:46.8",
		"responseHeaders":{},
		"serviceId":"order-service",
		"serviceIp":"127.0.0.1",
		"servicePort":"8081",
		"spanId":"241ef717-b0b3-4fcc-adae-b63ffd3dbbe4",
		"startTime":1573009439301,
		"timeConsuming":539,
		"traceId":"3e20cc72-c880-4575-90ed-d54a6b4fe555"
	}
]
複製代碼

收到good-service上報的日誌

Receiving Service: 【good-service -> 127.0.0.1】, Request Log Report,Logging Content:[
	{
		"endTime":1573009439810,
		"httpStatus":200,
		"parentSpanId":"241ef717-b0b3-4fcc-adae-b63ffd3dbbe4",
		"requestBody":"",
		"requestHeaders":{
			"minbox-logging-x-parent-span-id":"241ef717-b0b3-4fcc-adae-b63ffd3dbbe4",
			"minbox-logging-x-trace-id":"3e20cc72-c880-4575-90ed-d54a6b4fe555",
			"host":"10.180.98.156:8082",
			"connection":"keep-alive",
			"accept":"*/*",
			"user-agent":"Java/1.8.0_211"
		},
		"requestIp":"10.180.98.156",
		"requestMethod":"GET",
		"requestParam":"{}",
		"requestUri":"/good/1",
		"responseBody":"15.6",
		"responseHeaders":{},
		"serviceId":"good-service",
		"serviceIp":"127.0.0.1",
		"servicePort":"8082",
		"spanId":"6339664e-097d-4a01-a734-935de52a7d44",
		"startTime":1573009439787,
		"timeConsuming":23,
		"traceId":"3e20cc72-c880-4575-90ed-d54a6b4fe555"
	}
]
複製代碼

結果分析:

  • 請求日誌的入口爲order-service因此並不存在parentSpanId(上級單元編號),而spanId(單元編號)、traceId(鏈路編號)也是新生成的。

  • 本次請求會通過good-service服務,所以parentSpanId則是order-service生成的spanIdtraceId一樣也是order-service生成的,透傳HttpHeader方式進行傳遞,表示在同一條請求鏈路上。

敲黑板,劃重點

ApiBoot Logging支持使用Openfeign傳遞鏈路信息,內部經過Openfeign攔截器實現,源碼詳見:org.minbox.framework.logging.client.http.openfeign.LoggingOpenFeignInterceptor

traceId(鏈路編號)、parentSpanId(單元編號)經過HttpHeader的形式傳遞到目標訪問服務,服務經過請求日誌攔截器進行提取並設置鏈路綁定關係。

  • traceId傳遞時HttpHeader名稱爲:minbox-logging-x-trace-id.
  • parentSpanId傳遞時HttpHeader名稱爲:minbox-logging-x-parent-span-id

代碼示例

若是您喜歡本篇文章請爲源碼倉庫點個Star,謝謝!!! 本篇文章示例源碼能夠經過如下途徑獲取,目錄爲SpringBoot2.x/apiboot-logging-using-openfeign-transparent-traceid

做者我的 博客 使用開源框架 ApiBoot 助你成爲Api接口服務架構師

相關文章
相關標籤/搜索