我有多个冗余应用程序实例,这些实例要消耗主题的所有事件并独立存储它们以进行磁盘查找(通过rocksdb)。
为了论证,让我们假设这些多余的使用者正在服务无状态的http请求;因此不会使用kafka共享负载,而是使用kafka将数据从生产者复制到每个实例本地存储中。
在查看生成的主题时,每个使用应用的应用都会创建3个额外的主题:
但是,每个生成的主题都与原始主题的压缩视图一样大。这意味着每个消费商店都将原始主题(已经压缩)的大小乘以3。
简而言之,我对存储可伸缩性感到担忧,因为我们使用的应用程序越来越多,这些应用程序会产生更多的更改日志主题...
这是创建商店的代码
public class ProgramMappingEventStoreFactory {
private static final Logger logger = Logger.getLogger(ProgramMappingEventStoreFactory.class.getName());
private final static String STORE_NAME = "program-mapping-store";
private final static String APPLICATION_NAME = "epg-mapping-catalog_program-mapping";
public static ReadOnlyKeyValueStore<ProgramMappingEventKey, ProgramMappingEvent> newInstance(String kafkaBootstrapServerUrl,
String avroRegistryUrl,
String topic,
String storeDirectory)
{
Properties kafkaConfig = new KafkaConfigBuilder().withBootstrapServers(kafkaBootstrapServerUrl)
.withSchemaRegistryUrl(avroRegistryUrl)
.withApplicationId(createApplicationId(APPLICATION_NAME))
.withGroupId(UUID.randomUUID().toString())
.withClientId(UUID.randomUUID().toString())
.withDefaultKeySerdeClass(SpecificAvroSerde.class)
.withDefaultValueSerdeClass(SpecificAvroSerde.class)
.withStoreDirectory(storeDirectory)
.build();
StreamsBuilder streamBuilder = new StreamsBuilder();
bootstrapStore(streamBuilder, topic);
KafkaStreams streams = new KafkaStreams(streamBuilder.build(), kafkaConfig);
streams.start();
try {
return getStoreAndBlockUntilQueryable(STORE_NAME,
QueryableStoreTypes.keyValueStore(),
streams);
} catch (InterruptedException e) {
throw new IllegalStateException("Failed to create the LiveMediaPolicyIdStore", e);
}
}
private static <T> T getStoreAndBlockUntilQueryable(String storeName,
QueryableStoreType<T> queryableStoreType,
KafkaStreams streams)
throws InterruptedException
{
while (true) {
try {
return streams.store(storeName, queryableStoreType);
} catch (InvalidStateStoreException ignored) {
Thread.sleep(100);
}
}
}
private static void bootstrapStore(StreamsBuilder builder, String topic) {
KTable<ProgramMappingEventKey, ProgramMappingEvent> table = builder.table(topic);
table.groupBy((k, v) -> KeyValue.pair(k, v)).reduce((newValue, aggValue) -> newValue,
(newValue, aggValue) -> null,
Materialized.as(STORE_NAME));
}
private static String createApplicationId(String applicationName) {
try {
return String.format("%s-%s", applicationName, InetAddress.getLocalHost().getHostName());
} catch (UnknownHostException e) {
logger.warning(() -> "Failed to find the hostname, generating a uique applicationId");
return String.format("%s-%s", applicationName, UUID.randomUUID());
}
}
}
答案 0 :(得分:2)
如果要将同一状态加载到多个实例中,则应在所有实例(GlobalKTable
)上使用application.id
和唯一的builder.globalTable()
。
如果您使用KTable
,则数据已分区,迫使您对每个实例使用不同的application.id
。这可以被视为反模式。
我也不确定,为什么要groupBy((k, v) -> KeyValue.pair(k, v)).reduce()
-这样会导致不必要的分区主题。
对于为table()
运算符生成的changelog主题,如果使用1.0
,则1.1
和StreamsBuilder
版本中存在一个已知错误(不使用KStreamBuilder
受影响)。它已在2.0
版本(https://issues.apache.org/jira/browse/KAFKA-6729)中修复