簡體   English   中英

如何在同一個盒子上獨立運行多個kafka消費者?

[英]How to run multiple kafka consumers on the same box independent of each other?

我有兩個Kafka消費者ConsumerAConsumerB 我想在同一台機器上獨立運行這兩個kafka消費者。 它們之間根本沒有關系。 這兩個kafka消費者將在同一台機器上處理不同的主題。

  • 每個使用者都應該有一個不同的Properties對象。
  • 每個消費者應該具有不同的線程池配置,因為它們可以以多線程方式(消費者組)運行,如果需要獨立於其他消費者。

以下是我的設計:

消費者類(摘要):

 public abstract class Consumer implements Runnable {
    private final Properties consumerProps;
    private final String consumerName;

    public Consumer(String consumerName, Properties consumerProps) {
        this.consumerName = consumerName;
        this.consumerProps = consumerProps;
    }

    protected abstract void shutdown();
    protected abstract void run(String consumerName, Properties consumerProps);

    @Override
    public final void run() {
        run(consumerName, consumerProps);
    }
}

消費者類:

public class ConsumerA extends Consumer {
    private final AtomicBoolean closed = new AtomicBoolean(false);
    private KafkaConsumer<byte[], byte[]> consumer;

    public ConsumerA(String consumerName, Properties consumerProps) {
        super(consumerName, consumerProps);
    }

    @Override
    public void shutdown() {
        closed.set(true);
        consumer.wakeup();
    }

    @Override
    protected void run(String consumerName, Properties consumerProps) {
        consumer = new KafkaConsumer<>(consumerProps);
        consumer.subscribe(getTopicsBasisOnConsumerName());

        Map<String, Object> config = new HashMap<>();
        config.put(Config.URLS, TEST_URL);
        GenericRecordDomainDataDecoder decoder = new GenericRecordDomainDataDecoder(config);

        try {
            while (!closed.get()) {
                ConsumerRecords<byte[], byte[]> records = consumer.poll(Long.MAX_VALUE);
                for (ConsumerRecord<byte[], byte[]> record : records) {
                    GenericRecord payload = decoder.decode(record.value());
                    // extract data from payload
                    System.out.println("topic = %s, partition = %s, offset = %d, customer = %s, country = %s\n",
                                      record.topic(), record.partition(), record.offset(), record.key(), record.value());
                }
                consumer.commitAsync();
            }
        } catch (WakeupException ex) {
            // Ignore exception if closing
            System.out.println("error= ", ex);
            if (!closed.get()) throw e;             
        } catch (Exception ex) {
            System.out.println("error= ", ex);      
        } finally {
            try {
                consumer.commitSync();
            } finally {
                consumer.close();
            }
        }
    }
}

ConsumerA B類:

// similar to `ConsumerA` but with specific details of B

ConsumerHandler類:

public final class ConsumerHandler {
  private final ExecutorService executorServiceConsumer;
  private final Consumer consumer;
  private final List<Consumer> consumers = new ArrayList<>();

  public ConsumerHandler(Consumer consumer, int poolSize) {
    this.executorServiceConsumer = Executors.newFixedThreadPool(poolSize);
    this.consumer = consumer;
    for (int i = 0; i < poolSize; i++) {
      this.consumers.add(consumer);
      executorServiceConsumer.submit(consumer);
    }
 }
  public void shutdown() {
    Runtime.getRuntime().addShutdownHook(new Thread() {
      @Override
      public void run() {
        for (Consumer consumer : consumers) {
          consumer.shutdown();
        }
        executorServiceConsumer.shutdown();
        try {
          executorServiceConsumer.awaitTermination(1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException ex) {
          Thread.currentThread().interrupt();
        }
      }
    });
  }
}

下面是我的一個項目中的主要課程,如果我啟動我的服務器,呼叫將首先自動進入,並從這個地方開始我執行我的ConsumerAConsumerB所有kafka消費者。 一旦調用shutdown,我就通過在所有Kafka消費者上調用shutdown來釋放所有資源。

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Singleton;

@Singleton
@DependencyInjectionInitializer
public class Initializer {
  private ConsumerHandler consumerHandlerA;
  private ConsumerHandler consumerHandlerB;

  @PostConstruct
  public void init() {
    consumerHandlerA = new ConsumerHandler (new ConsumerA("consumerA", getConsumerPropsA()), 3);
    consumerHandlerB = new ConsumerHandler (new ConsumerB("consumerB", getConsumerPropsB()), 3);
  }

  @PreDestroy
  public void shutdown() {
    consumerHandlerA.shutdown();
    consumerHandlerB.shutdown();
  }
}

對於我想在同一個盒子上運行多個kafka消費者的這類問題,這是正確的設計嗎? 如果有更好更有效的方法來解決這個問題,請告訴我。 一般來說,我將在同一個盒子上運行最多三個或四個Kafka消費者,並且每個消費者可以根據需要擁有自己的消費者群體。

這是我在消費者中使用的KafkaConsumer的Javadoc。 本文的基礎上,我創建了我的消費者,只是我使用了抽象類來擴展它。 在該鏈接中搜索“全部放在一起”

在文檔中提到消費者不是線程安全的,但看起來我的代碼正在為池中的每個線程重用相同的消費者實例。

public ConsumerHandler(Consumer consumer, int poolSize) {
    this.executorServiceConsumer = Executors.newFixedThreadPool(poolSize);
    this.consumer = consumer;
    for (int i = 0; i < poolSize; i++) {
      this.consumers.add(consumer);
      executorServiceConsumer.submit(consumer);
    }
 }

解決此線程安全問題的最佳方法是什么,仍然可以實現相同的功能?

一個快速的建議,如果您已經了解它,請道歉。 類級變量永遠不是線程安全的。 如果需要為每個線程使用不同的Properties對象,最好在方法級別聲明它們,並將它們作為參數提供給需要訪問Properties對象的其他方法。

最簡單的解決方案解決“解決此線程安全問題的最佳方法是什么,仍能實現相同的功能?”

不要實現多線程(Thread API / Executor Service),而是在每個單獨的JVM進程中使用並運行每個使用者作為單個使用者,因此如果您需要在同一台機器上有4個使用者並且您不想處理多線程處理頭痛那么讓你的kafka消費者代碼JAR在它自己的4個獨立的Java進程中運行。

試試Apache Samza。 它解決了這些消費者的問題。 沒有雜亂(有時是有問題的)線程處理,通過集群實現冗余,經過數萬億經過驗證的已處理消息的成熟解決方案等等。我們目前在集群上運行多個作業。 我們的代碼遠不如您的代碼復雜。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM