Kafka Streams:按键进行部分重新处理

时间:2018-02-15 12:14:08

标签: apache-kafka event-sourcing apache-kafka-streams data-partitioning

情景

在KafkaStreams 网络会话场景中, 无限(或多年)保留, 使用互动查询(必要时可以查看), 许多客户每个 <每个客户特定的每个用户, 分区的地方如下:

按(clientId,userId)%numberOfPartitions 的功能进行分区,根据群集大小预先设置numberOfPartitions。这将允许在(clientId,userId)数据上执行会话,并且应该在节点之间提供均匀的数据分布(没有热点,分区大小或写入负载)。

但是,当查询时,我会按客户端(和时间范围)进行查询。那么,我将从Sessions表中构建一个聚合的Ktable,其中key是客户端,Sessions被查询(client,timeStart,timeEnd)。这会使来自客户端的数据必须进入一个节点,这可能会造成可伸缩性问题(客户端太大),但由于数据已经聚合,我想这是可以管理的。

问题:

在这种情况下(感谢变体),我希望能够仅为一个客户端重新处理

但是来自一个客户端的数据将分散在(可能是所有)分区中。

如何在Kafka Streams中实现部分重新处理并产生轻微影响,并在此期间保持(旧)状态可查询?

1 个答案:

答案 0 :(得分:2)

我认为一般情况下你已经知道了你的问题的答案,如你所描述的分区方案,如果要重新处理客户端,你必须阅读所有分区,因为消息将遍及所有分区。

在重新处理整个客户端时,我唯一能够限制开销量的方法是实现一个分区方案,该方案为客户端分组几个分区,然后在这些分区上分配用户以避免一个分区过载特别是&#34;大&#34;客户。图片应该有希望澄清我可能无法用文字解释的内容。

Grouping partitions per client

实现此分发的自定义分区程序看起来有点像下面的代码。请带上一粒盐,到目前为止这纯粹是理论上的,从未经过测试(甚至没有经过测试) - 但它应该说明原理。

public class ClientUserPartitioner implements Partitioner {
int partitionGroupSize = 10;

@Override
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
    // For this we expect the key to be of the format "client/user"
    String[] splitValues = ((String)key).split("/");
    String client = splitValues[0];
    String user = splitValues[1];

    // Check that partitioncount is divisible by group size
    if (cluster.availablePartitionsForTopic(topic).size() % partitionGroupSize != 0) {
        throw new ConfigException("Partitioncount must be divisible by "+ partitionGroupSize +" for this partitioner but is " +
                cluster.availablePartitionsForTopic(topic).size() + " for topic " + topic);
    }

    // Calculate partition group from client and specific partition from user
    int clientPartitionOffset = Utils.murmur2(client.getBytes()) % partitionGroupSize * partitionGroupSize;
    int userPartition = Utils.murmur2(user.getBytes()) % partitionGroupSize;

    // Combine group and specific value to get final partition
    return clientPartitionOffset + userPartition;
}

@Override
public void configure(Map<String, ?> configs) {
    if (configs.containsKey("partition.group.size")) {
        this.partitionGroupSize = Integer.parseInt((String)configs.get("partition.group.size"));
    }
}

@Override
public void close() {
}

}

执行此操作当然会影响您的分发,您可能值得花时间使用不同的partitionGroupSize值和数据的代表性样本运行一些模拟来估算分布的均匀程度以及您需要多少开销。 d在重新处理整个客户端时保存。