前段时间在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来帮助我们实现。具体的步骤为:

  1. 构建ConcurrentLinkedQueue对象分别给两个线程使用(这里并不限定于两个线程,但这个需求最可能的实际场景是consumer主线程和一个后台管理类的用户线程,而后者负责触发“动态修改订阅”逻辑)
  2. 调用KafkaConsumer.poll(timeout)来不断消费消息。经常有人问这里的timeout到底是做什么用的?这里统一回答一下:这里的timeout赋予了用户在consumer读取消息后可以执行其他一些操作的能力,比如定期的记录日志等。如果你的consumer没有这样的需求,那么调用KafkaConsumer.poll(1000)和KafkaConsumer.poll(Integer.MAX)没有任何区别。事实上, 我们更加推荐用户使用KafkaConsumer.poll(Integer.MAX) + wakeup的方式来响应后端其他逻辑
  3. 每次poll之后尝试去探查一下ConcurrentLinkedQueue有没有新东西(如果有说明订阅topic列表发生变化),响应之
  4. 使用另一个线程往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订阅的更多相关文章

  1. [Kafka] - Kafka Java Consumer实现(一)

    Kafka提供了两种Consumer API,分别是:High Level Consumer API 和 Lower Level Consumer API(Simple Consumer API) H ...

  2. 关于Kafka java consumer管理TCP连接的讨论

    本篇是<关于Kafka producer管理TCP连接的讨论>的续篇,主要讨论Kafka java consumer是如何管理TCP连接.实际上,这两篇大部分的内容是相同的,即consum ...

  3. [Kafka] - Kafka Java Consumer实现(二)

    Kafka提供了两种Consumer API,分别是:High Level Consumer API 和 Lower Level Consumer API(Simple Consumer API) H ...

  4. Java实现动态修改Jar包内文件内容

    import java.io.*; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; ...

  5. kafka 客户端 consumer 配置参数

    1.Consumer Group 与 topic 订阅 每个Consumer 进程都会划归到一个逻辑的Consumer Group中,逻辑的订阅者是Consumer Group.所以一条message ...

  6. kafka java动态获取topic并动态创建消费者

    1.获取所有topic package com.example.demo; import java.io.IOException; import java.util.List; import org. ...

  7. Kafka动态增加Topic的副本

    一.kafka的副本机制 由于Producer和Consumer都只会与Leader角色的分区副本相连,所以kafka需要以集群的组织形式提供主题下的消息高可用.kafka支持主备复制,所以消息具备高 ...

  8. 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 ...

  9. Kafka Java API获取非compacted topic总消息数

    目前Kafka并没有提供直接的工具来帮助我们获取某个topic的当前总消息数,需要我们自行写程序来实现.下列代码可以实现这一功能,特此记录一下: /** * 获取某个topic的当前消息数 * Jav ...

随机推荐

  1. Qt中QString::toStdString().c_str() 偶尔存在问题

    假设 QString str = "string"; const char* c = str.toStdString().c_str()单步调试显示的结果可能会是'\0' 而当我这 ...

  2. Encapsulating Data 数据封装

    Objective-C中类的封装本质上其他OO语言没什么区别,不过在概念和书写表达上差异还是比较大的, Property属性 这里的Property并不是简单的类成员变量,而是OC中特有的可以为编译器 ...

  3. c语言数据类型、运算符和表达式

    数据类型 1.基本数据类型: 基本数据类型最主要的特点是,其值不可以分解为其他类型.也就是说,基本数据类型是 自我说明的 2.构造数据类型: 构造数据类型是根据已定于的一个或多个数据类型用构造的方法来 ...

  4. core 文件查看

    步骤: (1)无论你是用Makefile来编译,还是直接在命令行手工输入命令来编译,都应该加上 -g 选项. (2)一般来说,在默认情况下,在程序崩溃时,core文件是不生成的(很多Linux发行版在 ...

  5. SpringMVC系列(一)SpringMVC概述和搭建SpringMVC的第一个helloWord入门程序

    一.SpringMVC 概述 • Spring为展现层提供的基于MVC设计理念的优秀的Web框架,是目前最主流的MVC框架之一 • Spring3.0 后全面超越 Struts2,成为最优秀的 MVC ...

  6. e828. 创建JTabbedPane

    A tabbed pane is a container that displays only one child component at a time. Typically, the childr ...

  7. 在Ubuntu14.04上编译Android4.0.1出现的几个问题

    一. 工具 sudo apt-get install git-core gnupg flex bison gperf build-essential \       zip curl libc6-de ...

  8. 【阿里云】WindowsServer2012 搭建FTP站点 图文记录

    配置说明: 服务商:阿里云 系统: WindowsServer2012 一:配置FTP服务器 1.进入操作系统,直接从启动栏打开服务器管理器,选择添加功能和角色 2.选择服务器 3.勾选FTP服务器选 ...

  9. A:LinkedList实现了List接口; B: AbstractSet实现了Set接口; C: HashSet继承自AbstractSet基类; D: WeakMap继承自 AbstractMap

    List,Set,Map在java.util包下都是接口 List有两个实现类:ArrayList和LinkedListSet有两个实现类:HashSet和LinkedHashSetAbstractS ...

  10. css3实现小箭头,各种图形

    转:http://blog.csdn.net/tangtang5963/article/details/51490107 https://segmentfault.com/a/119000000278 ...