我有多个使用StreamListener注释的方法,它们使用了不同的主题。但是,其中一些主题需要从“最早的”偏移量中读取,以填充内存中的映射(类似于状态机),然后从其他主题中使用,这些主题中可能包含应针对“最新”执行的命令状态机。
当前代码如下:
@Component
@AllArgsConstructor
@EnableBinding({InputChannel.class, OutputChannel.class})
@Slf4j
public class KafkaListener {
@StreamListener(target = InputChannel.EVENTS)
public void event(Event event) {
// do something with the event
}
@StreamListener(target = InputChannel.COMMANDS)
public void command(Command command) {
// do something with the command only after all events have been processed
}
}
我试图添加一些可怕的代码,这些代码从传入的事件消息中获取kafka主题偏移量元数据,然后使用信号量阻止命令,直到事件达到总偏移量的某个百分比为止。这有点奏效,但让我难过,一旦我们有20个左右相互依赖的主题,就很难维护。
SpringBoot / Spring Streams是否具有任何内置机制来执行此操作,还是有人使用了我不知道的一些常见模式?
TL; DR :如何在使用主题B的消息之前处理主题A的所有消息,而又不做任何肮脏的事情,例如在主题B的使用者中粘贴Thread.sleep(60000)?
答案 0 :(得分:1)
请参见kafka consumer binding property resetOffsets
重置偏移量
是否将使用者的偏移量重置为
startOffset
提供的值。如果提供了KafkaRebalanceListener
,则必须为false;否则为false。请参阅使用KafkaRebalanceListener。默认值:false。
startOffset
新组的起始偏移量。允许的值:
earliest
和latest
。如果为消费者“绑定”显式设置了消费者组(通过spring.cloud.stream.bindings..group),则“ startOffset”最早设置。否则,对于匿名使用者组,它设置为最新。另请参阅resetOffsets
(在此列表的前面)。默认值:null(相当于最早的)。
您还可以添加KafkaBindingRebalanceListener并在使用者上执行搜索。
编辑
您还可以在第二个侦听器上将autoStartup
设置为false,并在准备就绪时开始绑定。这是一个示例:
@SpringBootApplication
@EnableBinding(Sink.class)
public class Gitter55Application {
public static void main(String[] args) {
SpringApplication.run(Gitter55Application.class, args);
}
@Bean
public ConsumerEndpointCustomizer<KafkaMessageDrivenChannelAdapter<?, ?>> customizer() {
return (endpoint, dest, group) -> {
endpoint.setOnPartitionsAssignedSeekCallback((assignments, callback) -> {
assignments.keySet().forEach(tp -> callback.seekToBeginning(tp.topic(), tp.partition()));
});
};
}
@StreamListener(Sink.INPUT)
public void listen(String value, @Header(KafkaHeaders.RECEIVED_MESSAGE_KEY) byte[] key) {
System.out.println(new String(key) + ":" + value);
}
@Bean
public ApplicationRunner runner(KafkaTemplate<byte[], byte[]> template,
BindingsEndpoint bindings) {
return args -> {
while (true) {
template.send("gitter55", "foo".getBytes(), "bar".getBytes());
System.out.println("Hit enter to start");
System.in.read();
bindings.changeState("input", State.STARTED);
}
};
}
}
spring.cloud.stream.bindings.input.group=gitter55
spring.cloud.stream.bindings.input.destination=gitter55
spring.cloud.stream.bindings.input.content-type=text/plain
spring.cloud.stream.bindings.input.consumer.auto-startup=false