fix: harden jt808 tdengine ingestion
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -36,16 +36,44 @@ public class RateLimitInterceptor implements IngestInterceptor, Ordered {
|
||||
|
||||
@Override
|
||||
public boolean before(RawFrame frame, IngestContext ctx) {
|
||||
String vin = frame.sourceMeta().getOrDefault("vin", "unknown");
|
||||
// VIN 粒度隔离,避免某一辆车的高频/异常上报挤占其他车辆处理额度。
|
||||
RateLimiter rl = limiters.get(vin, k -> RateLimiter.of("vin-" + k, defaultConfig));
|
||||
String limiterKey = limiterKey(frame);
|
||||
// 车辆粒度隔离:VIN 可用时按 VIN;JT808 等未映射 VIN 的协议按 vehicleKey/phone 兜底,
|
||||
// 避免所有 unknown 终端挤在同一个限流桶里。
|
||||
RateLimiter rl = limiters.get(limiterKey, k -> RateLimiter.of("vehicle-" + k, defaultConfig));
|
||||
if (!rl.acquirePermission()) {
|
||||
ctx.abort("rate-limited:" + vin);
|
||||
ctx.abort("rate-limited:" + limiterKey);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String limiterKey(RawFrame frame) {
|
||||
String vin = meta(frame, "vin");
|
||||
if (isKnown(vin)) {
|
||||
return "vin:" + vin;
|
||||
}
|
||||
String vehicleKey = meta(frame, "vehicleKey");
|
||||
if (isKnown(vehicleKey)) {
|
||||
return "vehicleKey:" + vehicleKey;
|
||||
}
|
||||
String phone = meta(frame, "phone");
|
||||
if (isKnown(phone)) {
|
||||
return "phone:" + phone;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private static String meta(RawFrame frame, String key) {
|
||||
if (frame == null || frame.sourceMeta() == null) {
|
||||
return "";
|
||||
}
|
||||
return frame.sourceMeta().getOrDefault(key, "").trim();
|
||||
}
|
||||
|
||||
private static boolean isKnown(String value) {
|
||||
return value != null && !value.isBlank() && !"unknown".equalsIgnoreCase(value.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 200;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.lingniu.ingest.core.pipeline.builtin;
|
||||
|
||||
import com.lingniu.ingest.api.ProtocolId;
|
||||
import com.lingniu.ingest.api.pipeline.IngestContext;
|
||||
import com.lingniu.ingest.api.pipeline.RawFrame;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class RateLimitInterceptorTest {
|
||||
|
||||
@Test
|
||||
void unknownJt808VinUsesPhoneAsIndependentLimiterKey() {
|
||||
RateLimitInterceptor interceptor = new RateLimitInterceptor(1, 100);
|
||||
IngestContext first = new IngestContext("trace-1");
|
||||
IngestContext second = new IngestContext("trace-2");
|
||||
|
||||
boolean firstAllowed = interceptor.before(frame("unknown", "13079970001"), first);
|
||||
boolean secondAllowed = interceptor.before(frame("unknown", "13079970002"), second);
|
||||
|
||||
assertThat(firstAllowed).isTrue();
|
||||
assertThat(secondAllowed).isTrue();
|
||||
assertThat(first.aborted()).isFalse();
|
||||
assertThat(second.aborted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameJt808PhoneStillSharesLimiterWhenVinIsUnknown() {
|
||||
RateLimitInterceptor interceptor = new RateLimitInterceptor(1, 100);
|
||||
IngestContext first = new IngestContext("trace-1");
|
||||
IngestContext second = new IngestContext("trace-2");
|
||||
|
||||
boolean firstAllowed = interceptor.before(frame("unknown", "13079970001"), first);
|
||||
boolean secondAllowed = interceptor.before(frame("unknown", "13079970001"), second);
|
||||
|
||||
assertThat(firstAllowed).isTrue();
|
||||
assertThat(secondAllowed).isFalse();
|
||||
assertThat(second.aborted()).isTrue();
|
||||
assertThat(second.abortReason()).isEqualTo("rate-limited:phone:13079970001");
|
||||
}
|
||||
|
||||
private static RawFrame frame(String vin, String phone) {
|
||||
return new RawFrame(
|
||||
ProtocolId.JT808,
|
||||
0x0200,
|
||||
0,
|
||||
new Object(),
|
||||
new byte[]{0x01},
|
||||
Map.of("vin", vin, "phone", phone),
|
||||
Instant.parse("2026-06-29T00:00:00Z"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user