十一、监测和Metrics
监视和测量正在发生的事情的能力是非常重要的。尤其是在像Axon应用程序这样的位置透明环境中,能够跟踪您的消息并检查它的接收率是非常重要的
11.1监控
监控以消息为中心的应用程序将要求您能够查看消息在给定时间点的位置。这意味着能够在Axon应用程序中从一个组件跟踪命令、事件和查询到另一个组件
11.1.1相关数据
与此相关的一个重要方面是跟踪给定的消息。为此,框架提供了CorrelationDataProvider,如下文所述。此接口及其实现为您提供了用特定字段填充消息元数据的方法,如“跟踪id”、“相关id”或您可能感兴趣的任何其他字段
要配置MessageOriginProvider,您可以执行以下操作:
Axon API
public class MonitoringConfiguration {
public Configurer buildConfigurer() {
return DefaultConfigurer.defaultConfiguration();
}
public void configureMessageOriginProvider(Configurer configurer) {
configurer.configureCorrelationDataProviders(configuration -> {
List<CorrelationDataProvider> correlationDataProviders = new ArrayList<>();
correlationDataProviders.add(new MessageOriginProvider());
return correlationDataProviders;
});
}
}
Springboot 自动配置
public class MonitoringConfiguration {
// When using Spring Boot, simply defining a CorrelationDataProvider bean
//is sufficient
public CorrelationDataProvider messageOriginProvider() {
return new MessageOriginProvider();
}
}
11.1.2拦截器记录
在Axon应用程序中跟踪消息流的另一个好方法是在应用程序中设置正确的拦截器。有两种类型的拦截器,分派拦截器和处理程序拦截器(如这里所讨论的),它们在发布消息之前拦截消息(分派拦截器)或在消息被处理时截获消息(处理程序拦截器)。拦截器机制很好地引入了一种在消息被调度/处理时一致地进行日志记录的方法。LoggingInterceptor是一个现成的解决方案,可以将任何类型的消息记录到SLF4J,但也提供了一个简单的可重写模板来设置您自己想要的日志格式。关于如何配置消息拦截器,我们参考命令、事件和查询部分。
11.1.3事件跟踪程序状态
因为跟踪任何事件的进程,所以他们提供了一个跟踪任何事件的线索。这样一个钩子证明了它的有用性,当我们想要重建视图模型时,我们想检查处理器何时赶上了所有事件
为此,TrackingEventProcessor公开processingStatus()方法。它返回一个映射,其中键是段标识符,值是“事件跟踪器状态”。事件跟踪程序状态公开了一些指标:
- 反映其状态的segment。
- 通过isCaughtUp()指定是否与事件流同步的布尔值。
- 通过isReplaying()指定给定段是否正在重播的布尔值。
- 通过isMerging()指定给定段是否正在合并的布尔值。
- 给定段的TrackingToken。
- 通过isErrorState()指定段是否处于错误状态的布尔值。
- 如果事件跟踪器到达错误状态,则为可选的throwable。
- 一个可选的Long-through getCurrentPosition,用于定义TrackingToken的当前位置。
- 一个可选的Long-through getResetPosition,用于定义TrackingToken重置时的位置。如果isReplaying()返回false,则此字段将为空。通过将当前位置与此字段进行比较,可以得出重放的估计持续时间。
- 一个可选的Long-through mergeCompletedPosition(),定义合并完成时TrackingToken上的位置。
- 如果isMerging()返回false,则此字段将为空。通过将当前位置与此字段进行比较,可以得出合并的估计持续时间。
有些场景需要一种当处理器的状态发生变化时做出反应的方法。例如,每当状态从replay变为true或false时。为此,可以通过TrackingEventProcessor或TrackingEventProcessor的配置来配置eventtrackerstatuschangelister。
EventTrackerStatusChangeListener是一个定义OneEventTrackerStatusChange(Map<Integer,EventTrackerStatus>)方法的函数接口,每当任何一个EventTrackerStatus对象发生重大更改时,TrackingEventProcessor都将调用该方法。EventTrackerStatus的整数集合提供了导致调用更改侦听器的状态。因此,您可以检查给定的EventTrackerStatus'以作出相应的反应。
要知道,默认情况下,处理器只会在任何布尔字段发生更改时调用更改侦听器。如果还需要对位置更改做出响应,则可以提供一个EventTrackerStatusChangeListener,它重写validatePositions方法返回true。请注意,这意味着更改侦听器将经常被调用,因为它被期望处理许多事件。
11.2 metrics(标准)
在以消息为中心的系统中,有趣的度量有几种形式和风格,例如计数、容量和延迟。Axon框架允许您通过使用axon-metrics或axon-micrometer模块来检索这些度量。使用这些模块,您可以向消息传递组件注册许多MessageMonitor实现,如CommandBus、EventBus、QueryBus和EventProcessors
axon metrics模块使用Dropwizard来正确注册度量。这意味着MessageMonitors是根据Dropwizard MetricRegistry注册的
axon Millimeter模块使用的是dimensional first metrics集合facade,其目标是允许您使用供应商中立的API计时、计数和测量代码。这意味着信息监视器是根据测微计注册的
目前提供以下监视器实现:
- CapacityMonitor—通过跟踪消息处理所花费的总时间与它处于活动状态的总时间的比较来测量消息容量。这将返回0到n个线程数之间的数字。因此,如果有4个线程在工作,如果每个线程100%都处于活动状态,则最大容量为4。
- EventProcessorLatencyMonitor-测量上次摄取和最后处理的事件消息之间的消息时间戳的差异。
- MessageCountingMonitor—统计接收、成功、失败、忽略和已处理的消息数。
- MessageTimerMonitor—为所有成功的、失败的和被忽略的消息保留一个计时器,并为这三个消息的组合保留一个总计时器。
- PayloadTypeMessageMonitorWrapper—一种特殊的MessageMonitor实现,它允许为每个消息类型而不是每个消息发布/处理组件设置一个监视器。
您可以通过消息传递组件上的构造函数自由配置MessageMonitors的任何组合,只需使用configurationapi。包含在axon metrics和axon Millicler模块中的GlobalMetricRegistry为每种类型的消息传递组件提供了一组合理的默认值。以下示例显示如何为消息处理组件配置默认指标:
##Dropwizard的配置:
Axon api
public class MetricsConfiguration {
public Configurer buildConfigurer() {
return DefaultConfigurer.defaultConfiguration();
}
// The MetricRegistry is a class from the Dropwizard Metrics framework
public void configureDefaultMetrics(Configurer configurer, MetricRegistry metricRegistry) {
GlobalMetricRegistry globalMetricRegistry = new GlobalMetricRegistry(metricRegistry);
// We register the default monitors to our messaging components by //doing the following
globalMetricRegistry.registerWithConfigurer(configurer);
}
}
SpringBoot 自动配置
# The default value is `true`. Thus you will have Metrics configured if `axon-
# metrics` and `io.dropwizard.metrics` are on your classpath.
axon.metrics.auto-configuration.enabled=true
##micrometer的配置:
无tags的Axon 的API
public class MetricsConfiguration {
public Configurer buildConfigurer() {
return DefaultConfigurer.defaultConfiguration();
}
// The MeterRegistry is a class from the Micrometer library
public void configureDefaultMetrics(Configurer configurer, MeterRegistry meterRegistry) {
GlobalMetricRegistry globalMetricRegistry = new GlobalMetricRegistry(meterRegistry);
globalMetricRegistry.registerWithConfigurer(configurer);
}
}
有tags的Axon 的API
public class MetricsConfiguration {
public Configurer buildConfigurer() {
return DefaultConfigurer.defaultConfiguration();
}
// The MeterRegistry is a class from the Micrometer library
public void configureDefaultMetrics(Configurer configurer, MeterRegistry meterRegistry) {
GlobalMetricRegistry globalMetricRegistry = new GlobalMetricRegistry(meterRegistry);
globalMetricRegistry.registerWithConfigurerWithDefaultTags(configurer);
}
}
无tags的Springboot的自动配置
# The default value is `true`.
# Thus you will have Metrics configured if `axon-micrometer` and
# appropriate metric implementation (for example: `micrometer-registry-prometheus`) are on your classpath.
axon.metrics.auto-configuration.enabled=true
# Spring Boot metrics enabled
management.endpoint.metrics.enabled=true
# Spring Boot (Prometheus) endpoint (`/actuator/prometheus`) enabled and exposed
management.metrics.export.prometheus.enabled=true
management.endpoint.prometheus.enabled=true
有tags的Springboot的自动配置
# The default value is `true`.
# Thus you will have Metrics configured if `axon-micrometer` and
# appropriate metric implementation (for example: `micrometer-registry-
# prometheus`) are on your classpath.
axon.metrics.auto-configuration.enabled=true
# The default value is `false`.
# By enabling this property you will have message (event, command, query)
# payload type set as a micrometer tag/dimension by default.
# Additionally, the processor name will be a tag/dimension instead of it being part # of the metric name.
axon.metrics.micrometer.dimensional=true
# Spring Boot metrics enabled
management.endpoint.metrics.enabled=true
# Spring Boot (Prometheus) endpoint (`/actuator/prometheus`) enabled and
# exposed
management.metrics.export.prometheus.enabled=true
management.endpoint.prometheus.enabled=true
在这种情况下,可能需要对定义的MessageMonitor实例进行更细粒度的控制。如果您希望在任何消息处理组件上有更具体的度量,则以下代码段提供了示例:
// Java (Spring Boot Configuration) - Micrometer example
@Configuration
public class MetricsConfig {
@Bean
public ConfigurerModule metricConfigurer(MeterRegistry meterRegistry) {
return configurer -> {
instrumentEventStore(meterRegistry, configurer);
instrumentEventProcessors(meterRegistry, configurer);
instrumentCommandBus(meterRegistry, configurer);
instrumentQueryBus(meterRegistry, configurer);
};
}
private void instrumentEventStore(MeterRegistry meterRegistry, Configurer configurer) {
MessageMonitorFactory messageMonitorFactory = (configuration, componentType, componentName) -> {
MessageCountingMonitor messageCounter = MessageCountingMonitor.buildMonitor(
componentName, meterRegistry,
message -> Tags.of(TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName())
.and(message.getMetaData().entrySet().stream()
.map(s -> Tag.of(s.getKey(), s.getValue().toString()))
.collect(Collectors.toList()))
);
// Naming the Timer monitor/meter with the name of the component
// Registering the Timer with custom tags: payloadType.
MessageTimerMonitor messageTimer = MessageTimerMonitor.buildMonitor(
componentName, meterRegistry,
message -> Tags.of(TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName())
);
return new MultiMessageMonitor<>(messageCounter, messageTimer);
};
configurer.configureMessageMonitor(EventStore.class, messageMonitorFactory);
}
private void instrumentEventProcessors(MeterRegistry meterRegistry, Configurer configurer) {
MessageMonitorFactory messageMonitorFactory = (configuration, componentType, componentName) -> {
// Naming the Counter monitor/meter with the fixed name `eventProcessor`.
// Registering the Counter with custom tags: payloadType and processorName.
MessageCountingMonitor messageCounter = MessageCountingMonitor.buildMonitor(
"eventProcessor", meterRegistry,
message -> Tags.of(
TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName(),
TagsUtil.PROCESSOR_NAME_TAG, componentName ));
// Naming the Timer monitor/meter with the fixed name `eventProcessor`.
// Registering the Timer with custom tags: payloadType and processorName.
MessageTimerMonitor messageTimer = MessageTimerMonitor.buildMonitor("eventProcessor", meterRegistry,message
-> Tags.of(
TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName(),
TagsUtil.PROCESSOR_NAME_TAG, componentName));
// Naming the Capacity/Gauge monitor/meter with the fixed name //`eventProcessor`.
// Registering the Capacity/Gauge with custom tags: payloadType and //processorName.
CapacityMonitor capacityMonitor1Minute = CapacityMonitor.buildMonitor(
"eventProcessor", meterRegistry,
message -> Tags.of(
TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName(),
TagsUtil.PROCESSOR_NAME_TAG, componentName
)
);
return new MultiMessageMonitor<>(messageCounter, messageTimer, capacityMonitor1Minute);
};
configurer.configureMessageMonitor(TrackingEventProcessor.class, messageMonitorFactory);
}
private void instrumentCommandBus(MeterRegistry meterRegistry, Configurer configurer) {
MessageMonitorFactory messageMonitorFactory = (configuration, componentType, componentName) -> {
MessageCountingMonitor messageCounter = MessageCountingMonitor.buildMonitor(
componentName, meterRegistry,
message -> Tags.of(
TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName(),
"messageId", message.getIdentifier()
)
);
MessageTimerMonitor messageTimer = MessageTimerMonitor.buildMonitor(
componentName, meterRegistry,
message -> Tags.of(TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName())
);
CapacityMonitor capacityMonitor1Minute = CapacityMonitor.buildMonitor(
componentName, meterRegistry,
message -> Tags.of(TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName())
);
return new MultiMessageMonitor<>(messageCounter, messageTimer, capacityMonitor1Minute);
};
configurer.configureMessageMonitor(CommandBus.class, messageMonitorFactory);
}
private void instrumentQueryBus(MeterRegistry meterRegistry, Configurer configurer) {
MessageMonitorFactory messageMonitorFactory = (configuration, componentType, componentName) -> {
MessageCountingMonitor messageCounter = MessageCountingMonitor.buildMonitor(
componentName, meterRegistry,
message -> Tags.of(
TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName(),
"messageId", message.getIdentifier()
)
);
MessageTimerMonitor messageTimer = MessageTimerMonitor.buildMonitor(
componentName, meterRegistry,
message -> Tags.of(TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName())
);
CapacityMonitor capacityMonitor1Minute = CapacityMonitor.buildMonitor(
componentName, meterRegistry,
message -> Tags.of(TagsUtil.PAYLOAD_TYPE_TAG, message.getPayloadType().getSimpleName())
);
return new MultiMessageMonitor<>(messageCounter, messageTimer, capacityMonitor1Minute);
};
configurer.configureMessageMonitor(QueryBus.class, messageMonitorFactory);
}
}