在現代微服務架構中,隨著服務數量的增加,系統的復雜性也隨之增加。一個請求可能會經過多個服務,每個服務都可能調用其他服務。這種情況下,如何追蹤一個請求的完整路徑,以及每個服務的調用情況,成為了一個非常重要的問題。Spring Cloud Sleuth 就是為了解決這個問題而生的。
Spring Cloud Sleuth 是 Spring Cloud 生態系統中的一個組件,用于在分布式系統中實現鏈路追蹤。它通過為每個請求生成唯一的追蹤ID(Trace ID)和跨度ID(Span ID),來追蹤請求在系統中的流轉路徑。Sleuth 可以與 Zipkin 等分布式追蹤系統集成,將追蹤數據發送到這些系統中進行存儲和展示。
在了解如何使用 Sleuth 之前,我們需要先了解一些核心概念:
首先,我們需要在項目中添加 Sleuth 的依賴。如果你使用的是 Maven,可以在 pom.xml
中添加以下依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
如果你使用的是 Gradle,可以在 build.gradle
中添加以下依賴:
implementation 'org.springframework.cloud:spring-cloud-starter-sleuth'
Sleuth 的配置非常簡單,通常情況下不需要進行額外的配置。Sleuth 會自動為每個請求生成 Trace ID 和 Span ID,并將這些信息添加到日志中。
如果你需要自定義 Sleuth 的行為,可以在 application.yml
或 application.properties
中進行配置。例如,你可以配置 Sleuth 的采樣率:
spring:
sleuth:
sampler:
probability: 1.0
probability
表示采樣率,取值范圍是 0.0 到 1.0。1.0 表示對所有請求進行采樣,0.5 表示對 50% 的請求進行采樣。
Sleuth 會自動將 Trace ID 和 Span ID 添加到日志中。你可以在日志中看到類似以下的輸出:
2023-10-01 12:00:00.000 INFO [service-name,trace-id,span-id,true] 12345 --- [nio-8080-exec-1] c.e.s.ServiceController : Processing request
其中,[service-name,trace-id,span-id,true]
就是 Sleuth 添加的追蹤信息。service-name
是當前服務的名稱,trace-id
是請求的 Trace ID,span-id
是當前 Span 的 ID,true
表示這是一個可導出的 Span。
Sleuth 可以與 Zipkin 集成,將追蹤數據發送到 Zipkin 中進行存儲和展示。要集成 Zipkin,首先需要添加 Zipkin 的依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
然后,在 application.yml
中配置 Zipkin 的地址:
spring:
zipkin:
base-url: http://localhost:9411
配置完成后,Sleuth 會自動將追蹤數據發送到 Zipkin 中。你可以在 Zipkin 的 UI 中查看請求的完整路徑和每個服務的調用情況。
在某些情況下,你可能需要手動創建 Span。例如,你希望在某個方法中記錄一些自定義的追蹤信息。你可以使用 Tracer
接口來手動創建 Span:
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Autowired
private Tracer tracer;
public void doSomething() {
Span newSpan = tracer.nextSpan().name("my-custom-span").start();
try (Tracer.SpanInScope ws = tracer.withSpan(newSpan.start())) {
// 在這里執行你的業務邏輯
} finally {
newSpan.end();
}
}
}
在這個例子中,我們手動創建了一個名為 my-custom-span
的 Span,并在其中執行了一些業務邏輯。tracer.withSpan(newSpan.start())
用于將當前 Span 設置為新的 Span,并在 try
塊結束后自動結束 Span。
你還可以為 Span 添加自定義標簽,以便在 Zipkin 中更好地展示追蹤信息。你可以使用 Span
的 tag
方法來添加標簽:
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Autowired
private Tracer tracer;
public void doSomething() {
Span newSpan = tracer.nextSpan().name("my-custom-span").start();
try (Tracer.SpanInScope ws = tracer.withSpan(newSpan.start())) {
newSpan.tag("custom-tag", "custom-value");
// 在這里執行你的業務邏輯
} finally {
newSpan.end();
}
}
}
在這個例子中,我們為 Span 添加了一個名為 custom-tag
的標簽,并將其值設置為 custom-value
。
在微服務架構中,異步調用是非常常見的。Sleuth 也支持在異步調用中進行追蹤。你只需要使用 Tracer
的 wrap
方法來包裝異步任務:
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class MyService {
@Autowired
private Tracer tracer;
public CompletableFuture<Void> doSomethingAsync() {
return CompletableFuture.runAsync(tracer.wrap(() -> {
// 在這里執行你的異步任務
}));
}
}
在這個例子中,我們使用 tracer.wrap
方法包裝了一個異步任務。Sleuth 會自動為這個異步任務生成一個新的 Span,并將其與當前 Span 關聯起來。
在微服務架構中,服務之間的調用是非常頻繁的。Sleuth 會自動為跨服務調用生成新的 Span,并將其與當前 Span 關聯起來。你只需要確保在跨服務調用時傳遞 Trace ID 和 Span ID。
如果你使用的是 RestTemplate 進行 HTTP 調用,Sleuth 會自動為你處理這些細節:
import org.springframework.web.client.RestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Autowired
private RestTemplate restTemplate;
public void callAnotherService() {
String response = restTemplate.getForObject("http://another-service/api", String.class);
// 處理響應
}
}
如果你使用的是 Feign 客戶端,Sleuth 也會自動為你處理這些細節:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "another-service")
public interface AnotherServiceClient {
@GetMapping("/api")
String callApi();
}
默認情況下,Sleuth 會使用 HTTP 請求的路徑作為 Span 的名稱。如果你希望自定義 Span 的名稱,可以使用 @SpanName
注解:
import org.springframework.cloud.sleuth.annotation.SpanName;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@SpanName("my-custom-span-name")
public void doSomething() {
// 在這里執行你的業務邏輯
}
}
在這個例子中,我們使用 @SpanName
注解將 Span 的名稱設置為 my-custom-span-name
。
在某些情況下,你可能希望過濾掉某些 Span。例如,你可能不希望記錄某些健康檢查請求的 Span。你可以通過實現 SpanFilter
接口來自定義 Span 過濾器:
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanFilter;
import org.springframework.stereotype.Component;
@Component
public class MySpanFilter implements SpanFilter {
@Override
public boolean isExportable(Span span) {
// 在這里實現你的過濾邏輯
return !span.name().contains("health-check");
}
}
在這個例子中,我們實現了一個 SpanFilter
,過濾掉所有名稱包含 health-check
的 Span。
Spring Cloud Sleuth 是一個非常強大的工具,可以幫助我們在分布式系統中實現鏈路追蹤。通過為每個請求生成唯一的 Trace ID 和 Span ID,Sleuth 可以追蹤請求在系統中的流轉路徑,并將這些信息記錄到日志中。通過與 Zipkin 等分布式追蹤系統集成,我們可以更好地理解和分析系統的調用情況。
在實際使用中,Sleuth 的配置和使用非常簡單,幾乎不需要額外的代碼。我們只需要添加依賴,配置采樣率,就可以開始使用 Sleuth 進行鏈路追蹤。如果你有更復雜的需求,例如手動創建 Span、添加自定義標簽、過濾 Span 等,Sleuth 也提供了豐富的 API 來滿足這些需求。
希望這篇文章能幫助你更好地理解和使用 Spring Cloud Sleuth。如果你有任何問題或建議,歡迎在評論區留言。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。