feat: ack gb32960 after kafka boundary
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
package com.lingniu.ingest.protocol.gb32960.inbound;
|
||||
|
||||
import com.lingniu.ingest.api.event.VehicleEvent;
|
||||
import com.lingniu.ingest.api.sink.EventSink;
|
||||
import com.lingniu.ingest.core.concurrency.AsyncBatchExecutor;
|
||||
import com.lingniu.ingest.core.concurrency.DisruptorEventBus;
|
||||
import com.lingniu.ingest.core.dispatcher.Dispatcher;
|
||||
import com.lingniu.ingest.core.dispatcher.HandlerInvoker;
|
||||
import com.lingniu.ingest.core.dispatcher.HandlerRegistry;
|
||||
import com.lingniu.ingest.core.pipeline.InterceptorChain;
|
||||
import com.lingniu.ingest.protocol.gb32960.auth.Gb32960PlatformAuthorizer;
|
||||
import com.lingniu.ingest.protocol.gb32960.auth.Gb32960VinAuthorizer;
|
||||
import com.lingniu.ingest.protocol.gb32960.codec.Gb32960BodyParser;
|
||||
import com.lingniu.ingest.protocol.gb32960.codec.Gb32960DecoderTest;
|
||||
import com.lingniu.ingest.protocol.gb32960.codec.Gb32960MessageDecoder;
|
||||
import com.lingniu.ingest.protocol.gb32960.codec.InfoBlockParserRegistry;
|
||||
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.PositionV2016BlockParser;
|
||||
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.VehicleV2016BlockParser;
|
||||
import com.lingniu.ingest.protocol.gb32960.config.Gb32960Properties;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class Gb32960ChannelHandlerAckBoundaryTest {
|
||||
|
||||
@Test
|
||||
void realtimeReportAckWaitsForDispatchFutureCompletion() {
|
||||
try (DispatchHarness dispatch = new DispatchHarness()) {
|
||||
EmbeddedChannel channel = new EmbeddedChannel(handlerWith(dispatch.dispatcher()));
|
||||
|
||||
assertThat(channel.writeInbound(Gb32960DecoderTest.buildRealtimeFrame("LTEST000000000001"))).isFalse();
|
||||
assertThat((Object) channel.readOutbound()).isNull();
|
||||
|
||||
dispatch.completeDurability();
|
||||
channel.runPendingTasks();
|
||||
|
||||
ByteBuf ack = channel.readOutbound();
|
||||
assertThat(ack).isNotNull();
|
||||
assertThat(ack.getUnsignedByte(2)).isEqualTo((short) 0x02);
|
||||
assertThat(ack.getUnsignedByte(3)).isEqualTo((short) 0x01);
|
||||
ack.release();
|
||||
channel.finishAndReleaseAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void realtimeReportAckIsNotWrittenWhenDispatchFutureFails() {
|
||||
try (DispatchHarness dispatch = new DispatchHarness()) {
|
||||
EmbeddedChannel channel = new EmbeddedChannel(handlerWith(dispatch.dispatcher()));
|
||||
|
||||
assertThat(channel.writeInbound(Gb32960DecoderTest.buildRealtimeFrame("LTEST000000000001"))).isFalse();
|
||||
|
||||
dispatch.failDurability();
|
||||
channel.runPendingTasks();
|
||||
|
||||
assertThat((Object) channel.readOutbound()).isNull();
|
||||
assertThat(channel.isActive()).isFalse();
|
||||
channel.finishAndReleaseAll();
|
||||
}
|
||||
}
|
||||
|
||||
private static Gb32960ChannelHandler handlerWith(Dispatcher dispatcher) {
|
||||
return new Gb32960ChannelHandler(
|
||||
decoder(),
|
||||
dispatcher,
|
||||
accessService(),
|
||||
new Gb32960AckService(),
|
||||
new Gb32960FrameDiagnostics(16));
|
||||
}
|
||||
|
||||
private static Gb32960MessageDecoder decoder() {
|
||||
return new Gb32960MessageDecoder(new Gb32960BodyParser(new InfoBlockParserRegistry(List.of(
|
||||
new VehicleV2016BlockParser(),
|
||||
new PositionV2016BlockParser()))));
|
||||
}
|
||||
|
||||
private static Gb32960AccessService accessService() {
|
||||
Gb32960Properties.Auth auth = new Gb32960Properties.Auth();
|
||||
auth.setEnabled(false);
|
||||
return new Gb32960AccessService(
|
||||
new Gb32960VinAuthorizer(auth),
|
||||
new Gb32960PlatformAuthorizer(auth.getPlatforms()));
|
||||
}
|
||||
|
||||
private static final class DispatchHarness implements AutoCloseable {
|
||||
private final ControlledSink sink = new ControlledSink();
|
||||
private final DisruptorEventBus eventBus = new DisruptorEventBus(1024, "blocking", List.of(sink));
|
||||
private final AsyncBatchExecutor batchExecutor = new AsyncBatchExecutor(eventBus::publish);
|
||||
private final Dispatcher dispatcher = new Dispatcher(
|
||||
new HandlerRegistry(),
|
||||
new InterceptorChain(List.of()),
|
||||
new HandlerInvoker(),
|
||||
eventBus,
|
||||
batchExecutor);
|
||||
|
||||
Dispatcher dispatcher() {
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
void completeDurability() {
|
||||
sink.future.complete(null);
|
||||
}
|
||||
|
||||
void failDurability() {
|
||||
sink.future.completeExceptionally(new IllegalStateException("durability failed"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
batchExecutor.close();
|
||||
eventBus.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ControlledSink implements EventSink {
|
||||
private final CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "controlled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> publish(VehicleEvent event) {
|
||||
return future;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user