Samza:延迟处理消息直到时间戳

时间:2018-01-17 06:52:28

标签: apache-kafka message-queue apache-samza

我正在使用Samza处理来自Kafka主题的消息。有些消息将来带有时间戳,我想将处理推迟到该时间戳之后。与此同时,我想继续处理其他收到的消息。

我尝试做的是让我的Task队列消息并实现WindowableTask以定期检查消息,如果他们的时间戳允许处理它们。基本思想如下:

public class MyTask implements StreamTask, WindowableTask {

    private HashSet<MyMessage> waitingMessages = new HashSet<>();

    @Override
    public void process(IncomingMessageEnvelope incomingMessageEnvelope, MessageCollector messageCollector, TaskCoordinator taskCoordinator) {
        byte[] message = (byte[]) incomingMessageEnvelope.getMessage();
        MyMessage parsedMessage = MyMessage.parseFrom(message);

        if (parsedMessage.getValidFromDateTime().isBeforeNow()) {
            // Do the processing
        } else {
            waitingMessages.add(parsedMessage);
        }

    }

    @Override
    public void window(MessageCollector messageCollector, TaskCoordinator taskCoordinator) {
        for (MyMessage message : waitingMessages) {
            if (message.getValidFromDateTime().isBeforeNow()) {
                // Do the processing and remove the message from the set
            }
        }
    }
}

这显然有一些缺点。当我重新部署任务时,我将在内存中丢失等待的消息。所以我想知道推迟使用Samza处理消息的最佳实践。我是否需要一次又一次地将消息重新发送到同一主题,直到我最终可以处理它们为止?我们正在谈论将处理延迟几分钟至1-2小时。

2 个答案:

答案 0 :(得分:0)

在处理消息队列时,请记住,重要的是它们在系统中执行非常特定的功能:它们在处理器忙于处理先前消息时保留消息。预计正常运行的消息队列将按需提供消息。这意味着,只要消息到达队列的头部,队列的下一次拉动就会产生消息。

请注意,延迟不是等式的可配置部分。相反,delay是具有队列的系统的输出变量。事实上,Little's Law提供了一些有趣的见解。

因此,在需要延迟的系统中(例如,加入/等待并行操作完成),您应该查看其他方法。通常,可查询数据库在此特定实例中是有意义的。如果您发现自己在预设的时间段内将消息保留在队列中,那么您实际上将消息队列用作数据库 - 这是一个它未设计的功能。这不仅风险很大,而且很可能会损害您的消息代理的性能。

答案 1 :(得分:0)

我认为您可以使用Samza的键值存储来保持任务实例的状态而不是内存中Set。 它应该看起来像:

public class MyTask implements StreamTask, WindowableTask, InitableTask {

  private KeyValueStore<String, MyMessage> waitingMessages;


  @SuppressWarnings("unchecked")
  @Override
  public void init(Config config, TaskContext context) throws Exception {
    this.waitingMessages = (KeyValueStore<String, MyMessage>) context.getStore("messages-store");
  }

  @Override
  public void process(IncomingMessageEnvelope incomingMessageEnvelope, MessageCollector messageCollector,
      TaskCoordinator taskCoordinator) {
    byte[] message = (byte[]) incomingMessageEnvelope.getMessage();
    MyMessage parsedMessage = MyMessage.parseFrom(message);

    if (parsedMessage.getValidFromDateTime().isBefore(LocalDate.now())) {
      // Do the processing
    } else {
      waitingMessages.put(parsedMessage.getId(), parsedMessage);
    }

  }

  @Override
  public void window(MessageCollector messageCollector, TaskCoordinator taskCoordinator) {
    KeyValueIterator<String, MyMessage> all = waitingMessages.all();
    while(all.hasNext()) {
      MyMessage message = all.next().getValue();
      // Do the processing and remove the message from the set
    }
  }

}

如果您重新部署任务,Samza应重新创建键值存储的状态(Samza将值保存在与键值存储相关的特殊kafka主题中)。您当然需要提供商店的一些额外配置(在上面的示例中为messages-store)。

您可以在此处阅读有关键值存储的信息(最新的Samza版本): https://samza.apache.org/learn/documentation/0.14/container/state-management.html

相关问题