feat: make gb32960 archive history query production ready

This commit is contained in:
kkfluous
2026-06-23 11:55:44 +08:00
parent b14871ff1c
commit ba68ffe061
462 changed files with 23639 additions and 2341 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lingniu.ingest</groupId>
<artifactId>lingniu-vehicle-ingest</artifactId>
<version>0.1.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>observability</artifactId>
<name>observability</name>
<description>Micrometer + Prometheus + 业务指标封装事件计数、Dispatcher 耗时、DLQ 计数等)。</description>
<dependencies>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>ingest-api</artifactId>
</dependency>
<dependency>
<groupId>com.lingniu.ingest</groupId>
<artifactId>ingest-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,84 @@
package com.lingniu.ingest.observability;
import com.lingniu.ingest.api.ProtocolId;
import com.lingniu.ingest.api.event.VehicleEvent;
import com.lingniu.ingest.api.pipeline.IngestContext;
import com.lingniu.ingest.api.pipeline.IngestInterceptor;
import com.lingniu.ingest.api.pipeline.RawFrame;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;
import org.springframework.core.Ordered;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 指标拦截器:自动以 {@link IngestInterceptor} 形式接入拦截链,无侵入采集核心流量指标。
*
* <p>暴露指标:
* <ul>
* <li>{@code ingest_frames_total{protocol}} - 入站帧计数
* <li>{@code ingest_events_total{protocol,type}} - 事件产出计数
* <li>{@code ingest_errors_total{protocol}} - 异常计数
* <li>{@code ingest_frame_duration_seconds{protocol}} - 单帧处理耗时
* </ul>
*/
public class IngestMetrics implements IngestInterceptor, Ordered {
private static final String ATTR_TIMER_SAMPLE = "metrics.timer.sample";
private final MeterRegistry registry;
private final ConcurrentMap<ProtocolId, Counter> frameCounters = new ConcurrentHashMap<>();
private final ConcurrentMap<ProtocolId, Counter> errorCounters = new ConcurrentHashMap<>();
private final ConcurrentMap<ProtocolId, Timer> frameTimers = new ConcurrentHashMap<>();
private final ConcurrentMap<String, Counter> eventCounters = new ConcurrentHashMap<>();
public IngestMetrics(MeterRegistry registry) {
this.registry = registry;
}
@Override
public boolean before(RawFrame frame, IngestContext ctx) {
ProtocolId p = frame.protocolId();
frameCounters.computeIfAbsent(p, k ->
Counter.builder("ingest_frames_total").tag("protocol", k.name()).register(registry)).increment();
Timer.Sample sample = Timer.start(registry);
ctx.attr(ATTR_TIMER_SAMPLE, sample);
return true;
}
@Override
public void after(VehicleEvent event, IngestContext ctx) {
String key = event.source().name() + ":" + event.getClass().getSimpleName();
eventCounters.computeIfAbsent(key, k -> {
String[] parts = k.split(":");
return Counter.builder("ingest_events_total")
.tags(Tags.of("protocol", parts[0], "type", parts[1]))
.register(registry);
}).increment();
Timer.Sample s = ctx.attr(ATTR_TIMER_SAMPLE);
if (s != null) {
Timer t = frameTimers.computeIfAbsent(event.source(), p ->
Timer.builder("ingest_frame_duration_seconds").tag("protocol", p.name()).register(registry));
s.stop(t);
ctx.attr(ATTR_TIMER_SAMPLE, null);
}
}
@Override
public void onError(Throwable error, IngestContext ctx) {
// 无协议上下文时归到 UNKNOWN
Counter c = errorCounters.computeIfAbsent(
ProtocolId.GB32960, // 默认桶:由上层拦截器设置具体协议更精确
p -> Counter.builder("ingest_errors_total").tag("protocol", "unknown").register(registry));
c.increment();
}
@Override
public int getOrder() {
return 10; // 尽早介入,晚于 Tracingorder=0
}
}

View File

@@ -0,0 +1,18 @@
package com.lingniu.ingest.observability;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
@ConditionalOnBean(MeterRegistry.class)
public class ObservabilityAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public IngestMetrics ingestMetrics(MeterRegistry registry) {
return new IngestMetrics(registry);
}
}

View File

@@ -0,0 +1 @@
com.lingniu.ingest.observability.ObservabilityAutoConfiguration