kafka的定義:是一個分布式消息系統,由LinkedIn使用Scala編寫,用作LinkedIn的活動流(Activity
Stream)和運營數據處理管道(Pipeline)的基礎,具有高水平擴展和高吞吐量。
應用領域:已被多家不同類型的公司作為多種類型的數據管道和消息系統使用。如:
淘寶,支付寶,百度,twitter等
目前越來越多的開源分布式處理系統如Apache
flume、Apache
Storm、Spark,elasticsearch都支持與Kafka集成。
ActiveMQ
RabbitMQ
Kafka
所屬社區/公司
Apache
Mozilla
Public LicenseApache/LinkedIn
開發語言
Java
Erlang
Java
支持的協議
OpenWire、STOMP、
REST、XMPP、AMQPAMQP
仿AMQP
事物
支持
不支持
不支持
集群
支持
支持
支持
負載均衡
支持
支持
支持
動態擴容
不支持
不支持
支持(zk)
ActiveMQ還是支持JMS的一種消息中間件
Kafka的動態擴容目前是通過zookeeper來完成的
阿里巴巴的metaq,rocketmq都有kafka的影子
AMQP協議
消費者(Consumer):從消息隊列中請求消息的客戶端應用程序;
生產者(Producer):向broker發布消息的客戶端應用程序;
AMQP服務器端(broker):用來接收生產者發送的消息并將這些消息路由給服務器中的隊列;
Kafka客戶端支持當前大部分主流語言,包括:
C、C++、Erlang、Java、.net、perl、PHP、Python、Ruby、Go、Javascript。
可以使用以上任何一種語言和kafka服務器進行通信(即編寫自己的consumer和producer程序)
kafka的架構
主題(Topic):一個主題類似新聞中的體育、娛樂、教育等分類概念,在實際工程中通常一個業務一個主題;
分區(Partition):一個topic中的消息數據按照多個分區組織,分區是kafka消息隊列組織的最小單位,一個分區可以看做是一個FIFO的隊列;
備份(Replication):為了保證分布式可靠性,kafka0.8開始對每個分區的數據進行備份(不同Broker上),防止其中一個Broker宕機造成分區數據不可用
zookeeper:一個提供分布式狀態管理、分布式配置管理、分布式鎖服務等的集群
Kafka編程實例
消費者編程模型
分區消費模型
一個分區 一個消費者實例
分區消費偽代碼描述
Main()
分區模型消費的分區數是多少 即? 獲取分區的size
針對每個分區創建一個線程或進程 – 消費者實例
For
index = 0 to size
Create
thread(or process) consumer(index)
第index個線程(進程) –每個消費者實例的工作
Consumer(index)
創建到kafka
broker的連接,KafkaClient(host,port)
指定消費參數構建consumer,SimpleConsumer(topic,partitions)
設置消費offset:consumer.seek(offset,0)
While(true)
{
消費指定topic第index個分區的數據
處理
}
記錄當前消息offset(可選)
提交當前offset(可選)(kafka集群默認做兩個操作Java客戶端)
組消費模型
組消費可以復制消費,kafka集群的一條消息會同時發送給各個組消費者,每組都會拿到一個全量的數據。
組消費偽代碼:
Main()
設置需要創建的流數N
---每個consumer組里有多少個consumer實例
For
index = 0 to N
Create
thread comsumer(index)
第index個線程(進程) –每個消費者實例的工作
Consumer(index)
創建到kafka
broker的連接,KafkaClient(host,port)
指定消費參數構建consumer,SimpleConsumer(topic,partitions)
設置從頭消費還是從最新的消費(smallest或largest)offset
While(true)
{
消費指定topic第index個流的數據
處理
}
(offset自動提交給zookeeper)
Consumer分配算法:
假設一個組消費者包含兩個消費者實例,某一topic擁有4個分區,則如何進行consumer分配?
針對kafka集群下的某一topic的所有分區進行排序,part0.part1,part2,part3
針對客戶端實例進行排序consumer1
consumer2
用partition數量除以consumer數量N
將I
* N to? (i+1) * N傳遞給消費者consumer(i)
將當前分配關系注冊到集群
For
each topic T that Ci subscribes to
let
PT be all partitions producing topic T
let
CG be all consumers in the same group as Ci that consume topic T
sort
PT (so partitions on the same broker are clustered together)
sort
CG
let
i be the index position of Ci in CG and let N = size(PT)/size(CG)
assign
partitions from i*N to (i+1)*N - 1 to consumer Ci
remove
current entries owned by Ci from the partition owner registry
add
newly assigned partitions to the partition owner registry
(we
may need to re-try this until the original partition owner releases
its
ownership)
消費模型對比
分區消費模型更加靈活,但需要自己處理各種異常情況
需要自己管理offset以實現消息傳遞的其他語義—至少一次(kafka)發送至少一次,不會丟;
至多一次,只會發送一次,可能丟消息,可能會重復
準確一次(保存偏移量):
組消費者模型簡單但不靈活:
不需要自己處理異常情況,不需要自己管理offset
只能實現kafka默認的最少一次消息傳遞語義
Maven-assembly-plugin
Jar-with-dependencies
Kafka參數調優
FetchSize從服務器獲取單包大小 –每次從服務器端獲取的tcp大小
bufferSize
kafka客戶端緩沖區大小 –一次最多獲取多少數據再返回給用戶 一個bufersize由多個fetchsize組成 異國傳輸帶寬不一,盡可能設置大客戶端緩沖區
group.id分組消費時分組名--分組消費時指定不同的id可以實現復制消費的目的
生產者編程模型
同步生產模型
Kafka客戶端發送一條消息后處于阻塞狀態,等待kafka服務器的確認消息,如果沒有等到確認消息(等了一段時間),則生產線程sleep一段時間后,繼續重新發送,直到到達最大發送次數,直接結束發送。
Kafka至少一次 否則程序會中止
異步生產模型
Kafka客戶端發送一條消息,直接將該消息發送至客戶端的緩沖隊列中,直到達到客戶端的緩沖隊列消息數目達到預先配置的數目或者kafka消息隊列的消息累計時間到了預先配置的時間,kafka生產者客戶端的 消息隊列打包一次性發送給kafka服務器,kafka客戶端消息隊列也是存在kafka集群,并不是kafka
Server.? Kafka集群代碼包括kafka
Server和kafka
Client,維持一個隊列。
偽代碼描述
Main()
創建到kafka
broker的連接,KafkaClient(host,port)
選擇或者自定義生產者負載均衡算法partitioner
設置生產者參數
根據負責均衡算法(默認hash輪詢? 隨機) 和 設置得生產者參數(緩沖隊列長度累計時間等等)構造producer對象
While
true
getMessage從上游獲得一條消息
按照kafka要求的消息格式構造(kafka自定義的)kafka消息
根據分區算法得到分區
發送消息
處理異常
兩種生產模型對比
同步:
低消息丟失率
高消息重復率(等待時間較長,由于網絡原因,確認長時間未收到,導致多次重發)
高延遲
異步生產模型:(每秒一個分區50萬記錄)
低延遲
高發送性能
高消息丟失率(無確認機制,發送端隊列滿時,無法傳送消息。 隊列發送kafka服務器期間,可能造成整個消息隊列的丟失。)
生產者編程
req_acks:發送失敗次數
ack_timeout:未接到確認,認為發送失敗的時間
async:是否異步發送
batch_send_every_n:異步發送時,累計最大消息數
batch_send_every_t:異步發送時,累計最大時間
同步需要設置ack參數--默認為同步傳輸? 不指定發送類型的情況下
在發送消息時需要制定topic
key(可能根據key進行partition)以及value
即便自己的分區算法未使用到仍然需要制定key不能將其設置為null或者“”“”
Proprs.put(“serializer.class”,”kafka.serializer.StringEncoder”)
//默認encoder
–字節序列化
Proprs.put(“partitioner.class”,”kafka.proceducer.partition.SimplePartitioner”)
//自己實現的分區算法? 默認的是針對key進行hash
//
kafka.proceducer.partition.SimplePartitioner為自己實現的實現了Partitioner的接口
實現了partition方法,其參數為key和分區數(當前topic的分區數,系統調用時,自動傳參)key來自于EventKey(傳遞的Message中的key)
Proprs.put(“request.required.acks”,”1”)
//
0絕不等確認,1:leader的一個副本收到這一消息,并發回確認-1:leader的所有副本收到這一消息,并發回確認
KeyedMessage
data = new KeyedMessage(eventTopic, eventKey,
eventvalue)
//
eventKey必須有(即便自己的分區算法用不到這個key,也不能為null或者””),否則自己的分區算法根本得不到調用,
異步不需要Proprs.put(“request.required.acks”,”1”)設置了也沒有用? 需要制定類型為異步
Props.put(“producer.type”,”async”)
// 1:async? ? 2:sync
Java客戶端參數調優
Message.send.max.retries發送失敗重試次數? 同步
Retry.backoff.ms:未接到確認,認為發送失敗的時間? 同步
Producer.type:同步發送或者異步發送
Batch.num.messages:異步發送時,累計最大消息數
Queue.buffering.max.ms:異步發送時,累計最大時間
Kafka消息組織原理
磁盤重認識
根據數據的局部性原理kafka
預讀或者提前讀
讀取當前字節 讀取下一塊數據
合并寫
多個邏輯上的寫操作 合并成一個大的物理寫操作
順序讀寫 不需要尋道時間
很少旋轉時間
一般而言 順序寫比 隨機寫? 相差很多(這是kafka設計的關鍵考慮)速度相差萬倍 線性每秒300M隨機寫每秒50k
Kafka特性:gree
Kafka消息的寫入原理
一般的將數據從文件傳到套接字的路徑;
Os將數據從磁盤讀取到內核空間中的頁緩存
應用將數據從內核空間讀到用戶空間的緩存中
應用將數據寫回到內存空間的套接字緩存中
Os將數據從套接字緩存寫到網卡緩存中,以便數據經網絡發出
以上效率很低,經歷了四次拷貝(磁盤空間到內核空間,內核空間到用戶空間,用戶空間到套接字空間,套接字空間到網卡空間;)和兩次系統調用(磁盤到內核空間;套接字到網卡空間)。
使用sendFile(linux系統調用)FileChannel.transferTo
api,兩次拷貝可以被避免:允許os將數據直接從頁緩存發送到網絡上(保留1/4)。優化后,只需要最后一步將數據拷貝到網卡緩存中。
數據寫入和讀出的Byte
Zero Copy
生產:
網絡------------------------
pagecache(生產一次消費多次)---------------------------磁盤
消費:
磁盤-------------------------網絡
以上兩種都沒有使用用戶空間
Kafka設計目標是生產一次消費多次? 生產的時候寫入pagecache消費者可以從pagecache中直接讀取? 不需要再磁盤讀取(順序讀雖然快但還是比不上主存的讀取)。
Pagecache滿了或者存儲的時間到了,寫入進磁盤可以增加整體開發的性能
以上設計 就是數據寫入和讀出的零字節拷貝Byte
Zero? Copy(不用拷貝到用戶空間,所以稱為零字節拷貝)
基于的原理:
順序寫比隨機寫快很多
數據的零字節拷貝
不用拷貝到用戶空間中
Isr:1,0
處于同步中的broker
不在同步中broker:
假如當前有兩個broker
broker0與broker1假設broker0掛了則broker0為不在同步中
假設slave的消費進度比leader的消費進度 快很多? 超過了某個閾值 這個時候也是不在同步中slave中的數據不是最新的數據 假如leader掛了 數據不是最新的? 不會選擇該slave作為新的leader會默認選擇正在同步中的broker為leader即數據達到了最新 數據一致性可以保證
Kafka消息文件存儲
每個分區都是以index
log文件進行存儲的
Kafka消息的刪除原理
從最久的日志段
開始刪除? (按日志段為單位進行刪除),然后逐步向前推進,知道某個日志段不滿足條件為止,刪除條件:
滿足給定條件:(kafka/config/server.properties) 一般默認保留7天
Predicate配置項:log.retention.{ms,minutes,hours}和log.retention.bytes指定
駐留時間 每條消息存放在kafka里的最長時間
消息的
最大字節數(某個分區最大存儲空間)? 某一分區非常大,
不能是當前激活日志段(盡管日期過了7天,但是僅有的當前日志段為當前激活日志段,不會被刪除。)kafka保證topic當前有一個日志段
大小不能小于日志段的大小(log.segment.bytes配置,這種情況只存在于當前激活日志段,因為是一個日志段寫滿后 才會繼續寫入后續的日志段)
要刪除的是否是所有日志段,如果是,直接調用roll方法進行切分,kafka至少保留一個日志段
Scheduler:
刪除log.retention.check.interval.ms指定間隔
刷盤log.flush.scheduler.interval.ms
記錄checkpoint
log.flush.offset.checkpoint.interval.ms
壓縮(如果有)一直運行(log.cleaner.enable指定是否開啟)
Kafka消息檢索原理– 消費時快速定位
Kafka消息的segment
file的組成和物理結構
--(.log
file)
分區文件=
log文件+
index文件
Segment
file? =? Seq[Message]
Message
= 8 byte Offset (當前partition的第幾條消息)+
4 byte message size(Meaasge大小)
+ 4 byte CRC32(CRC32校驗)+
1 byte “magic” (本次發布kafka服務程序協議版本號)
+ 1 byte “attributes”(獨立版本,或標識壓縮類型或編碼類型)+
4byte key length(key的長度,當key為-1時,K
byte key字段不填)+
K byte key(Key可選)+
value bytes length(實際消息數據)+
payload (實際的消息)
Partition
file存儲方式
Segment
file結構
為加快kafka檢索、消費以及生產速度,以log文件保留消息,并通過index文件保存,首先根據索引的offset(是當前分區的第ofset個消息),根據分段log文件的消息條數范圍,進行二分查找,確定當前offset消息所在的分段log。然后根據index的索引(二分索引)(index的key,value中的key指的是當前分段的第key個消息,value為當前分段的第key個消息對應于當前分段的字節偏移量),進行二分查找,如果正好匹配,則直接讀取對應字節偏移量的消息,否則,在相近的二分查找所找到的索引出的字節偏移量后續進行順序讀知道找到知道消息個數偏移量的消息。
查找第368776條消息(二分查找根據消息偏移量確定分段文件、二分查找根據分段索引確定相對于當前分段的字節偏移量)和第368774條消息(二分查找根據消息偏移量確定分段文件、二分查找+順序讀取? 根據分段索引確定相對于當前分段的字節偏移量)
以讀取offset=368776的message為例,需要通過下面2個步驟查找:
第一步查找segment
file;
以上圖為例,其中00000000000000000000.index表示最開始的文件,起始偏移量(offset)為0.第二個文件00000000000000368769.index的消息量起始偏移量為368770
= 368769 +
1。只要根據offset二分查找文件列表,就可以快速定位到具體文件。當offset=368776時定位到00000000000000368769.index|log
第二步通過segment
file查找message;
算出368776-368770=6,取00000000000000368769.index文件第三項(6,1407),得出從00000000000000368769.log文件頭偏移1407字節讀取一條消息即可
Kafka消息的index
file的組成和物理結構(.index
file)
Kafka集群維護
增加一個topic,針對某個topic進行擴展分區,
實時獲取kafka集群信息
Topic工具
列出集群當前所有可用的topic
Kafka-topic.sh
–list –zookeeper zookeeper_address
查看集群特定的topic
Kafka-topic.sh
–describe –zookeeper zookeeper_address –topic topic_name
Ctrl
+ R –
創建topic
Kafka_topic.sh
–create –zookeeper zookeeper_address –replication-factor 1
–partitions 1 –topic topic_name
Kafka-topics.sh
–zookeeper zookeeper_address –alter –topic topic_name
–partitions 4修改partitions數量
Kafka集群leader平衡機制
Leader下線 上線時不平衡
每個partition的所有replicas叫做“assigned
replicas”,“assigned
replicas”中第一個replicas叫做“preferred
replica”,剛創建的topic一般“preferred
replica”是leader。
集群leader手動平衡:
Kafka-preferred-replica-election.sh
–zookeeper zookeeper_address
自動平衡設置:
Auto.leader.balance.enable=true
Kafka集群分區日志遷移
主要有兩種情況:
某個topic全部遷移一個機器移動到另外一個機器
某個topic的某個分區移動
遷移topic數據到其他broker,以下四步:
寫json文件文件格式:cat
topics-to-move.json
{“topics”:[{“topic”:”foo1”},{“topic”,”foo2”}],”version”:1}記錄遷移的topic列表
使用–generate生成遷移計劃kafka-reassign-partitions.sh
–zookeeper zookeeper_address –topics-to-move-json-file
topics-to-move.json –broker-list “5,6” –generate僅僅是生成計劃 沒有執行數據遷移(會生成一個新的分區json文件可以寫入expand-cluster-reassignment.json命名隨意)
使用-execute執行計劃kafka-reassign-partitions.sh
–zookeeper zookeeper_address –reassignment-json-file
expand-cluster-reassignment.json –execute執行前最好保存當前分配情況,以防出錯回滾
使用-verify驗證是否已經遷移完成kafka-reassign-partitions.sh
–zookeeper zookeeper_address –reassignment-json-file
expand-cluster-reassignment.json
–verify遷移某個topic的某些特定partition數據到其他broker,步驟同上,但json文件如下:cat
custom-reassignment.json
{“partitions”:[{“topic”:”foo1”,”partition”:0,”replicas”:[5,6]},{
“topic”:”foo2”,”partition”:1,”replicas”:[2,3]}]}
?可以指定到topic分區編號?
集群分區日志遷移和leader平衡機制在實際的kafka集群運維中非常重要
kafka-reassign-partitions.sh工具會復制磁盤上的日志文件,只有當完全復制完成,才會刪除遷移前磁盤的日志文件。執行分區日志遷移需要注意:
kafka-reassign-partitions.sh粒度只能到broker(機器),不能到broker的目錄(如果broker上面配置了多個目錄,是按照磁盤上已駐留的分區數來均勻分配的),如果topic之間的數據或topic中partition之間的數據本身就不均勻,很有可能造成磁盤數據的不均勻。
對于分區數據較多的分區遷移數據會花大量時間(經過網絡傳輸),topic數據較少或磁盤有效數據較少時進行數據遷移
進行分區遷移時,最好先保留一個分區在原來的磁盤上,這樣不會影響正常的消費和生產,如果目的是將分區5(broker1,5)遷移到broker2,3.可以先將5遷移到2和1【1作為軸,沒有變,確切是將5遷移到2】;再將2和1遷移到2和3【首先使用leader平衡工具,將2和1中的作為分區的leader,將數據從1遷移到broker3】.而不是一次將1和5遷移到2和3.
Kafka集群監控
Kafka
Offset Monitor –監控當前的kafka集群有哪些機器存活哪些topic隊列生產者消費者積壓了多少數據? 監控一個集群
Kafka
Manager? Yahoo開源的監控工具? 監控多個集群
(另有JMX)
Kafka
Offset Monitor:
當前存活的broker集合
當前活動topic集合
消費者組列表
當前consumer按組消費的offset
lag數(當前topic當前分區目前有多少消息積壓而沒有及時消費)
部署kafka
offset Monitor github的一個KafkaOffsetMonitor的jar包
Java
–cp (class package )KafkaOffsetMonitor-assembly-0.2.0.jar
com.quantifind.kafka.offsetapp.OffsetGetterWeb –zk zk-01,zk-02
–refresh 5.minutes –retain 1.day &? (刷新幾次 駐留時間)引用gogle的api必須翻墻? 一次即可
Kafka
Manager由雅虎開源:(安裝要求能上網還要求能翻墻)
管理幾個不同的集群
檢查集群狀態(topics,brokers,副本的分布,分區的分布)
選擇副本進行查看
基于集群的當前狀態產生分區分配
重新分配分區
安裝sbt
scala-sbt配置環境變量
安裝kafka-manager下載 編譯
部署 修改配置文件
下載打包好的kafka-manager解壓后修改配置
Kafka服務器模型和leader選舉機制
Hadoop
netty? redis? mina服務器 常使用的reactor模型leader會使用zookeeper選舉kafka不是使用zookeeper選舉因為會存在一系列的問題
Kafka核心源碼分析
設計和技巧
Kafka消費者源碼
分區消費模式源碼:
分區消費模式
服務器端源碼:
通過findLeader創建PartitionMetadata從kafka服務器端獲取消息,需要分區、leader的元數據,通過findLeader服務器端提供的函數,接收一個host、port,獲取對應的分區。其分區、備份數據,構造一個PartitionMetadata。
通過PartitionMetadata創建一個SimpleConsumer,分區消費模式使用的是SimpleConsumer
SimpleConsumer創建完成之后,可以進行消費,構建FetchRequest對象--包括所要獲取的topic、partition、offset等組成的數據結構,發送給服務器,服務器根據所要了解的這些key,獲取返回的值,發送這些結果給client。
發送FetchRequest,如果沒成功,可能是我們請求的那個broker掛了等原因,回到第一步創建MetaData,如果發送成功,服務器端回復一個響應
接收服務器端發送的FetchResponse響應,(message、offset)
迭代的處理消息(message,offset)
再去構造一個FetchRequest再進行消息的獲取
服務器端發送的消息是以消息組或消息塊的形式發送給我們,發送一個的消息后,會在塊內進行迭代,迭代完成后,再構建FetchRequest。一次發送一個塊,能夠使用盡量少的交互獲取盡量多的消息。
分區消費模式直接由客戶端(任何高級語言編寫)使用Kafka提供的協議向服務器發送RPC請求獲取數據,服務器接收到客戶端的RPC請求后,將數據構造成RPC響應,返回給客戶端,客戶端解析相應的RPC響應獲取數據。
Kafka支持的協議:
獲取消息的FetchRequest和FetchResponse發送請求 發回響應
獲取offset的OffsetRequest和OffsetResponse單獨發送一個請求返回某一個offse
提交offset的OffsetCommitRequest和OffsetCommitResponse
low-level的api中,提交某個offset,發送OffsetCommitRequest獲取OffsetCommitResponse
獲取Metadata的Metadata
Request和Metadata
Response metadata(topic的某個分區,leader是誰,還有多少分區,當前服務器還有多少broker)
生產消息的producerRequest和ProducerResponse生產消息時,服務器端會構造一個producerRequest,發送給broker,broker回復一個producerResponse,
組消費模式源碼:
通過ConsumerConfig對象創建配置,通過ConsumerConfig傳遞配置參數,(zk地址,fetch取數據創建線程,buffer參數)
根據配置ConsumerConfig中的相關配置創建ConsumerConnector(實際上創建的是zkConsumerConnector)
通過ConsumerConnector,創建一個KafkaStream流,kafkaStream流封裝了流式消息 ,類似于java的輸入輸出流
基于KafkaStream創建一個消費者迭代器ConsumerIterator獲取消息進行消息的處理
如果服務器無新的消息就會進行阻塞,知道有新的消息進入處理消息。Iterator
next hasNext
通過配置創建ConsumerConfig
基于配置建立ZookeeperConsumerConnector連接,創建ZookeeperConsumerConnector對象
一下分為兩方面,第一:
創建ConsumerFetchManager獲取消息通過ConsumerFetchManager
ConsumerFetchManager創建ConsumerFetchThread可以創建多個線程可配置
ConsumerFetchThread發送FetchRequest獲取消息
服務器端 將返回的消息填充至FetchDataChannel隊列 將隊列封裝成FetchResponse響應? 獲取FetchResponse填充數據到KafkaStream獲取流式數據,在流式數據上獲取迭代器,通過迭代器獲取消息
仍然通過發送FetchRequest
RPC請求獲取消息,由服務器端幫忙處理,不需要客戶端的操作? 分區消費模式需要我們自己發送FetchRequest
組消費模式也會自動提交offset
另一方面:
創建Scheduler,定式調度的任務,調度的頻率可通過參數配置
定期調用autoCommit
Offset向Zk提交offet把當前用戶消費過的offset更新在zk對應的節點并返回 下一次獲取的時候讀取zk的配置即可
兩種消費模式服務器端源碼對比:
分區消費模式:
指定消費topic、partition、Offset通過向服務器發送RPC請求進行消費
需要自己提交offset發送offset
commit Request提交offset;將offset寫到zk對應的目錄
需要自己處理各種錯誤,如leader切換錯誤
自己處理消費者負載均衡策略在FetchRequest中指定需要消費的topic和對應分區需要用戶、客戶端自己處理負載均衡
組消費模式:
通過向服務器發送RPC請求完成的 和分區消費模式一樣
組消費模式由kafka客戶端處理各種錯誤(如leader切換錯誤),然后將消息放入隊列再封裝為迭代器(隊列為FetchedDataChunk對象),客戶端只需要在迭代器上迭代取出消息。
由kafka服務器端周期性的通過scheduler提交當前消費的offset,無需客戶端負責。分區消費模式則需要自己來管理和提交
Kafka服務器端處理消費者負載均衡策略
監控工具Kafka
Offset Monitor和KafkaManager均是基于組消費模式
盡可能使用組消費模式,除非需要:
自己管理offset,(為了實現消息投遞的其他語義 至多一次? 準確一次)
自己處理各種錯誤(根據業務需求)? 分區消費模式和low-level
api
Kafka生產者源碼介紹:
同步發送模式源碼:
創建producer對象 傳遞一些配置? 如brokerlist
ack等等
創建發送者線程
根據brokerId和partition選擇SyncPool發送消息在服務器端維護一個線程池 從線程池中選擇一個線程
選擇好一個發送線程后 構造ProducerRequest請求 發送實質的消息
按照用戶執行的序列化函數序列化消息
用戶傳遞的消息可能是字符串或其他的形式,需要將其序列化為二進制
序列化函數可為用戶定義? 默認兩種:字符串序列化為字符數組
對象序列化為字節數組
發送ProducerRequest請求? 檢查發送是否出錯(所選擇broker
leader切換)
如果無錯誤接受ProducerResponse響應
按照client配置發送ack信息
如果有錯誤
是否超過最大出錯次數 如果是發送出錯? 如果沒有繼續重試
異步發送源碼:
Producer異步發送至客戶端段的一個隊列中,隊列中的消息沒有達到一定數量或超過一定時間,producer消息一直存放在該隊列中,達到一定數量后或者積累時間超過配置,
會啟動一個發送線程,發送線程會通過輪詢讀取消息,并將這些消息批量發送至Kafka
Cluster
在發送消息之前會在Producer
Pool中根據partition尋找一個SyncProducer
創建producer
創建發送者線程(同樣是從創建的同步發送池中獲取的同步發送線程)
消息放入隊列
消息數達到或者時間達到?
是的話
根據brokerId和Partition選擇SyncPool(kafka消息都是通過leader進行讀寫的)
按照用戶執行的序列化函數序列化消息
構造ProducerRequest請求RPC請求 同步會一條一條發送消息和ack異步是批量發送數據給broker
發送ProducerRequest請求
接收ProducerReponse響應
發送成功 –繼續接收producer發送的消息
不管是同步還是異步都是取出一個同步發送線程,最后都是同步過程,只是異步的時候會先將消息發送至隊列里,當消息數達到一定數量和時間達到配置時發送。所以同步發送和異步發送后半部分都是一致的。
同步發送和異步發送最后都是通過構造RPC請求進行發送的,
同步發送服務器端會向客戶端發送一個確認,異步發送服務器端不會發送ACK。
兩種模式服務器端源碼介紹:
同步發送模式具有幾下特點:
同步的向服務器發送RPC請求進行生產,(所謂同步就是每條消息一個請求一個ack)
發送錯誤可以重試
可以向客戶端發送ack(可以配置不發送)
異步:
最終也是通過向服務器發送RPC請求完成的(和同步發送模式一樣),異步發送的是消息段批量發送
異步發送模式先將一定量消息放入隊列中,待達到一定數量后再一起發送。
異步發送模式不支持發送ack,但是Client可以調用回調函數獲取發送結果。
性能高用異步,準確性用同步。
Kafka
Server Reactor設計模式
大量的連接數 高并發Reactor模型基于Java
NIO進行設計,是對Linux
epoll模型進行改造,
Reactor模型:
Java
NIO:
Java
1.4引入的,NIO比較成熟,Java1.3之前使用的是原生態的打開文件、讀取文件的方式(比較慢,不方便)
New
io全新的方式讀取文件 套接字
Java
NIO:
Channels:java中的流
Buffers:緩沖區 存放二進制的緩沖區 類似于數組
Selectors:解決并發網絡套接字
Channel通道和Java中的Stream一樣,用于傳輸數據的數據流,數據可以從Channel讀到buffer中,也可以從buffer寫到Channel中。
Selector允許單線程處理多個Channel。使用Selector,首先得向Selector注冊Channel,然后調用它的select方法。此方法會一直阻塞到某個注冊的Channel有時間就緒。一旦這個方法返回,線程可以處理這些事件,事件的例子如新連接進來,數據接收等。
Linux
epoll模型:
Epoll是一種IO多路復用技術,在Linux內核中廣泛使用。常見的三種IO多路復用技術為select模型、poll模型和epoll模型。
Select模型:輪詢所有的套接字查看是否有時間發生(套接字需要注冊在select上,select向注冊的套接字進行輪詢,查看是否有事件發生)
缺點:套接字最大1024個主動輪詢效率低(對所有的套接字不斷地輪詢) 事件發生后需要將套接字從內存空間拷貝到用戶空間效率低
Poll模型和select模型類似,修正了select模型最大套接字限制
Epoll模型:修改主動輪詢為被動輪詢,當有事件發生時,被動接收通知。所以,epoll模型注冊套接字后,主程序可以做其他事情,當事件發生時,接收到通知后再去處理。修正了select模型的三個缺點,(第三點使用共享內存修正)共享內存:內核空間和用戶空間共同使用的內存空間。
Epoll模型為Linux系統作為服務器提供很大的支持。
Java
nio叫做select模型,底層使用的是epoll模型。
Java
nio叫做select模型指的是選擇哪一個Channel的意思。
Kafka
Server Reactor模型:處理大量連接? 大量套接字 不同套接字 不同的行為
Kafka
SocketServer是基于Java
NIO開發的,采用Reactor的模式(已被大量實踐證明非常高效,在Netty和Mina中廣泛使用 網絡開發的框架 大量使用Reactor模型處理高并發大量連接)。Kafka
Reactor的模式包括三種角色:
Acceptor:接收注冊Channel
Procesor:接收從Select傳回的Channel然后將Channel注冊到Handler
Handler:Handler處理用戶邏輯
Kafka
Reactor包含了一個Acceptor負責接受客戶端請求,N個Processor線程負責讀寫數據(為每個Connection創建出一個Processor去單獨處理,每個Processor中均引用獨立的Selector—Processor下也有Selector需要注冊Channel),M個handler來處理業務邏輯。在Acceptor和Processor,Processor和Handler之間都有隊列(緩沖、解耦)來緩沖請求。
Kafka
Server Reactor模型:(高并發 的核心Hbase
Storm? Hadoop? Redis服務器端
大量使用)? (大量的連接 每個連接處理大量的事件)
客戶端發起連接
KafkaSocketServer接收連接? 通過Acceptor接收連接注冊Selector創建N個Processor處理業務 每個Processor對應一個客戶端連接? 為Processor注冊一個Selector
Selector注冊一個Channel,查看對應的連接上是否有讀數據和寫數據
Acceptor注冊Selector給Processor
Processor注冊Selector給Handler
Acceptor主要職責是監聽客戶端的連接請求,并建立和客戶端的數據傳輸通道,然后為客戶端指定一個Processor,它的工作到此結束。接著響應下一個客戶端的連接請求:
Processor主要職責是負責從客戶端讀取數據和將響應返回給客戶端,本身不處理業務邏輯,每個Processor都有一個Selector,用來監聽多個客戶端,可以非阻塞地處理多個客戶端的讀寫請求,Processor將數據放入RequestChannel的RequestQueue和從ReponseQueue讀取響應。
Handler(kafka.server.KafkaRequestHandler
kafka.server.KafkaApis)的職責從RequestChannel中的RequestQueue取出Request,處理以后再將Response添加到RequestChannel中的ResponseQueue
Kafka
Partition Leader選主機制
大數據常用的選主機制:
Leader選舉算法很多
Zab:Zookeeper使用 (選主 同步隊列? 復制鎖)
他們都是Paxos算法的變種--大數據領域非常著名的算法
Zab協議四個階段:
Leader
election:leader選舉
Discovery(epoch
establish):發現 版本建立
Synchronize(sync
with followers):從leader同步數據和狀態
Broadcast:leader廣播狀態和數據到follower
Zk和raft
posx都會首先選擇自己為leader然后進行同步協商;另一個特點:半數以上的服務器認為是leader,則同一其為leader。(少數服從多數)
先選自己為leader,然后選擇一個編號大的為leader,然后半數贊同為leader,則選舉其唯leader。
一般來說第二個啟動的節點就是leader
Zk是奇數的,負載不是很高,處理元數據。
Raft:
在Raft中,任何時候一個服務器可以扮演下面角色之一:
Leader:處理所有客戶端交互,日志復制等,一般只有一個Leader
Follower:類似選民,完全被動
Candidate:候選人可以被選為新領導者
Zk中每個人都可以成為leader
raft必須從候選人里選擇,候選人是由系統管理員配置的
啟動時集群中制定一些機器為候選人candidate,然后candidate開始向其他機器(尤其是Follower)拉票,當某一個Candidate的票數超過半數,它就成為leader。
Kafka集群將元數據存儲在zk
Kafka集群依賴zk集群,所有follower都在zookeeper上設置一個watch,一旦leader宕機,其對應的ephemeral
znode臨時節點會自動刪除,此時所有的Follower都嘗試創建該節點,創建成功者(Zookeeper保證只有一個能創建成功)即是新的leader,其它replica即成為follower
Split-brain(腦裂):由zookeeper特性引起,雖然zookeeper能保證所有watch順序觸發,但并不能保證同一時刻所有repilca“看”到的狀態是一樣的(可能由于網絡原因),可能造成replica的響應不一致 可能會產生多個leader
Herd
effect(羊群效應):如果宕機的那個broker(leader)上的partition比較多,會造成多個watch(follower)被觸發,造成集群內大量的調整。
Zookeeper負載過重:每個replica都要為此在zookeeper注冊一個watch,當集群規模增加到幾千個partition時zookeeper負載會過重
主要用來處理數據量比較小的,比如HDFS的HA切換HA的選舉,用于通信量比較小 數據量比較小的選舉
像kafka大數據量的傳輸HDFS大數據量的傳輸不使用zk
raft進行選舉? 大量通信的開銷kafka和HDFS的大量數據的傳輸 很容易造成元數據通信消息的消失? 網絡丟包等缺點
Kafka使用自己的選主機制:
Kafka的Leader
Election解決以上問題,在所有broker中選出一個controller,所有partition的leader選舉都由controller決定。Controller會將leader的改變直接通過rpc的方式(比zookeeper
queue方式更高效)通知需要為此做出相應的broker。
controller的選舉過程:以broker為單位使用zookeeper進行選舉而不是之前那種以partition為單位使用zookeeper進行選舉量太大了
kafka
partition leader選舉過程有controller執行:從zk中讀取當前分區的所有ISR(in-sync
replicas)集合(心跳機制保持連接且同步數據中數據記錄相差不超過10000) 調用配置的分區選擇算法選擇分區的leader(基本都會選擇ISr中的第一個即Prefer分區作為leader) 當前包括五種選擇算法
離線處理:
Flume
-? kafka? - storm、spark
– mongodb(數據入庫)/hbase/redis
Elasticsearch
drill? impala? kylin
常用選主機制的缺點:
Kafka
Partition選主機制:
Kafka生產者源碼
Kafka
Server Reactor設計模型
KafkaPartition
Leader選舉機制
Kafka回顧:
Kafka業務場景
接觸耦合:
增加冗余:規避數據丟失風險
提高可擴展性:解耦了處理過程
增大消息入隊和處理的頻率 – 額外增加處理的過程即可
Buffering:任何一個重要的系統需要處理不同的時間元素? 消息隊列通過緩沖層幫助任務高效執行 寫入隊列的處理會盡可能的快速,該緩沖有助于控制和優化數據流經系統的速度
異步通信:異步的非阻塞發送對于擴展消息系統來說
是一個基本的特性
應用場景:
Push
Message:消息推送? 使用kafka作為消息系統的核心中間件完成消息的生產和消費
Website
Tracking:網站跟蹤
日志收集中心:kafka的push
pull適合異構集群,適合批量提交消息,對于生產端來說,性能方面沒有消費。消費段;Hadoop離線分析storm實時分析
實時統計平臺搭建注意事項:
HA特性:分布式計算分布式存儲kafka有HA特性
核心文件配置:
啟動步驟:先啟動zookeeper再啟動kafka
Kafka
Project Process
Data
Collection ->? Data? Access? -> Stream Computing ->
Dataoutput
Flume
kafka? ? storm? ? Redis/Mysql(持久化)
Kafka
Producer:Flume
cluster ------ Sink ---------》 (producer)KafkaCluster
Kafka
Consumer:Kafka
Cluster ----------KafkaSpout? ------------》Storm
Cluster
Kafka工程準備:kafka監控工具stormui管理界面
基礎環境準備:
Producer:服務器數據------
flume agent代理節點-------Sink到Kafka集群
Consumer:kafka集群-------通過kafkaSpout輸送到Storm集群----Storm集群--------通過Storm集群進行實時計算,并將結果持久化到DB庫中 (MySQl、Redis)