Kafka初試

weixin_33850890發表於2017-03-02

昨天在測試環境搭建了一套zookeeper+kafka(各一臺)的機器,開始進行kafka的實踐之旅。昨天下班前一直都出現無法傳送無法接收的問題,今天終於搞定了。

zookeeper的安裝

直接從官網下載bin包後,解壓即可

tar -zxvf zookeeper-3.4.9.tar.gz

需要修改的配置有:

  1. 把conf目錄下的zoo_sample.cfg改名為zoo.cfg(並修改dataDir)
  2. 修改bin目錄下的zkEnv.sh指令碼中的ZOO_LOG_DIR和ZOO_LOG4J_PROP

啟動zookeeper

bin/zkServer.sh start

Kafka的安裝

由於只使用了一個broker,所以直接解壓包

tar -zxvf kafka_2.11-0.10.2.0.tgz

需要修改的配置為config/server.properties檔案,主要修改的有log.dirs和listeners。

listeners=PLAINTEXT://localhost:9092

這裡有個坑,server.properties中一定要配置host.name或者listeners,不然會出現無法收發訊息的現象
然後啟動即可

bin/kafka-server-start.sh config/server.properties &

客戶端

安裝完以後需要寫生產者的消費者了,直接用最簡單的方法來寫。

Producer

package producer;

import java.util.Properties;
import java.util.concurrent.ExecutionException;

import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;

public class Producer {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
    Properties props = new Properties();
    props.put("bootstrap.servers","122.20.109.68:9092");
    props.put("acks","1");
    props.put("retries","0");
    props.put("batch.size","16384");
// props.put("linger.ms","1");
// props.put("buffer.memory","33554432");
    props.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
    props.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
//生產者的建立
    KafkaProducer producer = new KafkaProducer<>(props);

    for (int i=0;i<100;i++) {
      System.out.println("seding message "+i);
      ProducerRecord record = new ProducerRecord("testTopic",String.valueOf(i),"this is message"+i);
      producer.send(record, new Callback() {
        public void onCompletion (RecordMetadata metadata, Exception e) {
          if (null != e) {
            e.printStackTrace();
          } else {
            System.out.println(metadata.offset());
          }
        }
      });
    }
    Thread.sleep(100000);
    producer.close();
  }
} 

這裡有個坑,如果我直接用producer.send(ProducerRecord)方法,發完100條以後producer.close(),會導致Kafka無法收到訊息,懷疑是非同步傳送導致的,需要真的傳送到Kafka以後才能停止Producer,所以我在後面sleep了一下,加上以後就可以正常傳送了。
使用callback是非同步傳送,此外還能使用同步傳送,直接在send方法後加上一個get方法就會直接阻塞直到broker返回訊息已收到。

producer.send(record).get();

Producer的properties有幾個常用配置:

  • bootstrap.servers:Kafka叢集連線串,可以由多個host:port組成
  • acks:broker訊息確認的模式,有三種:
    0:不進行訊息接收確認,即Client端傳送完成後不會等待Broker的確認
    1:由Leader確認,Leader接收到訊息後會立即返回確認資訊
    all:叢集完整確認,Leader會等待所有in-sync的follower節點都確認收到訊息後,再返回確認資訊
    我們可以根據訊息的重要程度,設定不同的確認模式。預設為1
  • retries:傳送失敗時Producer端的重試次數,預設為0
  • batch.size:當同時有大量訊息要向同一個分割槽傳送時,Producer端會將訊息打包後進行批量傳送。如果設定為0,則每條訊息都DuLi傳送。預設為16384位元組
  • linger.ms:傳送訊息前等待的毫秒數,與batch.size配合使用。在訊息負載不高的情況下,配置linger.ms能夠讓Producer在傳送訊息前等待一定時間,以積累更多的訊息打包傳送,達到節省網路資源的目的。預設為0
  • key.serializer/value.serializer:訊息key/value的序列器Class,根據key和value的型別決定
  • buffer.memory:訊息緩衝池大小。尚未被髮送的訊息會儲存在Producer的記憶體中,如果訊息產生的速度大於訊息傳送的速度,那麼緩衝池滿後傳送訊息的請求會被阻塞。預設33554432位元組(32MB)

Consumer

package consumer;

import java.util.Arrays;
import java.util.Properties;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;


public class Consumer {
  public static void main(String[] args) {
    Properties props = new Properties();
    props.put("bootstrap.servers","122.20.109.68:9092");
    props.put("group.id","test");
    props.put("enable.auto.commit","true");
    props.put("auto.commit.interval.ms","1000");
    props.put("session.timeout.ms","30000");
    props.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
    props.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");

    KafkaConsumer consumer = new KafkaConsumer<>(props);
    consumer.subscribe(Arrays.asList("testTopic"));
    while(true) {
      ConsumerRecords records = consumer.poll(1000);
      for (ConsumerRecord record: records) {
        System.out.println("offset "+record.offset()+" Message: "+record.value());
      }
    }
  }
}

Consumer的Properties的常用配置有:

  • bootstrap.servers/key.deserializer/value.deserializer:和Producer端的含義一樣,不再贅述
  • fetch.min.bytes:每次最小拉取的訊息大小(byte)。Consumer會等待訊息積累到一定尺寸後進行批量拉取。預設為1,代表有一條就拉一條
  • max.partition.fetch.bytes:每次從單個分割槽中拉取的訊息最大尺寸(byte),預設為1M
  • group.id:Consumer的group id,同一個group下的多個Consumer不會拉取到重複的訊息,不同group下的Consumer則會保證拉取到每一條訊息。注意,同一個group下的consumer數量不能超過分割槽數。
  • enable.auto.commit:是否自動提交已拉取訊息的offset。提交offset即視為該訊息已經成功被消費,該組下的Consumer無法再拉取到該訊息(除非手動修改offset)。預設為true
  • auto.commit.interval.ms:自動提交offset的間隔毫秒數,預設5000。

參考:http://www.cnblogs.com/edison2012/p/5774207.html