我有一个混合匹配的Scala拓扑,其中主要角色是PAPI处理器,其他部分则通过DSL连接。
EventsProcessor:
INPUT: eventsTopic
OUTPUT: visitorsTopic (and others)
整个主题中的数据(包括原始eventsTopic
)通过进行分区,我们称其为DoubleKey
,它具有两个字段。
访客通过接收器被发送到visitorsTopic
:
.addSink(VISITOR_SINK_NAME, visitorTopicName,
DoubleKey.getSerializer(), Visitor.getSerializer(), visitorSinkPartitioner, EVENT_PROCESSOR_NAME)
在DSL中,我在以下主题上创建了一个KV KTable:
val visitorTable = builder.table(
visitorTopicName,
Consumed.`with`(DoubleKey.getKafkaSerde(),
Visitor.getKafkaSerde()),
Materialized.as(visitorStoreName))
我稍后将其连接到EventProcessor
:
topology.connectProcessorAndStateStores(EVENT_PROCESSOR_NAME, visitorStoreName)
所有内容都被共同分区(通过DoubleKey)。 visitorSinkPartitioner
执行典型的模运算:
Math.abs(partitionKey.hashCode % numPartitions)
在PAPI处理器EventsProcessor中,我查询此表以查看是否已经存在访客。
但是,在我的测试中(使用EmbeddedKafka,但这没有什么区别),如果我在一个分区上运行它们,一切都很好(EventProcessor在同一{{1}上的两个事件上检查KTable) },并且在第二个事件上(有一些延迟),它可以看到商店中存在的DoubleKey
),但是如果我使用更高的数字运行它,则EventProcessor永远不会在商店中看到该值。
但是,如果我通过API检查商店(迭代Visitor
),记录就在那里。所以我知道它一定要去不同的分区。
由于KTable应该处理其分区上的数据,并且所有数据都发送到同一分区(使用显式分区程序调用相同的代码),因此KTable应该将数据获取到同一分区上。
我的假设正确吗?可能会发生什么?
KafkaStreams 1.0.0,Scala 2.12.4。
PS。当然,在PAPI上通过store.all()
而不是put
来创建商店,这将是可行的,因为这肯定会使用代码运行所在的分区,但这是不可能的。 / p>
答案 0 :(得分:1)
是的,这些假设是正确的。
以防万一:
将分区程序传递到Scala EmbeddedKafka库时遇到问题。在其中一个测试套件中,操作不正确。 现在,按照健康的重构实践,在这种拓扑的所有套件中都使用了这种方法。
def getEmbeddedKafkaTestConfig(zkPort: Int, kafkaPort: Int) :
EmbeddedKafkaConfig = {
val producerProperties = Map(ProducerConfig.PARTITIONER_CLASS_CONFIG ->
classOf[DoubleKeyPartitioner].getCanonicalName)
EmbeddedKafkaConfig(kafkaPort = kafkaPort, zooKeeperPort = zkPort,
customProducerProperties = producerProperties)
}