Spring Boot 中实现日志链路追踪

1,259 阅读4分钟

在 Spring Boot 中实现日志链路追踪通常需要借助一些额外的库或框架来跟踪服务间调用的上下文信息,如请求 ID 或事务 ID。以下是实现链路追踪的一些常用方法和步骤:使用 Spring Cloud Sleuth、集成 Zipkin

Spring Cloud Sleuth 简介

Spring Cloud Sleuth 是一个为分布式系统提供链路追踪的工具。它帮助开发者追踪在一个微服务架构中由一个用户请求引发的多个服务间的调用。Sleuth 通过在日志中自动添加跟踪 ID 和跨度 ID 来实现这一功能,使得可以追踪整个请求链路。

场景描述

假设你有一个简单的电商应用,包含两个微服务:

  • 订单服务:接收订单请求并处理订单。
  • 库存服务:检查商品库存并更新库存信息。

用户下单时,订单服务需要调用库存服务来确保商品库存足够。

实例代码案例

首先,你需要在两个服务的 pom.xmlbuild.gradle 文件中添加 Spring Cloud Sleuth 的依赖。

Maven 依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

Gradle 依赖:

implementation 'org.springframework.cloud:spring-cloud-starter-sleuth'

1. 订单服务

订单服务Controller(OrderController.java)

package com.example.orderservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @PostMapping("/order")
    public String createOrder(@RequestBody Order order) {
        // 假设库存服务的URL是 http://inventory-service/check-inventory
        String url = "http://inventory-service/check-inventory";
        Boolean isAvailable = restTemplate.postForObject(url, order, Boolean.class);
        
        if (Boolean.TRUE.equals(isAvailable)) {
            return "Order placed successfully!";
        } else {
            return "Order failed due to insufficient stock.";
        }
    }
}

订单服务配置(OrderServiceApplication.java)

package com.example.orderservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

2. 库存服务

库存服务Controller(InventoryController.java)

package com.example.inventoryservice;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class InventoryController {

    @PostMapping("/check-inventory")
    public Boolean checkInventory(@RequestBody Order order) {
        // 这里简化逻辑:假设所有商品都有足够的库存
        return true;
    }
}

库存服务配置(InventoryServiceApplication.java)

package com.example.inventoryservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class InventoryServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(InventoryServiceApplication.class, args);
    }
}

测试和验证

启动这两个服务,并向订单服务发送一个 POST 请求到 /order 端点,使用类似 Postman 的工具。你可以在两个服务的日志文件中看到由 Sleuth 添加的跟踪信息,如 traceIdspanId

这样,即使在复杂的微服务架构中,你也能追踪特定用户请求的完整路径,从而便于调试和监控服务性能。

Zipkin 简介

Zipkin 是一个开源的分布式跟踪系统,由 Twitter 开发。它可以帮助收集和分析微服务架构中服务间调用的数据,提供延迟分析、服务依赖关系探测、故障诊断等功能。Spring Cloud Sleuth 和 Zipkin 的集成可以使服务间的请求链路更加透明,方便开发者监控和排错。

场景描述

假设有一个电商平台,包含用户服务、订单服务和库存服务。当用户下单时,用户服务会验证用户信息,订单服务会处理订单逻辑,库存服务会检查和更新库存。我们将使用 Zipkin 来跟踪这一过程中的所有服务调用,以便于查看请求从一个服务传递到另一个服务的全过程和时间消耗。

集成 Zipkin

1. 添加 Zipkin 服务器

首先,你需要设置一个 Zipkin 服务器。可以通过 Docker 快速启动一个 Zipkin 服务器:

docker run -d -p 9411:9411 openzipkin/zipkin

2. 添加 Zipkin 依赖到 Spring Boot 应用

在每个微服务的 pom.xmlbuild.gradle 文件中添加 Zipkin 的依赖:

Maven 依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

Gradle 依赖:

implementation 'org.springframework.cloud:spring-cloud-starter-zipkin'

3. 配置 Zipkin 客户端

在每个微服务的 application.ymlapplication.properties 文件中配置 Zipkin 客户端,指定 Zipkin 服务器的地址:

spring:
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      probability: 1.0  # 记录所有请求

示例代码

假设我们有三个服务:用户服务、订单服务和库存服务。这里给出订单服务和库存服务的简化示例代码。

订单服务 (Order Service)

OrderController.java

package com.example.orderservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @PostMapping("/orders")
    public String createOrder(@RequestBody Order order) {
        // 调用库存服务
        String inventoryServiceUrl = "http://inventory-service/inventory/check";
        boolean inventoryAvailable = restTemplate.postForObject(inventoryServiceUrl, order, Boolean.class);
        
        if (inventoryAvailable) {
            // 处理订单逻辑
            return "Order processed successfully";
        } else {
            return "Insufficient inventory";
        }
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

OrderServiceApplication.java

package com.example.orderservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

库存服务 (Inventory Service)

InventoryController.java

package com.example.inventoryservice;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class InventoryController {

    @PostMapping("/inventory/check")
    public boolean checkInventory(@RequestBody Order order) {
        // 检查库存逻辑
        return order.getQuantity() <= 50;  // 假设库存检查
    }
}

InventoryServiceApplication.java

package com.example.inventoryservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class InventoryServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(InventoryServiceApplication.class, args);
    }
}

测试和验证

启动所有服务并发送请求到订单服务的 /orders 端点。之后,可以在 Zipkin UI (http://localhost:9411) 中查看请求的跟踪信息,包括每个服务调用的详细时间和链路。这将有助于理解服务间的交互和潜在的瓶颈。