kafka
流處理消息隊列
1. 安裝
// 環境centos7,jdk8
wget https://mirrors.cnnic.cn/apache/kafka/2.2.0/kafka_2.11-2.2.0.tgz
tar -zxvf kafka_2.11-2.2.0.tgz
cd kafka_2.11-2.2.0
// 配置zookeeper的broker.id、log.dir、zookeeper.connect,broker.idp配置成集羣內唯一
vim config/server.properties
// 啓動zookeeper
bin/zookeeper-server-start.sh -daemon config/zookeeper.properties
// 啓動kafka
bin/kafka-server-start.sh config/server.properties &
2. 配置
3. 相關概念
- Broker:Kafka集羣包含一個或多個服務器,這些服務器就是Broker
- Topic:每條發佈到Kafka集羣的消息都必須有一個Topic, 一個Topic可以認爲是一類消息的集合。
- Partition:是物理概念上的分區,爲了提供系統吞吐率,在物理上每個Topic會分成一個或多個Partition,每個Partition對應一個文件夾;每個partition在存儲層面是append log文件。任何發佈到此partition的消息都會被直接追加到log文件的尾部,每條消息在文件中的位置稱爲offset(偏移量),offset爲一個long型數字,它是唯一標記一條消息。
- Producer:消息產生者,負責生產消息併發送到Kafka Broker;Producer將消息發佈到指定的Topic中,同時Producer也能決定將此消息歸屬於哪個partition;
Consumer:消息消費者,向kafka broker讀取消息並處理的客戶端。 每個consumer屬於一個consumer group;反過來說,每個group中可以有多個consumer.發送到Topic的消息,只會被訂閱此Topic的每個group中的一個consumer消費.如果所有的consumer都具有相同的group,這種情況和queue模式很像;消息將會在consumers之間負載均衡. 如果所有的consumer都具有不同的group,那這就是"發佈-訂閱";消息將會廣播給所有的消費者.
- Consumer Group:每個Consumer屬於一個特定的組,組可以用來實現組內消息分發負載均衡功能。
zookeeper:服務註冊發現, 無論是kafka集羣,還是producer和consumer都依賴於zookeeper來保證系統可用性集羣保存一些meta信息。
kafka和activeMQ不同的是:即使消息被消費,消息仍然不會被立即刪除.日誌文件將會根據broker中的配置要求,保留一定的時間之後刪除;比如log文件保留 2天,那麼兩天後,文件會被清除,無論其中的消息是否被消費.kafka通過這種簡單的手段,來釋放磁盤空間,以及減少消息消費之後對文件內容改動的磁盤IO開支.
對於consumer而言,它需要保存消費消息的offset,對於offset的保存和使用,有consumer來控制;當consumer正常消費消息時,offset將會"線性"的向前驅動,即消息將依次順序被消費.事實上consumer可以使用任意順序消費消息,它只需要將offset重置爲任意值.
kafka集羣幾乎不需要維護任何consumer和producer狀態信息,這些信息有zookeeper保存;因此producer和consumer的客戶端實現非常輕量級,它們可以隨意離開,而不會對集羣造成額外的影響.
partitions的設計目的有多個.最根本原因是kafka基於文件存儲.通過分區,可以將日誌內容分散到多個server上,來避免文件尺寸達到單機磁盤的上限,每個partiton都會被當前server(kafka實例)保存;可以將一個topic切分多任意多個partitions,來消息保存/消費的效率.此外越多的partitions意味着可以容納更多的consumer,有效提升併發消費的能力.
基於replicated方案,那麼就意味着需要對多個備份進行調度;每個partition都有一個server爲"leader";leader負責所有的讀寫操作,如果leader失效,那麼將會有其他follower來接管(成爲新的leader);follower只是單調的和leader跟進,同步消息即可..由此可見作爲leader的server承載了全部的請求壓力,因此從集羣的整體考慮,有多少個partitions就意味着有多少個"leader",kafka會將"leader"均衡的分散在每個實例上,來確保整體的性能穩定.
在kafka中,一個partition中的消息只會被group中的一個consumer消費;每個group中consumer消息消費互相獨立;我們可以認爲一個group是一個"訂閱"者,一個Topic中的每個partions,只會被一個"訂閱者"中的一個consumer消費,不過一個consumer可以消費多個partitions中的消息.kafka只能保證一個partition中的消息被某個consumer消費時,消息是順序的.事實上,從Topic角度來說,消息仍不是有序的.
kafka的設計原理決定,對於一個topic,同一個group中不能有多於partitions個數的consumer同時消費,否則將意味着某些consumer將無法得到消息.
如果一個topic的名稱爲"my_topic",它有2個partitions,那麼日誌將會保存在my_topic_0和my_topic_1兩個目錄中;日誌文件中保存了一序列"log entries"(日誌條目)
4. kafka的優缺點
優點
- 大數據量流式操作
- 可異步操作,性能好
- 批量操作,性能好
- kafka是對日誌文件進行append操作,因此磁盤檢索的開支是較小的;同時爲了減少磁盤寫入的次數,broker會將消息暫時buffer起來,當消息的個數(或尺寸)達到一定閥值時,再flush到磁盤,這樣減少了磁盤IO調用的次數.
- 消息傳送機制
- at most once: 最多一次,這個和JMS中"非持久化"消息類似.發送一次,無論成敗,將不會重發.
- at least once: 消息至少發送一次,如果消息未能接受成功,可能會重發,直到接收成功.[首選]
缺點:
- 尚未確保消息的發送與接收絕對可靠
5. 常見命令行操作
topic相關操作
// 創建topic, replication-factor:每個partition的副本個數 bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test // 查看所有topic bin/kafka-topics.sh --list --zookeeper localhost:2181 // 查看某個topic詳情 bin/kafka-topics.sh --zookeeper 127.0.0.1:2181 --topic test --describe
group相關操作
// 創建消費者組,消費者登錄時自動指定group,如果group不存在,kafka broker自動創建group; // 查看消費者組列表 bin/kafka-consumer-groups.sh test --bootstrap-server localhost:9092 --list // 查看某個消費者組詳情 bin/kafka-consumer-groups.sh test --bootstrap-server localhost:9092 --grop test --describe // 生產者登錄 bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test // 消費者登錄(指定topic,指定group) bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning --group 'test-group'
6. node操作 kafka-node
ConsumerGroup可以實現同組中只有一個可以收到信息,可以配合pm2負載均衡 + 數據庫的唯一索引使用, consumer不可以實現同組中只有一個可以收到信息,會同時受到信息,想來是groupId的設置沒有起作用,
生產者負載均衡
const kafka = require('kafka-node'), HighLevelProducer = kafka.HighLevelProducer, client = new kafka.KafkaClient('127.0.0.1:9092'), producer = new HighLevelProducer(client); producer.on('ready',function() { console.log('1') let payloads = [{ topic: 'test', messages: [ '1|xiaoming|23', '2|lili|22' ], partition:0, attributes: 2, timestamp: Date.now() }] producer.send(payloads, function(err, data) { console.log(data) }) }) producer.on('error', function(err) { console.log(err); })
消費者負載均衡
const kafka = require('kafka-node'), Producer = kafka.Producer, ConsumerGroup = kafka.ConsumerGroup; var options = { kafkaHost: '127.0.0.1:9092', groupId: 'test-group', protocol: ['roundrobin'], sessionTimeout: 15000, fromOffset: 'latest' // 'latest'標記每次從最近開始消費,'earliest'從最初開始消費,一旦確定後,後續無法更改,只能換組; }; consumerGroup = new ConsumerGroup(options,['test']); consumerGroup.on('message', function(message) { console.log(message); }); consumerGroup.on('error', function(message) { console.log(message); })