Spring Boot 3 的可觀察性
一、概述
在本文中,我們將學習如何使用 Spring Boot 3 配置可觀察性。可觀察性是僅通過其外部輸出來衡量系統內部狀態的能力。 (日誌、指標和跟踪)我們可以在“分佈式系統中的可觀察性”中了解基礎知識。此外,我們必須意識到 Spring Boot 2 和 3 之間存在重大變化。
我們只會捕獲有關 Spring Boot 3 的詳細信息。在從 Spring Boot 2 遷移的情況下,我們可以找到詳細說明。
2.千分尺觀測API
Micrometer是一個提供供應商中立的應用程序指標外觀的項目。它定義了計量器、速率聚合、計數器、儀表和計時器等概念,每個供應商都可以根據自己的概念和工具進行調整。一個核心部分是Observation API ,它允許對代碼進行一次檢測並具有多種好處。
它作為 Spring Framework 多個部分的依賴項包含在內,因此我們需要了解此 API 才能了解 Spring Boot 中的觀察工作原理。我們可以用一個簡單的例子來做到這一點。
2.1. Observation
和ObservationRegistry
來自dictionary.com的觀察是“為了某些科學或其他特殊目的而觀察或記錄事實或事件的行為或實例”。在我們的代碼中,我們可以觀察單個操作或完整的 HTTP 請求處理。在這些觀察中,我們可以進行測量,為分佈式跟踪創建跨度或只是註銷其他信息。
要創建一個觀察,我們需要一個ObservationRegistry
。
ObservationRegistry observationRegistry = ObservationRegistry.create();
Observation observation = Observation.createNotStarted("sample", observationRegistry);
Observations 的生命週期非常簡單,如下圖所示:
我們可以像這樣使用Observation
類型:
observation.start();
try (Observation.Scope scope = observation.openScope()) {
// ... the observed action
} catch (Exception e) {
observation.error(e);
// further exception handling
} finally {
observation.stop();
}
或者乾脆
observation.observe(() -> {
// ... the observed action
});
2.2. ObservationHandler
數據收集代碼作為ObservationHandler
實現。此處理程序收到有關Observation
的生命週期事件的通知,因此提供回調方法。可以這樣實現一個只打印事件的簡單處理程序:
public class SimpleLoggingHandler implements ObservationHandler<Observation.Context> {
private static final Logger log = LoggerFactory.getLogger(SimpleLoggingHandler.class);
@Override
public boolean supportsContext(Observation.Context context) {
return true;
}
@Override
public void onStart(Observation.Context context) {
log.info("Starting");
}
@Override
public void onScopeOpened(Observation.Context context) {
log.info("Scope opened");
}
@Override
public void onScopeClosed(Observation.Context context) {
log.info("Scope closed");
}
@Override
public void onStop(Observation.Context context) {
log.info("Stopping");
}
@Override
public void onError(Observation.Context context) {
log.info("Error");
}
}
然後我們在創建ObservationRegistry
之前在 ObservationRegistry 註冊ObservationHandler
Observation:
observationRegistry
.observationConfig()
.observationHandler(new SimpleLoggingHandler());
對於簡單的日誌記錄,一個實現已經存在。例如,要簡單地將事件記錄到控制台,我們可以使用:
observationRegistry
.observationConfig()
.observationHandler(new ObservationTextPublisher(System.out::println));
要使用定時器樣本和計數器,我們可以這樣配置:
MeterRegistry meterRegistry = new SimpleMeterRegistry();
observationRegistry
.observationConfig()
.observationHandler(new DefaultMeterObservationHandler(meterRegistry));
// ... observe using Observation with name "sample"
// fetch maximum duration of the named observation
Optional<Double> maximumDuration = meterRegistry.getMeters().stream()
.filter(m -> "sample".equals(m.getId().getName()))
.flatMap(m -> StreamSupport.stream(m.measure().spliterator(), false))
.filter(ms -> ms.getStatistic() == Statistic.MAX)
.findFirst()
.map(Measurement::getValue);
3. 彈簧集成
3.1.執行器
我們在具有 Actuator 依賴項的 Spring Boot 應用程序中獲得了最佳集成:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
它包含一個ObservationAutoConfiguratio
n,提供ObservationRegistry
的可注入實例(如果它尚不存在)並配置ObservationHandlers
以收集指標和跟踪。
例如,我們可以使用註冊表在服務中創建自定義觀察:
@Service
public class GreetingService {
private ObservationRegistry observationRegistry;
// constructor
public String sayHello() {
return Observation
.createNotStarted("greetingService", observationRegistry)
.observe(this::sayHelloNoObs);
}
private String sayHelloNoObs() {
return "Hello World!";
}
}
此外,它在ObservationRegistry
註冊ObservationHandler
bean。我們只需要提供 beans:
@Configuration
public class ObservationTextPublisherConfiguration {
private static final Logger log = LoggerFactory.getLogger(ObservationTextPublisherConfiguration.class);
@Bean
public ObservationHandler<Observation.Context> observationTextPublisher() {
return new ObservationTextPublisher(log::info);
}
}
3.2.網絡
對於 MVC 和 WebFlux,有 Filter 可以用於 HTTP 服務器觀察:
- 用於 Spring MVC 的
org.springframework.web.filter.ServerHttpObservationFilter
- 用於 WebFlux 的
org.springframework.web.filter.reactive.ServerHttpObservationFilter
當 Actuator 是我們應用程序的一部分時,這些過濾器已經註冊並處於活動狀態。如果沒有,我們需要配置它們:
@Configuration
public class ObservationFilterConfiguration {
// if an ObservationRegistry is configured
@ConditionalOnBean(ObservationRegistry.class)
// if we do not use Actuator
@ConditionalOnMissingBean(ServerHttpObservationFilter.class)
@Bean
public ServerHttpObservationFilter observationFilter(ObservationRegistry registry) {
return new ServerHttpObservationFilter(registry);
}
}
我們可以在文檔中找到有關 Spring Web 中可觀察性集成的更多詳細信息。
3.3.面向對象編程
Micrometer Observation API 還聲明了一個帶有基於 AspectJ 的方面實現的@Observed
註釋。為了完成這項工作,我們需要將 AOP 依賴項添加到我們的項目中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
然後,我們將方面實現註冊為 Spring 管理的 bean:
@Configuration
public class ObservedAspectConfiguration {
@Bean
public ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}
}
現在,我們可以快速編寫GreetingService
,而不是在我們的代碼中創建Observation
:
@Observed(name = "greetingService")
@Service
public class GreetingService {
public String sayHello() {
return "Hello World!";
}
}
結合 Actuator,我們可以使用http://localhost:8080/actuator/metrics/greetingService
讀取預先配置的指標(在我們至少調用一次服務之後),並將得到如下結果:
{
"name": "greetingService",
"baseUnit": "seconds",
"measurements": [
{
"statistic": "COUNT",
"value": 15
},
{
"statistic": "TOTAL_TIME",
"value": 0.0237577
},
{
"statistic": "MAX",
"value": 0.0035475
}
],
...
}
4. 測試觀察
Micrometer Observability API 提供了一個允許編寫測試的模塊。為此,我們需要添加以下依賴項:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation-test</artifactId>
<scope>test</scope>
</dependency>
micrometer-bom
是 Spring Boot 託管依賴的一部分,所以我們不需要指定任何版本。
因為默認情況下整個可觀察性自動配置對於測試是禁用的,所以我們需要使用@AutoConfigureObservability
重新啟用它,每當我們想要測試默認觀察時。
4.1. TestObservationRegistry
我們可以使用允許基於TestObservationRegistry
的斷言的 TestObservationRegistry。因此,我們必須用TestObservationRegistry
實例替換已經在上下文中的ObservationRegistry
。
因此,例如,如果我們想測試對GreetingService
的觀察,我們可以使用這個測試設置:
@ExtendWith(SpringExtension.class)
@ComponentScan(basePackageClasses = GreetingService.class)
@EnableAutoConfiguration
@Import(ObservedAspectConfiguration.class)
@AutoConfigureObservability
class GreetingServiceObservationTest {
@Autowired
GreetingService service;
@Autowired
TestObservationRegistry registry;
@TestConfiguration
static class ObservationTestConfiguration {
@Bean
TestObservationRegistry observationRegistry() {
return TestObservationRegistry.create();
}
}
// ...
}
我們還可以使用 JUnit 元註釋來配置它:
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({
ObservedAspectConfiguration.class,
EnableTestObservation.ObservationTestConfiguration.class
})
@AutoConfigureObservability
public @interface EnableTestObservation {
@TestConfiguration
class ObservationTestConfiguration {
@Bean
TestObservationRegistry observationRegistry() {
return TestObservationRegistry.create();
}
}
}
然後,我們只需要將註釋添加到我們的測試類:
@ExtendWith(SpringExtension.class)
@ComponentScan(basePackageClasses = GreetingService.class)
@EnableAutoConfiguration
@EnableTestObservation
class GreetingServiceObservationTest {
@Autowired
GreetingService service;
@Autowired
TestObservationRegistry registry;
// ...
}
然後,我們可以調用該服務並檢查觀察是否已完成:
import static io.micrometer.observation.tck.TestObservationRegistryAssert.assertThat;
// ...
@Test
void testObservation() {
// invoke service
service.sayHello();
assertThat(registry)
.hasObservationWithNameEqualTo("greetingService")
.that()
.hasBeenStarted()
.hasBeenStopped();
}
4.2.觀察處理器兼容性套件
為了測試我們的ObservationHandler
實現,我們可以在測試中繼承幾個基類(所謂的Compatibility Kits
):
-
NullContextObservationHandlerCompatibilityKit
測試觀察處理程序在null
值參數的情況下是否正常工作。 -
AnyContextObservationHandlerCompatibilityKit
測試觀察處理程序在未指定測試上下文參數的情況下是否正常工作。這也包括NullContextObservationHandlerCompatibilityKit
。 -
ConcreteContextObservationHandlerCompatibilityKit
測試觀察處理程序在測試上下文的上下文類型的情況下是否正常工作。
實現很簡單:
public class SimpleLoggingHandlerTest
extends AnyContextObservationHandlerCompatibilityKit {
SimpleLoggingHandler handler = new SimpleLoggingHandler();
@Override
public ObservationHandler<Observation.Context> handler() {
return handler;
}
}
這將導致以下輸出:
5. 千分尺追踪
之前的 Spring Cloud Sleuth 項目已經遷移到 Micrometer,自 Spring Boot 3 以來,Micrometer Tracing 的核心是 Micrometer Tracing。我們可以在文檔中找到 Micrometer Tracing 的定義:
Micrometer Tracing provides a simple facade for the most popular tracer libraries, letting you instrument your JVM-based application code without vendor lock-in. It is designed to add little to no overhead to your tracing collection activity while maximizing the portability of your tracing effort.
我們可以單獨使用它,但它也通過提供ObservationHandler
擴展與 Observation API 集成。
5.1.集成到觀察 API
要使用 Micrometer Tracing,我們需要在項目中添加以下依賴項——版本由 Spring Boot 管理:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
</dependency>
然後,我們需要一種受支持的跟踪器(目前是 OpenZipkin Brave或OpenTelemetry )。然後,我們必須為特定於供應商的集成添加到 Micrometer Tracing 的依賴項:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
要么
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
Spring Actuator 對兩個跟踪器都有自動配置,即它註冊特定於供應商的對象和 Micrometer Tracing ObservationHandler
實現,將這些對象委託到應用程序上下文中。因此,不需要進一步的配置步驟。
5.2.測試支持
出於測試目的,我們需要將以下依賴項添加到我們的項目中——版本由 Spring Boot 管理:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-test</artifactId>
<scope>test</scope>
</dependency>
然後,我們可以使用SimpleTracer
類在測試期間收集和驗證跟踪數據。為了讓它工作,我們在應用程序上下文中用SimpleTracer
替換了原始的、特定於供應商的Tracer
。我們還必須記住通過使用@AutoConfigureObservability
來啟用跟踪的自動配置。
因此,用於跟踪的最小測試配置可能如下所示:
@ExtendWith(SpringExtension.class)
@EnableAutoConfiguration
@AutoConfigureObservability
public class GreetingServiceTracingTest {
@TestConfiguration
static class ObservationTestConfiguration {
@Bean
TestObservationRegistry observationRegistry() {
return TestObservationRegistry.create();
}
@Bean
SimpleTracer simpleTracer() {
return new SimpleTracer();
}
}
@Test
void shouldTrace() {
// test
}
}
或者,在使用 JUnit 元註釋的情況下:
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@AutoConfigureObservability
@Import({
ObservedAspectConfiguration.class,
EnableTestObservation.ObservationTestConfiguration.class
})
public @interface EnableTestObservation {
@TestConfiguration
class ObservationTestConfiguration {
@Bean
TestObservationRegistry observationRegistry() {
return TestObservationRegistry.create();
}
@Bean
SimpleTracer simpleTracer() {
return new SimpleTracer();
}
}
}
然後我們可以通過以下示例測試來測試我們的 GreetingService:
import static io.micrometer.tracing.test.simple.TracerAssert.assertThat;
// ...
@Autowired
GreetingService service;
@Autowired
SimpleTracer tracer;
// ...
@Test
void testTracingForGreeting() {
service.sayHello();
assertThat(tracer)
.onlySpan()
.hasNameEqualTo("greeting-service#say-hello")
.isEnded();
}
六,結論
在本教程中,我們探討了Micrometer Observation API
以及與 Spring Boot 3 的集成。我們了解到Micrometer
是一個用於獨立於供應商的儀器的 API,而Micrometer Tracing
是一個擴展。我們了解到Actuator
有一組預配置的觀察和跟踪,並且默認情況下禁用測試的可觀察性自動配置。
像往常一樣,所有代碼實現都可以在 GitHub 上找到。