Java 春季卡夫卡制作人线程不断增加
我们将Kafka与Spring一起使用,目前正在对应用程序进行一些负载测试。在启动负载测试的几分钟内,Tomcat停止响应,在分析线程转储时,我看到相当多的Kafka生产者线程,并假设这可能是应用程序挂起的原因。线程数量相当高,即在几分钟内有200多个Kafka生产者线程。有没有办法关闭这些生产者线程。下面是我的Spring Kafka producer配置 编辑: 在我们的应用程序中,我们有一个事件发布/订阅,我使用Kafka发布事件分区数:15,并发数:5Java 春季卡夫卡制作人线程不断增加,java,tomcat,apache-kafka,spring-kafka,Java,Tomcat,Apache Kafka,Spring Kafka,我们将Kafka与Spring一起使用,目前正在对应用程序进行一些负载测试。在启动负载测试的几分钟内,Tomcat停止响应,在分析线程转储时,我看到相当多的Kafka生产者线程,并假设这可能是应用程序挂起的原因。线程数量相当高,即在几分钟内有200多个Kafka生产者线程。有没有办法关闭这些生产者线程。下面是我的Spring Kafka producer配置 编辑: 在我们的应用程序中,我们有一个事件发布/订阅,我使用Kafka发布事件分区数:15,并发数:5 @Bean public Prod
@Bean
public ProducerFactory<String, Object> producerFactory() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
configProps.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, KafkaCustomPartitioner.class);
configProps.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
configProps.put(ProducerConfig.LINGER_MS_CONFIG, 200);
DefaultKafkaProducerFactory factory = new DefaultKafkaProducerFactory<>(configProps);
factory.setTransactionIdPrefix(serverId+"-tx-");
// factory.setProducerPerConsumerPartition(false);
return factory;
}
public ConsumerFactory<String, Object> consumerFactory(String groupId) {
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
props.put(ConsumerConfig.ISOLATION_LEVEL_CONFIG,"read_committed");
props.put(ConsumerConfig.GROUP_ID_CONFIG,"custom-group-id");
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG,60000);
props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG,5000);
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG,20);
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG,600000);
props.put(JsonDeserializer.TRUSTED_PACKAGES, "org.xxx.xxx.xxx");
return new DefaultKafkaConsumerFactory<>(props);
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> customKafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
//factory.setConcurrency(eventTopicConcurrency);
factory.getContainerProperties().setAckOnError(false);
factory.getContainerProperties().setAckMode(AckMode.MANUAL_IMMEDIATE);
factory.setErrorHandler(new SeekToCurrentErrorHandler());
factory.setConsumerFactory(consumerFactory("custom-group-id"));
return factory;
}
我将写一个更完整的答案来帮助其他可能发现这个问题的人 在使用事务时,默认情况下,我们必须为每个
组/主题/分区
组合创建一个新的生产者(假设事务由使用者线程启动);这样,如果发生再平衡,生产商就可以得到适当的保护
卡夫卡2.5客户端有一个改进的算法,可以改善这种情况,我们不再需要所有这些生产者
但是,必须将代理升级到2.5.0才能使用此功能
即将发布的2.5.0.0版本(将于明天发布)允许事务生产者使用这种新的线程模型
可用于测试
有关新功能的文档已发布
但是,您已禁用创建提供适当生产者防护的生产者
factory.setProducerPerConsumerPartition(false);
因此,在本例中,您应该看到生产者被缓存;除非您的侦听器容器上有一个巨大的并发性,并且以非常高的容量生成,否则拥有如此多的生产者是不寻常的
生产者工厂目前不支持限制缓存的大小
也许您可以编辑您的问题,以进一步解释您的应用程序正在执行的操作,并显示更多的代码/配置。我添加了更多的代码,请在设置
factory.setProducerPerConsumerPartition(false)后查看它是否给出了任何提示代码>我没有运行负载测试,给出的观察结果是默认的,仍然200+看起来是一个相当大的数字,在设置producerpconsumerpartition=false
多少订阅者后,明天将检查线程数?考虑到这种架构,如果订阅者的数量很高,那么最终会有很多制作人,我并不感到惊讶。我假定publish()
是从subscriber.handle()
调用的。也许考虑使用一个用户池来限制您需要的并发生产者的数量。对不起,我在您的第二个评论之前发表了这篇文章;但是,考虑到您正在新线程(而不是容器线程)上运行生产者,我怀疑生产者工厂设置是否会有所不同,因为您使用的是缓存中的生产者,而不是容器将启动的生产者(假设您已将CKTM注入容器)。但是,使用事务容器是没有意义的,因为您的生产者没有参与该事务。我无法理解您的第一部分评论,您能详细说明吗?“但是,考虑到您正在新线程(而不是容器线程)上运行生产者,我怀疑producer-factory设置是否会起作用,因为您使用的是缓存中的producer,而不是容器将启动的producer”。还有,为什么您说生产者没有参与事务,调用发布方法的方法在@Transactional下
@KafkaListener(topics = "${kafka.event.topic.name}-#{ClusterConfigSplitter.toClusterId('${cluster.info}')}", concurrency="${kafka.event.topic.concurrency}", clientIdPrefix="${web.server.id}-event-consumer", containerFactory = "customKafkaListenerContainerFactory")
public void eventTopicListener(Event event, Acknowledgment ack)
throws InterruptedException, ClassNotFoundException, IOException {
if(LOGGER.isDebugEnabled())
LOGGER.debug("Received event {} : {}", event.getDomainEvent().getEventName(), event.getDomainEvent().getEventId());
DomainEvent domainEvent = event.getDomainEvent();
List<EventSubscriber> subcribers = new ArrayList<>();
for (String failedSubscriber : event.getSubscribersToRetry()) {
subcribers.add(eventSubcribers.get(failedSubscriber));
}
CountDownLatch connectionLatch = new CountDownLatch(subcribers.size());
List<String> failedSubscribers = new ArrayList<>();
for (EventSubscriber subscriber : subcribers) {
taskExecutor.execute(new Runnable() {
@Override
public void run() {
tenantContext.setTenant(domainEvent.getTenantId());
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName(domainEvent.getEventId() + "-" + subscriber.getClass().getName());
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus status = txManager.getTransaction(def);
try {
subscriber.handle(domainEvent);
txManager.commit(status);
} catch (Exception ex) {
LOGGER.error("Processing event {} : {} failed for {} - {}", domainEvent.getEventName(), domainEvent.getEventId(), ex);
txManager.rollback(status);
failedSubscribers.add(subscriber.getClass().getName());
}
connectionLatch.countDown();
if(LOGGER.isDebugEnabled())
LOGGER.debug("Processed event {} : {} by {} ", domainEvent.getEventName(), domainEvent.getEventId(), subscriber.getClass().getName());
}
});
}
connectionLatch.await();
ack.acknowledge();
if(failedSubscribers.size()>0) {
eventPersistenceService.eventFailed(domainEvent, failedSubscribers, event.getRetryCount()+1);
}
}
@Bean
@Primary
public PlatformTransactionManager transactionManager(EntityManagerFactory factory,@Qualifier("common-factory") EntityManagerFactory commonFactory, ProducerFactory producerFactory){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(factory);
JpaTransactionManager commonTransactionManager = new JpaTransactionManager();
commonTransactionManager.setEntityManagerFactory(commonFactory);
KafkaTransactionManager kafkaTransactionManager= new KafkaTransactionManager(producerFactory);
return new ChainedKafkaTransactionManager(kafkaTransactionManager,commonTransactionManager,transactionManager);
}
factory.setProducerPerConsumerPartition(false);