chore: initial import of lingniu-vehicle-ingest
Multi-module Spring Boot ingest service for vehicle telemetry. Modules: - ingest-api / ingest-core / ingest-codec-common: shared SPI, dispatcher, Disruptor event bus, BCC/BCD codec helpers - protocol-gb32960: GB/T 32960.3 inbound (Netty + per-version parser packages v2016/v2025), platform login auth, VIN whitelist, idle handler - protocol-jt808 / protocol-jt1078 / protocol-jsatl12: JT/T inbound - inbound-mqtt / inbound-xinda-push: alternative ingest channels - session-core: per-channel session state - sink-archive / sink-mq: persistence sinks (local file / Kafka) - command-gateway: terminal control command gateway - bootstrap-all: aggregator Spring Boot app - observability: Micrometer / Actuator wiring Includes hex-dump golden samples under protocol-gb32960/src/test/resources and the GB/T 32960.3-2016 / 2025 reference PDFs under reference/. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
31
observability/pom.xml
Normal file
31
observability/pom.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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>
|
||||
</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>
|
||||
@@ -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; // 尽早介入,晚于 Tracing(order=0)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
com.lingniu.ingest.observability.ObservabilityAutoConfiguration
|
||||
Reference in New Issue
Block a user