Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/382.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在kafka 0.9.0中使用多线程使用者?_Java_Multithreading_Apache Kafka_Distributed Computing_Apache Flink - Fatal编程技术网

Java 如何在kafka 0.9.0中使用多线程使用者?

Java 如何在kafka 0.9.0中使用多线程使用者?,java,multithreading,apache-kafka,distributed-computing,apache-flink,Java,Multithreading,Apache Kafka,Distributed Computing,Apache Flink,《卡夫卡宣言》给出了一种关于以下内容的方法: 每个线程一个使用者:一个简单的选项是为每个线程提供自己的使用者>实例 我的代码: public class KafkaConsumerRunner implements Runnable { private final AtomicBoolean closed = new AtomicBoolean(false); private final CloudKafkaConsumer consumer; private fina

《卡夫卡宣言》给出了一种关于以下内容的方法:

每个线程一个使用者:一个简单的选项是为每个线程提供自己的使用者>实例

我的代码:

public class KafkaConsumerRunner implements Runnable {

    private final AtomicBoolean closed = new AtomicBoolean(false);
    private final CloudKafkaConsumer consumer;
    private final String topicName;

    public KafkaConsumerRunner(CloudKafkaConsumer consumer, String topicName) {
        this.consumer = consumer;
        this.topicName = topicName;
    }

    @Override
    public void run() {
        try {
            this.consumer.subscribe(topicName);
            ConsumerRecords<String, String> records;
            while (!closed.get()) {
                synchronized (consumer) {
                    records = consumer.poll(100);
                }
                for (ConsumerRecord<String, String> tmp : records) {
                    System.out.println(tmp.value());
                }
            }
        } catch (WakeupException e) {
            // Ignore exception if closing
            System.out.println(e);
            //if (!closed.get()) throw e;
        }
    }

    // Shutdown hook which can be called from a separate thread
    public void shutdown() {
        closed.set(true);
        consumer.wakeup();
    }

    public static void main(String[] args) {
        CloudKafkaConsumer kafkaConsumer = KafkaConsumerBuilder.builder()
                .withBootstrapServers("172.31.1.159:9092")
                .withGroupId("test")
                .build();
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        executorService.execute(new KafkaConsumerRunner(kafkaConsumer, "log"));
        executorService.execute(new KafkaConsumerRunner(kafkaConsumer, "log.info"));
        executorService.shutdown();
    }
}
公共类KafkaConsumerRunner实现可运行{
private final AtomicBoolean closed=新的AtomicBoolean(false);
私人最终消费者;
私有最终字符串topicName;
公共KafkanConsumerRunner(CloudKafkanConsumer消费者,字符串主题名){
这个。消费者=消费者;
this.topicName=topicName;
}
@凌驾
公开募捐{
试一试{
this.consumer.subscribe(topicName);
消费者记录;
而(!closed.get()){
已同步(使用者){
记录=消费者调查(100);
}
用于(消费者记录tmp:记录){
System.out.println(tmp.value());
}
}
}捕获(唤醒异常e){
//关闭时忽略异常
系统输出打印ln(e);
//如果(!closed.get())抛出e;
}
}
//可以从单独的线程调用的shutdownhook
公共空间关闭(){
关闭。设置(真);
consumer.wakeup();
}
公共静态void main(字符串[]args){
CloudKafkaConsumer kafkaConsumer=KafkaConsumerBuilder.builder()
.使用BootstrapServer(“172.31.1.159:9092”)
.withGroupId(“测试”)
.build();
ExecutorService ExecutorService=Executors.newFixedThreadPool(5);
executorService.execute(新的KafkaConsumerRunner(kafkaConsumer,“log”));
executorService.execute(新的KafkaConsumerRunner(kafkaConsumer,“log.info”);
executorService.shutdown();
}
}
但它不起作用并引发异常:

java.util.ConcurrentModificationException:KafkaConsumer对多线程访问不安全

此外,我还阅读了Flink的源代码(用于分布式流和批处理数据的开放源代码平台)。Flink使用多线程消费程序与我的类似

long pollTimeout = Long.parseLong(flinkKafkaConsumer.properties.getProperty(KEY_POLL_TIMEOUT, Long.toString(DEFAULT_POLL_TIMEOUT)));
pollLoop: while (running) {
    ConsumerRecords<byte[], byte[]> records;
    //noinspection SynchronizeOnNonFinalField
    synchronized (flinkKafkaConsumer.consumer) {
        try {
            records = flinkKafkaConsumer.consumer.poll(pollTimeout);
        } catch (WakeupException we) {
            if (running) {
                throw we;
            }
            // leave loop
            continue;
        }
    }
long pollTimeout=long.parseLong(flinkkafaconsumer.properties.getProperty(KEY\u POLL\u TIMEOUT,long.toString(默认\u POLL\u TIMEOUT));
pollLoop:正在(运行时){
消费者记录;
//非最终字段的无检查同步
已同步(flinkKafkaConsumer.consumer){
试一试{
records=flinkkafaconsumer.consumer.poll(pollTimeout);
}catch(唤醒异常we){
如果(正在运行){
扔给我们;
}
//左循环
继续;
}
}


有什么问题吗?

卡夫卡消费者不是线程安全的

一个简单的选择是为每个线程提供自己的使用者实例

但是在您的代码中,相同的使用者实例由不同的kafka SumerRunner实例包装。因此,多个线程正在访问相同的使用者实例。kafka文档清楚地说明了

Kafka使用者不是线程安全的。所有网络I/O都发生在 发出调用的应用程序线程。它由 用户必须确保多线程访问是正确的 已同步。未同步的访问将导致 ConcurrentModificationException


这正是您收到的异常。

它在您呼叫订阅时引发异常。
this.consumer.subscribe(topicName);

将该块移动到同步块中,如下所示:

@Override
public void run() {
    try {
        synchronized (consumer) {
            this.consumer.subscribe(topicName);
        }
        ConsumerRecords<String, String> records;
        while (!closed.get()) {
            synchronized (consumer) {
                records = consumer.poll(100);
            }
            for (ConsumerRecord<String, String> tmp : records) {
                System.out.println(tmp.value());
            }
        }
    } catch (WakeupException e) {
        // Ignore exception if closing
        System.out.println(e);
        //if (!closed.get()) throw e;
    }
}
@覆盖
公开募捐{
试一试{
已同步(使用者){
this.consumer.subscribe(topicName);
}
消费者记录;
而(!closed.get()){
已同步(使用者){
记录=消费者调查(100);
}
用于(消费者记录tmp:记录){
System.out.println(tmp.value());
}
}
}捕获(唤醒异常e){
//关闭时忽略异常
系统输出打印ln(e);
//如果(!closed.get())抛出e;
}
}

可能不是您的情况,但是如果您正在处理多个主题的数据,那么您可以使用同一个使用者从多个主题中读取数据。如果不是,则最好创建使用每个主题的单独作业。

为我工作。