使用者可以从存储特定键值数据的分区中读取记录吗?

时间:2019-02-26 04:59:26

标签: python apache-kafka kafka-consumer-api kafka-python

不是创建许多主题,而是为每个使用者创建一个分区,并使用密钥存储数据。因此,有一种方法可以使使用者组中的使用者从存储特定键数据的分区中读取。如果是这样,您可以建议使用kafka-python(或任何其他库)如何完成。

2 个答案:

答案 0 :(得分:0)

您可以使用“分配”逻辑(例如,由Kafka消费者Java客户端提供)来代替使​​用预订和相关的使用者组逻辑。 订阅主题并成为使用者组的一部分时,分区会自动分配给使用者,并在新的使用者加入或离开时重新分配分区,这与使用Assign不同。 使用assign,消费者要求将其分配到特定分区。它不属于任何消费群体。这也意味着如果使用者死亡,则由您负责重新平衡:例如,如果使用者1被分配了分区1,但在某个时刻崩溃,则分区1不会自动重新分配给另一个使用者。由您来编写和处理用于重新启动使用者(或另一个使用者)以从分区1获取消息的逻辑。

答案 1 :(得分:0)

我相信,从长远来看,您尝试实现的目标不是最佳实践。

据我了解,您需要根据消息的密钥确定使用者将连接到的分区。

我想发布者正在使用“默认分区程序”。

从技术上讲,您可以通过在使用者中重用使用者中使用的相同算法来确定主题分区。这是DefaultPartitioner的Java代码。您可以在Python中对其进行修改。

public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
    List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
    int numPartitions = partitions.size();
    if (keyBytes == null) {
        int nextValue = nextValue(topic);
        List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
        if (availablePartitions.size() > 0) {
            int part = Utils.toPositive(nextValue) % availablePartitions.size();
            return availablePartitions.get(part).partition();
        } else {
            // no partitions are available, give a non-available partition
            return Utils.toPositive(nextValue) % numPartitions;
        }
    } else {
        // hash the keyBytes to choose a partition
        return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
    }
}

private int nextValue(String topic) {
    AtomicInteger counter = topicCounterMap.get(topic);
    if (null == counter) {
        counter = new AtomicInteger(ThreadLocalRandom.current().nextInt());
        AtomicInteger currentCounter = topicCounterMap.putIfAbsent(topic, counter);
        if (currentCounter != null) {
            counter = currentCounter;
        }
    }
    return counter.getAndIncrement();
}

设置密钥后,用例中的重要部分是:

Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;

还有Utils.murmur2方法:

public static int murmur2(final byte[] data) {
    int length = data.length;
    int seed = 0x9747b28c;
    // 'm' and 'r' are mixing constants generated offline.
    // They're not really 'magic', they just happen to work well.
    final int m = 0x5bd1e995;
    final int r = 24;

    // Initialize the hash to a random value
    int h = seed ^ length;
    int length4 = length / 4;

    for (int i = 0; i < length4; i++) {
        final int i4 = i * 4;
        int k = (data[i4 + 0] & 0xff) + ((data[i4 + 1] & 0xff) << 8) + ((data[i4 + 2] & 0xff) << 16) + ((data[i4 + 3] & 0xff) << 24);
        k *= m;
        k ^= k >>> r;
        k *= m;
        h *= m;
        h ^= k;
    }

    // Handle the last few bytes of the input array
    switch (length % 4) {
        case 3:
            h ^= (data[(length & ~3) + 2] & 0xff) << 16;
        case 2:
            h ^= (data[(length & ~3) + 1] & 0xff) << 8;
        case 1:
            h ^= data[length & ~3] & 0xff;
            h *= m;
    }

    h ^= h >>> 13;
    h *= m;
    h ^= h >>> 15;

    return h;
}

为什么我认为这不是最佳解决方案?

如果您向主题添加新分区,则DefaultPartitioner将为您提供一个partition id,它可能与添加新分区之前返回的partition id不同。并且默认情况下,现有邮件不会重新分区,这意味着您将在不同分区上拥有具有相同密钥的邮件。

在消费者方面也会发生相同的行为。更新分区数量后,使用者将尝试使用来自其他分区的消息。您将错过此密钥使用的前一个分区中的消息。