Kafka Java consumer动态修改topic订阅
前段时间在Kafka QQ群中有人问及此事——关于Java consumer如何动态修改topic订阅的问题。仔细一想才发现这的确是个好问题,因为如果简单地在另一个线程中直接持有consumer实例然后调用subscribe进行修改,consumer端必然会抛出异常ConcurrentModificationException:KafkaConsumer is not safe for multi-threaded access
和KafkaProducer不同的是,KafkaConsumer不是线程安全的,所以我们不能直接在没有同步保护的机制下直接启用另一个线程调用consumer的任何方法(除了wakeup)。因此,实现这个需求有两种途径:
- 使用重量级的synchorinzed机制来实现线程安全
- 借助Java类库已有的线程安全数据结构来实现
如果是第一种方式,则无论哪个线程访问consumer都必须要配备必要的同步保护机制,代价相当大且极易出错。本文选取第二种方式,我们可以借助Java提供的ConcurrentLinkedQueue来帮助我们实现。具体的步骤为:
- 构建ConcurrentLinkedQueue对象分别给两个线程使用(这里并不限定于两个线程,但这个需求最可能的实际场景是consumer主线程和一个后台管理类的用户线程,而后者负责触发“动态修改订阅”逻辑)
- 调用KafkaConsumer.poll(timeout)来不断消费消息。经常有人问这里的timeout到底是做什么用的?这里统一回答一下:这里的timeout赋予了用户在consumer读取消息后可以执行其他一些操作的能力,比如定期的记录日志等。如果你的consumer没有这样的需求,那么调用KafkaConsumer.poll(1000)和KafkaConsumer.poll(Integer.MAX)没有任何区别。事实上, 我们更加推荐用户使用KafkaConsumer.poll(Integer.MAX) + wakeup的方式来响应后端其他逻辑!
- 每次poll之后尝试去探查一下ConcurrentLinkedQueue有没有新东西(如果有说明订阅topic列表发生变化),响应之
- 使用另一个线程往ConcurrentLinkedQueue中插入新的订阅信息
完整样例代码如下:
public class ConsumerTest {
public static void main(String[] args) {
final ConcurrentLinkedQueue<String> subscribedTopics = new ConcurrentLinkedQueue<>();
// 创建另一个测试线程,启动后首先暂停10秒然后变更topic订阅
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// swallow it.
}
// 变更为订阅topic: btopic, ctopic
subscribedTopics.addAll(Arrays.asList("btopic", "ctopic"));
}
};
new Thread(runnable).start();
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "my-group1");
props.put("auto.offset.reset", "earliest");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
// 最开始的订阅列表:atopic、btopic
consumer.subscribe(Arrays.asList("atopic", "btopic"));
while (true) {
consumer.poll(2000); //表示每2秒consumer就有机会去轮询一下订阅状态是否需要变更
// 本例不关注消息消费,因此每次只是打印订阅结果!
System.out.println(consumer.subscription());
if (!subscribedTopics.isEmpty()) {
Iterator<String> iter = subscribedTopics.iterator();
List<String> topics = new ArrayList<>();
while (iter.hasNext()) {
topics.add(iter.next());
}
subscribedTopics.clear();
consumer.subscribe(topics); // 重新订阅topic
}
}
// 本例只是测试之用,使用了while(true),所以这里没有显式关闭consumer
// consumer.close();
}
}
输出如下:
[atopic, btopic]
[atopic, btopic]
[atopic, btopic]
[ctopic, btopic]
[ctopic, btopic]
由此可见,本consumer在没有关闭的情况下动态进行了topic的订阅变更。另外需要说一下,动态变更时最好不要直接调用subscribe(topics),而是要显式地定义ConsumerRebalanceListener以避免位移提交的混乱。
Kafka Java consumer动态修改topic订阅的更多相关文章
- [Kafka] - Kafka Java Consumer实现(一)
Kafka提供了两种Consumer API,分别是:High Level Consumer API 和 Lower Level Consumer API(Simple Consumer API) H ...
- 关于Kafka java consumer管理TCP连接的讨论
本篇是<关于Kafka producer管理TCP连接的讨论>的续篇,主要讨论Kafka java consumer是如何管理TCP连接.实际上,这两篇大部分的内容是相同的,即consum ...
- [Kafka] - Kafka Java Consumer实现(二)
Kafka提供了两种Consumer API,分别是:High Level Consumer API 和 Lower Level Consumer API(Simple Consumer API) H ...
- Java实现动态修改Jar包内文件内容
import java.io.*; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; ...
- kafka 客户端 consumer 配置参数
1.Consumer Group 与 topic 订阅 每个Consumer 进程都会划归到一个逻辑的Consumer Group中,逻辑的订阅者是Consumer Group.所以一条message ...
- kafka java动态获取topic并动态创建消费者
1.获取所有topic package com.example.demo; import java.io.IOException; import java.util.List; import org. ...
- Kafka动态增加Topic的副本
一.kafka的副本机制 由于Producer和Consumer都只会与Leader角色的分区副本相连,所以kafka需要以集群的组织形式提供主题下的消息高可用.kafka支持主备复制,所以消息具备高 ...
- CDH下集成spark2.2.0与kafka(四十一):在spark+kafka流处理程序中抛出错误java.lang.NoSuchMethodError: org.apache.kafka.clients.consumer.KafkaConsumer.subscribe(Ljava/util/Collection;)V
错误信息 19/01/15 19:36:40 WARN consumer.ConsumerConfig: The configuration max.poll.records = 1 was supp ...
- Kafka Java API获取非compacted topic总消息数
目前Kafka并没有提供直接的工具来帮助我们获取某个topic的当前总消息数,需要我们自行写程序来实现.下列代码可以实现这一功能,特此记录一下: /** * 获取某个topic的当前消息数 * Jav ...
随机推荐
- 嵌入式开发之视频压缩比---h264、mjpeg、mpeg4
mjpeg:以hi3519 100v的编码性能,压缩比在20~80,平均1/50 http://blog.csdn.net/mengxihe29/article/details/52584544 ht ...
- char* 与 char[] 的区别
"Hello world"作为静态字符串实际上存储在数据区,但写程序的人不知道这个地址,而程序本身知道.当某一函数以{ char p[] = "Hello world&q ...
- Python——eventlet.wsgi
eventlet 的 wsgi 模块提供了一种启动事件驱动的WSGI服务器的简洁手段,可以将其作为某个应用的嵌入web服务器,或作为成熟的web服务器,一个这样的web服务器的例子就是 Spawnin ...
- html固定宽度下拉框内容显示不全问题解决方法
不少时候在页面中为了布局的需要,下拉列表<select>的宽度需要设成比较小的值,这时如果恰巧它包含的选择项<option>的内容比较长,那么超出select宽度的部分将会被截 ...
- vimdiff的常用命令
★ 跳转到下一个diff点: 请使用 ]c 命令★ 跳转到前一个diff点: 请使用 [c命令如果在命令前加上数字的话,可以跳过一个或数个差异点,从而实现跳的更远.比如如果在位于第一个差异点的行输入& ...
- linux中find命令
1.使用name选项: 文件名选项是find命令最常用的选项,要么单独使用该选项,要么和其他选项一起使用. 可以使用某种文件名模式来匹配文件,记住要用引号将文件名模式引起来. 不管当前路径是什么,如果 ...
- Docker镜像相关
一.中间镜像 通过持续集成工具Jenkins构建Docker镜像并运行容器,采用的是Docker Compose来进行编排构建运行的.但是每次构建完毕以后通过docker images命令查询,可以发 ...
- windows 找不到文件'igfxHK.exe'
现象:开机时windows报:windows 找不到文件'igfxHK.exe' 解决办法:win+r 输入services.msc 进入服务管理, 找到服务名称为: Intel(R) HD Gr ...
- Mac下配置Oracle数据库客户端远程连接数据库服务器
下载mac数据库客户端: 地址:http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html 下载这俩个:(来源:http:// ...
- XToDo未完成内容标记管理器
下载地址:https://github.com/trawor/XToDo 跟VVDocumenter规范注释生成器的安装方式一样: 下载开源工程在Xcode重新编译运行会自动安装此插件,重启Xcode ...