web-dev-qa-db-ja.com

Kafka作成時間に応じて正確なオフセットを取得する方法

Kafka 1日1時間ごとに生成されるメッセージを取得する必要があります。1時間ごとに、1時間前に生成されたメッセージを消費するジョブを起動します。たとえば、現在の時刻が20の場合: 12、19:00:00から19:59:59の間でメッセージを消費します。つまり、19:00:00までに開始オフセットを取得し、19:59:59までに終了オフセットを取得する必要があります。SimpleConsumerを使用しました。 「 .8.0 SimpleConsumerの例 」に示すように、getOffsetsBefore。問題は、返されるオフセットがパラメーターとして指定されたタイムスタンプと一致しないことです。たとえば、タイムスタンプを19:00:00にすると、メッセージが生成されます時間16:38:00。

14
Po Zhou

Kafkaでは、現在、特定のタイムスタンプに対応するオフセットを取得する方法はありません-これは設計によるものです。 Jay Krepsのログの上部に記載されているようにArticle 、オフセット番号は、実時間から切り離されたログの一種のタイムスタンプを提供します。時間の概念としてオフセットを使用すると、2つのシステムが一貫した状態は、購入したオフセットを知って購入するだけです。異なるサーバーの異なるクロック時間、うるう年、夏時間、タイムゾーンなどについて混乱することはありません。

NOW ...以上のことをすべて説明しましたが、ある時点でサーバーがダウンしたことがわかっている場合は、実際には、対応するオフセットを知りたいと考えています。近づくことができます。 kafkaマシン上のログファイルは、書き込みを開始した時間に応じて名前が付けられ、kafkaツールが存在しますこれらのファイルに関連付けられているオフセットを知ることができますが、正確なタイムスタンプを知りたい場合は、Kafkaに送信するメッセージのタイムスタンプをエンコードする必要があります。

5
JnBrymn

以下kafkaコンシューマAPIメソッドgetOffsetsByTimes()はこれに使用できます。0.10.0以降のバージョンから利用できます。 JavaDoc を参照してください。

/**
 * Look up the offsets for the given partitions by timestamp. The returned offset for each partition is the
 * earliest offset whose timestamp is greater than or equal to the given timestamp in the corresponding partition.
 *
 * This is a blocking call. The consumer does not have to be assigned the partitions.
 * If the message format version in a partition is before 0.10.0, i.e. the messages do not have timestamps, null
 * will be returned for that partition.
 *
 * Notice that this method may block indefinitely if the partition does not exist.
 *
 * @param timestampsToSearch the mapping from partition to the timestamp to look up.
 * @return a mapping from partition to the timestamp and offset of the first message with timestamp greater
 *         than or equal to the target timestamp. {@code null} will be returned for the partition if there is no
 *         such message.
 * @throws IllegalArgumentException if the target timestamp is negative.
 */
@Override
public Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes(Map<TopicPartition, Long> timestampsToSearch) {
    for (Map.Entry<TopicPartition, Long> entry : timestampsToSearch.entrySet()) {
        // we explicitly exclude the earliest and latest offset here so the timestamp in the returned
        // OffsetAndTimestamp is always positive.
        if (entry.getValue() < 0)
            throw new IllegalArgumentException("The target time for partition " + entry.getKey() + " is " +
                    entry.getValue() + ". The target time cannot be negative.");
    }
    return fetcher.getOffsetsByTimes(timestampsToSearch, requestTimeoutMs);
}
13
Liju John

他の返信が注記しているように、Kafkaの以前のバージョンは、時間をオフセットにマッピングするおおよその方法しかありませんでした。しかし、Kafka 0.10.0(5月にリリースされたため、 2016)、Kafkaは各トピックの時間インデックスを維持します。これにより、時間から正確なオフセットまで効率的に取得できます。 KafkaConsumer#offsetsForTimesメソッド を使用できますこの情報にアクセスします。

時間ベースのインデックスがどのように実装されるかについての詳細は KIP-33設計に関する議論 ページにあります。

6
cmccabe

あなたにコードを見せてください:

public static Map<TopicPartition, OffsetAndTimestamp> getOffsetAndTimestampAtTime(String kafkaServer, String topic, long time) {
    Map<String, Object> kafkaParams = new HashMap<>();
    kafkaParams.put(BOOTSTRAP_SERVERS_CONFIG, kafkaServers);
    kafkaParams.put(GROUP_ID_CONFIG, "consumerGroupId");
    kafkaParams.put(KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    kafkaParams.put(VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    kafkaParams.put(AUTO_OFFSET_RESET_CONFIG, "latest");
    kafkaParams.put(ENABLE_AUTO_COMMIT_CONFIG, false);
    KafkaConsumer<String, String> consumer = new KafkaConsumer<>(kafkaParams);

    List<PartitionInfo> partitionInfos = consumer.partitionsFor(topic);

    List<TopicPartition> topicPartitions = partitionInfos
            .stream()
            .map(pi -> new TopicPartition(pi.topic(), pi.partition()))
            .collect(Collectors.toList());

    Map<TopicPartition, Long> topicPartitionToTimestampMap = topicPartitions.stream()
            .collect(Collectors.toMap(tp -> tp, tp -> time));

    Map<TopicPartition, OffsetAndTimestamp> result = consumer.offsetsForTimes(topicPartitionToTimestampMap);
    consumer.close();
    return result;
}
1
diguage

Kafka 1.10はタイムスタンプをサポートしていますが、それを使用して目的の操作を行うのはまだ少し難しいでしょう。しかし、どのタイムスタンプから読み取りたいかがわかっていて、読み取りたいまでは、その時間までメッセージをポーリングするだけで、消費を停止できます。

0
Gerard