1. 訂閱主題
(1)訂閱主題的全部分區
package com.bonc.rdpe.kafka110.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;
/**
* @Title Subscribe.java
* @Description 訂閱多個主題的全部分區
* @Author YangYunhe
* @Date 2018-06-28 09:53:41
*/
public class Subscribe {
public static void main(String[] args) throws Exception {
Properties props = new Properties();
props.put("bootstrap.servers", "192.168.42.89:9092,192.168.42.89:9093,192.168.42.89:9094");
props.put("group.id", "dev3-yangyunhe-group001");
props.put("auto.offset.reset", "earliest");
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);
String[] topics = new String[]{"dev3-yangyunhe-topic001", "dev3-yangyunhe-topic002"};
// 訂閱指定主題的全部分區
consumer.subscribe(Arrays.asList(topics));
try {
while (true) {
/*
* poll() 方法返回一個記錄列表。
* 每條記錄都包含了記錄所屬主題的信息、記錄所在分區的信息、記錄在分區里的偏移量,以及記錄的鍵值對。
* 我們一般會遍歷這個列表,逐條處理這些記錄。
* 傳給poll() 方法的參數是一個超時時間,用于控制 poll() 方法的阻塞時間(在消費者的緩沖區里沒有可用數據時會發生阻塞)。
* 如果該參數被設為 0,poll() 會立即返回,否則它會在指定的毫秒數內一直等待 broker 返回數據。
* 而在經過了指定的時間后,即使還是沒有獲取到數據,poll()也會返回結果。
*/
ConsumerRecords<String, String> records = consumer.poll(1000);
for (ConsumerRecord<String, String> record : records) {
System.out.println("topic = " + record.topic() + ", partition = " + record.partition());
}
}
} finally {
/*
* 在退出應用程序之前使用 close() 方法關閉消費者。
* 網絡連接和 socket 也會隨之關閉,并立即觸發一次再均衡,而不是等待群組協調器發現它不再發送心跳并認定它已死亡,
* 因為那樣需要更長的時間,導致整個群組在一段時間內無法讀取消息。
*/
consumer.close();
}
}
}
(2) 用正則表達式來訂閱主題的全部分區
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
// 訂閱所有以"dev3"開頭的主題的全部分區
Pattern pattern = Pattern.compile("dev3.*");
consumer.subscribe(pattern, new ConsumerRebalanceListener() {
@Override
public void onPartitionsRevoked(Collection<TopicPartition> arg0) {
// TODO nothing:再均衡監聽器會在之后的文章中進行討論
}
@Override
public void onPartitionsAssigned(Collection<TopicPartition> arg0) {
// TODO nothing:再均衡監聽器會在之后的文章中進行討論
}
});
...... 省略部分重復代碼
try {
# 消費數據
}
(3) 訂閱指定的分區
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
TopicPartition[] topicPartitions = new TopicPartition[]{
new TopicPartition("dev3-yangyunhe-topic001", 0),
new TopicPartition("dev3-yangyunhe-topic002", 1)
};
// 訂閱"dev3-yangyunhe-topic001"的分區0和"dev3-yangyunhe-topic002"的分區1
consumer.assign(Arrays.asList(topicPartitions));
...... 省略部分重復代碼
try {
# 消費數據
}
2. 消費者常用配置
(1) fetch.min.bytes
- 類型:int
- 默認值:1
- 可設置值:[0,...]
- 重要性:高
- 說明:該屬性指定了消費者從服務器獲取記錄的最小字節數。broker 在收到消費者的數據請求時,如果可用的數據量小于 fetch.min.bytes 指定的大小,那么它會等到有足夠的可用數據時才把它返回給消費者。這樣可以降低消費者和 broker 的工作負載,因為它們在主題不是很活躍的時候(或者一天里的低谷時段)就不需要來來回回地處理消息。如果沒有很多可用數據,但消費者的 CPU 使用率卻很高,那么就需要把該屬性的值設得比默認值大。如果消費者的數量比較多,把該屬性的值設置得大一點可以降低 broker 的工作負載。
(2) fetch.max.wait.ms
- 類型:int
- 默認值:500
- 可設置值:[0,...]
- 重要性:低
- 說明:我們通過 fetch.min.bytes 告訴 Kafka,等到有足夠的數據時才把它返回給消費者。而 feth.max.wait.ms 則用于指定 broker 的等待時間,默認是如果沒有足夠的數據流入Kafka,消費者獲取最小數據量的要求就得不到滿足,最終導致 500ms 的延遲。如果 fetch.max.wait.ms 被設為 100ms,并且 fetch.min.bytes 被設為 1MB,那么 Kafka 在收到消費者的請求后,要么返回 1MB 數據,要么在 100ms 后返回所有可用的數據,就看哪個條件先得到滿足。
(3) max.partition.fetch.bytes
- 類型:int
- 默認值:1048576
- 可設置值:[0,...]
- 重要性:高
- 說明:該屬性指定了服務器從每個分區里返回給消費者的最大字節數。它的默認值是 1MB,也就是說,KafkaConsumer.poll() 方法從每個分區里返回的記錄最多不超過 max.partition.fetch.bytes 指定的字節。如果一個主題有20個分區和5個消費者,那么每個消費者需要至少 4MB 的可用內存來接收記錄。在為消費者分配內存時,可以給它們多分配一些,因為如果群組里有消費者發生崩潰,剩下的消費者需要處理更多的分區。max.partition.fetch.bytes 的值必須比 broker 能夠接收的最大消息的字節數(通過 max.message.size 屬性配置)大,否則消費者可能無法讀取這些消息,導致消費者一直掛起重試。在設置該屬性時,另一個需要考慮的因素是消費者處理數據的時間。消費者需要頻繁調用 poll() 方法來避免會話過期和發生分區再均衡,如果單次調用 poll() 返回的數據太多,消費者需要更多的時間來處理,可能無法及時進行下一個輪詢來避免會話過期。如果出現這種情況,可以把 max.partition.fetch.bytes 值改小,或者延長會話過期時間。
(4) session.timeout.ms
- 類型:int
- 默認值:10000
- 重要性:high
- 說明:該屬性指定了消費者在被認為死亡之前可以與服務器斷開連接的時間,默認是 1s。如果消費者沒有在 session.timeout.ms 指定的時間內發送心跳給群組協調器,就被認為已經死亡,組協調器就會觸發再均衡,把它的分區分配給群組里的其他消費者。該屬性與heartbeat.interval.ms緊密相關。heartbeat.interval.ms 指定了 poll() 方法向協調器發送心跳的頻率,session.timeout.ms 則指定了消費者可以多久不發送心跳。所以,一般需要同時修改這兩個屬性,heartbeat.interval.ms 必須比 session.timeout.ms 小,一般是 session.timeout.ms 的三分之一。如果 session.timeout.ms 是 3s,那么 heartbeat.interval.ms 應該是 1s。把 session.timeout.ms 值設得比默認值小,可以更快地檢測和恢復崩潰的節點,不過長時間的輪詢或垃圾收集可能導致非預期的再均衡。把該屬性的值設置得大一些,可以減少意外的再均衡,不過檢測節點崩潰需要更長的時間。
(5) auto.offset.reset
- 類型:latest
- 默認值:string
- 可設置值:[latest, earliest, none]
- 重要性:中等
- 說明:該屬性指定了消費者在讀取一個沒有偏移量的分區或者偏移量無效的情況下(因消費者長時間失效,包含偏移量的記錄已經過時并被刪除)該作何處理。它的默認值是 latest,意思是說,在偏移量無效的情況下,消費者將從最新的記錄開始讀取數據(在消費者啟動之后生成的記錄)。另一個值是 earliest,意思是說,在偏移量無效的情況下,消費者將從起始位置讀取分區的記錄。none 則代表當偏移量失效后,直接拋出異常。
(6) enable.auto.commit
- 類型:boolean
- 默認值:true
- 重要性:中等
- 說明:該屬性指定了消費者是否自動提交偏移量,默認值是 true。為了盡量避免出現重復數據和數據丟失,可以把它設為 false,由自己控制何時提交偏移量。如果把它設為 true,還可以通過配置 auto.commit.interval.ms 屬性來控制提交的頻率。
(7) partition.assignment.strategy
- 類型:list
- 默認值:org.apache.kafka.clients.consumer.RangeAssignor
- 可設置值:
org.apache.kafka.clients.consumer.RangeAssignor
org.apache.kafka.clients.consumer.RoundRobinAssignor
自定義的策略 - 重要性:中等
- 說明:PartitionAssignor 根據給定的消費者和主題,決定哪些分區應該被分配給哪個消費者。Kafka 有兩個默認的分配策略。
- Range:該策略會把主題的若干個連續的分區分配給消費者。假設消費者 C1 和消費者 C2 同時訂閱了主題 T1 和主題 T2,并且每個主題有 3 個分區。那么消費者 C1 有可能分配到這兩個主題的分區 0 和分區 1,而消費者 C2 分配到這兩個主題的分區2。因為每個主題擁有奇數個分區,而分配是在主題內獨立完成的,第一個消費者最后分配到比第二個消費者更多的分區。只要使用了 Range 策略,而且分區數量無法被消費者數量整除,就會出現這種情況。
- RoundRobin:該策略把主題的所有分區逐個分配給消費者。如果使用 RoundRobin 策略來給消費者 C1 和消費者 C2 分配分區,那么消費者 C1 將分到主題 T1 的分區 0 和分區 2 以及主題 T2 的分區 1,消費者 C2 將分配到主題 T1 的分區 1 以及主題 T2 的分區 0 和分區 2。一般來說,如果所有消費者都訂閱相同的主題(這種情況很常見),RoundRobin 策略會給所有消費者分配相同數量的分區(或最多就差一個分區)。
(8) client.id
- 類型:string
- 默認值:""
- 重要性:低
- 說明:該屬性可以是任意字符串,代表消費的ID,broker 用它來標識從客戶端發送過來的消息
(9) max.poll.records
- 類型:int
- 默認值:500
- 可設置值:[1,...]
- 重要性:中等
- 說明:該屬性用于控制單次調用 poll() 方法最多能夠返回的記錄條數,可以幫你控制在輪詢里需要處理的數據量。
(10) receive.buffer.bytes 和 send.buffer.bytes
receive.buffer.bytes
- 類型:int
- 默認值:65536(64K)
- 可設置值:[-1,...]
- 重要性:中等
send.buffer.bytes
- 類型:int
- 默認值:131072(128K)
- 可設置值:[-1,...]
- 重要性:中等
說明:這兩個參數分別指定了 TCP socket 接收和發送數據包的緩沖區大小。如果它們被設為 -1,就使用操作系統的默認值。如果生產者或消費者與 broker 處于不同的數據中心,那么可以適當增大這些值,因為跨數據中心的網絡一般都有比較高的延遲和比較低的帶寬。