Kafka 0.10.1.0 后,Kafka Consumer 变为双线程的设计 :
老 Consumer 是多线程的架构 :
新 Consumer 设计了单线程 + 轮询的机制 , 实现非阻塞式的消息获取
KafkaConsumer 不是线程安全的 (thread-safe)
两套多线程方案 :


例子 : 消费者要做 1、2、3、4、5
| 方案 | 优点 | 缺点 |
|---|---|---|
| 多线程 + 多 KafkaConsumer | 方便实现 | 占更多系统资源 |
| 速度块, 无线程间交互开销 | 线程数受限于主题分区数, 扩展性差 | |
| 易于维护分区内的消费顺序 | 线程处理消息易超时 , 会 Rebalance | |
| 单线程 + 单 KafkaConsumer + 消息处理 Worker 线程池 | 可独立扩展消费获取线程数和 Worker 线程数 | 实现难度高 |
| 可扩展性好 | 难维护分区的消息消费顺序 | |
| 处理链路长, 不易于位移提交管理 |
方案 1 优势 :
方案 1 缺点 :
方案 2 优势 :
方案 2 缺点 :
方案 1 的代码 :
public class KafkaConsumerRunner implements Runnable {private final AtomicBoolean closed = new AtomicBoolean(false);private final KafkaConsumer consumer;public void run() {try {consumer.subscribe(Arrays.asList("topic"));while (!closed.get()) {ConsumerRecords records = consumer.poll(Duration.ofMillis(10000));// 执行消息处理逻辑}} catch (WakeupException e) {// Ignore exception if closingif (!closed.get()) throw e;} finally {consumer.close();}}// Shutdown hook which can be called from a separate threadpublic void shutdown() {closed.set(true);consumer.wakeup();}
}
方案 2 :
private final KafkaConsumer consumer;
private ExecutorService executors;
//...
private int workerNum = ...;executors = new ThreadPoolExecutor(workerNum, workerNum, 0L, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy());//...
while (true) {ConsumerRecords records = consumer.poll(Duration.ofSeconds(1));for (final ConsumerRecord record : records) {executors.submit(new Worker(record));}
}
//..