Kafka客户端无法找到PlainLoginModule,无需显式设置类加载器如何解决?
问题
使用自定义ThreadPoolTaskExecutor运行的任务中异步调用kafkaTemplate.send时,抛出如下Kafka异常:
Caused by: org.apache.kafka.common.KafkaException: javax.security.auth.login.LoginException: No LoginModule found for org.apache.kafka.common.security.plain.PlainLoginModule at org.apache.kafka.common.network.SaslChannelBuilder.configure(SaslChannelBuilder.java:184) at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:192) at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:81) at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:105) at org.apache.kafka.clients.producer.KafkaProducer.newSender(KafkaProducer.java:447) at org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:428) ... 17 common frames omitted Caused by: javax.security.auth.login.LoginException: No LoginModule found for org.apache.kafka.common.security.plain.PlainLoginModule at java.base/javax.security.auth.login.LoginContext.invoke(LoginContext.java:739)
相关依赖及版本:
- Spring Boot版本:2.6.12
- Java版本:17
- io.confluent:kafka-avro-serializer:7.2.1
- org.apache.kafka:kafka-clients:3.3.1
- org.springframework.kafka:spring-kafka:2.9.1
目前通过显式设置线程上下文类加载器可临时解决问题:
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
需要找到无需显式修改线程上下文类加载器的解决方案。
解决方案
1. 自定义线程池时预设上下文类加载器
初始化自定义ThreadPoolTaskExecutor时,通过自定义线程工厂,给新创建的线程绑定正确的上下文类加载器,避免在任务中手动修改:
@Bean public ThreadPoolTaskExecutor customKafkaThreadPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(20); executor.setThreadNamePrefix("kafka-task-"); // 自定义线程工厂,继承创建线程池时的上下文类加载器 executor.setThreadFactory(runnable -> { Thread thread = new Thread(runnable); thread.setContextClassLoader(Thread.currentThread().getContextClassLoader()); return thread; }); executor.initialize(); return executor; }
2. 利用TaskDecorator传递上下文
如果用@Async结合自定义线程池,可通过TaskDecorator传递当前上下文的类加载器,确保异步执行时类加载环境一致:
@Bean public ThreadPoolTaskExecutor customKafkaThreadPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 线程池核心配置... executor.setTaskDecorator(runnable -> { ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); return () -> { try { Thread.currentThread().setContextClassLoader(currentClassLoader); runnable.run(); } finally { Thread.currentThread().setContextClassLoader(null); } }; }); executor.initialize(); return executor; }
3. 检查Kafka依赖完整性
确认kafka-clients依赖未被意外排除核心模块,org.apache.kafka.common.security.plain.PlainLoginModule属于kafka-clients核心包,若依赖被裁剪会导致类找不到问题。Maven依赖示例:
<dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> <version>3.3.1</version> <!-- 移除不必要的排除规则,确保核心类可被加载 --> </dependency>
4. 适配Spring Boot可执行JAR的类加载机制
若使用Spring Boot可执行JAR打包,默认用LaunchedURLClassLoader加载类。确保自定义线程池的线程不使用系统类加载器,而是继承线程池创建时的上下文类加载器——也就是LaunchedURLClassLoader,避免类加载环境不一致。
内容的提问来源于stack exchange,提问作者sabeena kumarawadu
相关产品推荐
相关产品推荐

