想象下有這樣的對話.html
你: 什麼是Apache Kafka?
我: Apache Kafka是發佈-訂閱消息系統,分佈式的提交日誌
你: …什麼?
我: 是的,它是一個分佈式的,分區的,複製的提交日誌服務
你: 你到底在說什麼?java
上面的描述(我)是正確的. 你只須要知道這些術語是什麼意思, 可是若是你不知道這些術語,就會感到很困惑.web
那就讓咱們以另一種方式來解釋吧. 我喜歡經過例子來學習, 而且在學習的時候經過和我已經知道的東西互相比較,
我發現這種學習方式很是有幫助. 那麼咱們就以這種舉例子,而且比較的方式來描述什麼是Kafka吧.算法
我會給一些例子來講明Kafka能幹什麼, 比較的對象是不少人都熟悉的: 命令行的Unix管道數據庫
看一個簡單的例子:apache
1 |
$ cat *.txt | tr A-Z a-z | grep hello |
這段腳本找出以.txt結尾的文件中全部包含單詞」hello」的行.它包含了三個步驟/階段:服務器
全部這些步驟的每個都寫到標準輸出流,後面的階段會從標準的輸入流中讀取.網絡
最簡單的來看, Kafka就像一個Unix的管道: 你將數據寫到它的一端, 而後數據從另外一端出來.
(嚴格來講,你寫的數據會經過網絡傳輸,你讀取的數據也是經過網絡,不過如今咱們暫時忽略這些.)分佈式
若是這就是Kafka所能作的,那有什麼了不得的,對吧?實際上Kafka還有一些額外的特徵,帶來新的能力.wordpress
Unix的管道在文本數據行之間流動,一般是以新的一行爲結束(這條管道). 這些行能夠很長,可是工做單元仍然是一行文本.
若是你處理的不是ASCII數據,或者你處理的數據不能以一一行來表示就會有點麻煩. 而Kafka支持任意的格式和任意大小.
這就容許你能夠存儲任何數據到Kafka中: 文本,CSV,二進制數據,自定義編碼數據等等. 對於Kafka而言,它只是一系列的
消息,其中每條消息都是一系列的字節. 好比能夠(模擬)寫一個Kafka的」命令行」:
1 |
$ TwitterFeed | filter_tweets From @apachekafka |
這裏的filter_tweets命令可能不是一個簡單的基於字符串的grep,而是一種可以理解從TwitterFeed輸出的數據格式.
好比TwitterFeed可能輸出JSON,則filter_tweets須要作些JSON的處理.TwitterFeed若是返回的是二進制數據,
則filter_tweets須要知道二進制的格式/協議. 這種靈活性可讓Kafka成爲一種發送任何數據類型的Unix管道.
咱們可能有一個複雜的會花費一些時間才能跑完的命令.若是隻運行一次,你可能不關心.可是若是你要屢次迭代運行,
你可能會會將輸出結果先寫到一個文件中, 這樣以後的階段能夠更快地迭代,而不須要從新屢次運行很慢的那部分命令.
1 2 |
$ find . -type f | grep \.java > javafiles.txt $ cat javafiles.txt | xargs grep ClassName |
這個模式工做的很好,可是這意味着你須要提早計劃去作(先寫文件). 若是管道自身可以作這件時間就方便多了.
Kafka會持久化你發送的全部數據到磁盤上.持久化很是方便,不只節省了你的一些時間,它還容許你能作以前不能作的一些事情.
就像上面的命令行同樣,每一個階段的輸出都被保存下來. 因爲第一個階段的輸出被保存了,第二個階段甚至不要求正在運行.
這種方式, Kafka做爲生產者數據和消費者數據之間的緩衝區. 它保持了數據,容許消費者可用而且準備好的時候纔讀取數據.
Kafka是高性能的,它甚至能夠運行在多臺機器上,而且能夠複製統一分數據到多臺機器防止數據丟失形成的風險.
三個Kafka節點組成的集羣可以處理每秒鐘兩百萬的寫入, 並能使網卡飽和.
因爲數據被持久化到了Kafka中,並無要求消費者要多快去讀取數據.消費者能夠想多快就多快,想多慢就多慢地讀取數據.
所以它容許一個高性能的生產者, 並不會由於一個很慢的消費者而江堤生產者的性能. 看一個很慢的消費者的例子.
1 2 |
$ produceNumbers > numbers.txt $ cat numbers.txt | xargs findPrimeFactorization |
從密碼學咱們知道,將一個數字因式分解成質數是很慢的.假設咱們分解了100萬個數字,程序掛掉了.
當下次重啓程序的時候若是可以從上次離開位置的那個點繼續處理,而不是重複不少工做,那就很友好了.
以這個例子中,我指望的是從numbers.txt中的第一百萬零一行開始繼續處理.
Kafka有相似的概念叫作」offset」.Kafka中的每條記錄都被分配了有序的offset,消費者能夠選擇在指定的offset從新開始.
數據持久化和offsets這兩個特性,容許你構建一個消費者數據和生產者數據分開的系統.
數據持久化–很是快的數據持久化–意味着它能很快地吸取大批量的數據.
它容許消費者按照它可以讀取的任何速度讀取數據.它容許持久化數據, 即便消費者掛掉了.
offsets容許消費者繼續執行, 不管它上次在什麼地方退出,而不會重複工做.
在某種狀況下,這是頗有意義的: 你並不想在一次匯款中從銀行帳號中扣了兩次錢.
另外一方面,這是出於效率方面考慮的: 你並不想從新對已經處理的數字從新進行因式分解.
不管哪一種狀況, 這兩個特性都容許你作傳統的Unix管道所不能作的事情.
再看下第一個例子:
1 |
$ cat *.txt | tr A-Z a-z | grep hello |
在這裏例子中,第一個階段(cat)輸出全部的行而後就結束了. 整個管道會找到全部包含單詞」hello」的行最後命令結束.
和下面的命令進行比較:
1 |
$ tail -F *.txt | tr A-Z a-z | grep hello |
這個命令不會結束, 第一個階段(tail)輸出一些行,可是仍然保持着監聽更多的數據.
若是你在以後往其中的一個添加了一行,tail命令會輸出這個新行, 而後接下來的命令會處理它.
Kafka支持相同的概念.數據寫到到Kafka而且被消費者讀取能夠看作一個流.
若是消費者到達數據的末尾, 它會繼續等待即將到來的更多的數據. 當新的數據寫入到Kafka,它會很快地被髮送到消費者.
我在以前說過數據流進Kafka是很快的, 實際上數據從Kafka流出也是很快的.
一條記錄被添加到Kafka後,可以在20ms以內發送給一個正在等待的消費者.
如今咱們知道Kafka除了支持數據持久化,也支持流數據. 咱們複習下以前的例子
1 2 |
$ produceNumbers > numbers.txt $ cat numbers.txt | xargs findPrimeFactorization |
上面的命令看起來向上一種批處理模式,由於produceNumbers最終會結束的.
可是數字是無限的,它永遠不會結束, 因此實際上看起來應該是這樣的:
1 |
$ produceNumbers |* findPrimeFactorization |
這裏我本身造了一個語法: |*
表示這是一個Kafka管道.它可以歸檔全部東西到磁盤,而且發送流式的更新.
streaming updates流式更新, 數據是流式傳入的,下游的方法基於最新的流數據作更新操做. 即對流數據更新操做
這種流式的數據容許你建立一個實時的管道,這裏有個例子:
1 |
$ tail -F /var/log/httpd/access_log |* grep index.html |* get_load_time |* make_fancy_graph |
這個管道會查詢你的web服務器日誌. 它會提取主頁的全部pageload,獲取出頁面加載的時間,建立一個可視化的圖,並及時更新.
太棒了,你剛剛建立了一臺服務器的監控面板. 若是頁面加載時間抖動,你能夠在幾秒內從圖中觀察到.
全部的這些Kafka管道(每一個|*
)都會持久化和緩衝數據. 管道中的任何一個階段均可能出錯,並在任什麼時候候重啓,
而且能夠在它們上次離開的地方繼續. 它們能夠處理的很慢,或者一直牢牢跟着(上一個階段).
或者若是它們落後的太多,能夠被中止,並移到新擁有更快CPU的服務器上,也可以從上一次做業離開的地方繼續.
你還能夠建立一些其餘類型的實時管道:
Kafka同時也支持多個生產者往相同的地方寫數據. 想象下前面的場景,但如今從多個服務器上收集web服務器日誌.
全部的服務器以漏斗形式的數據流入到Kafka管道. 你只有一個grep的進程在運行, 獲取加載時間的進程在運行,
只有一個繪圖的進程在運行. 可是它們是基於全部web服務器的輸出日誌的聚合.恭喜你,如今建立了一個數據中心的監控面板.
這裏的好處是你能夠從不少的地方收集數據, 可是隻在一箇中央的地方存儲並處理全部這些收集到的數據.
Kafka能夠成爲你的公司中全部數據的中心收集節點. 將分散在各個服務器上的數據都收集到統一一個節點.
Kafka不只支持多個生產者寫到同一個地方,也支持多個消費者從相同的地方讀取數據.
再強調一次,Kafka在多個階段之間可以緩衝數據. 上面的三個管道:find_ip_address
, grep index.html
,
get_login_attempts
–都可以按照本身的步伐(消費速度)從access_log這個Kafka管道中讀取數據.
前面兩個看起來會至關快,可是第三個可能會慢點.可是不要緊,Kafka會保持這些數據(不會由於其餘消費者消費了就刪除數據)
這樣的好處是一個單一的數據源可能用不一樣的方式處理,每種使用方式都和其餘方式都是獨立的,而且不會相互影響.
假設咱們找到了一種檢測黑客的方式. 咱們能夠將detect_hackers
實例部署在已有的實例旁(共存),而後一塊兒測試.
對於相同的輸入,看看他們都有什麼不一樣的表現(驗證咱們的新的檢測方式是否達到了預期的效果).
一旦咱們決定選擇使用新的方式會更好點,咱們會通知下游的notify_security
做業監聽更好的檢測方式.
而且新的方式真的很穩定了,咱們能夠將老的檢測方式移除掉.
看看咱們都作了什麼?
這個特性使得Kafka帶給咱們的威力很是大.經過將同一份數據分散到多個地方,咱們能夠從數據中得到多個分組的能力.
每一個管道的工做都是獨立的而且都是以本身的消費速度進行的. 而且讓咱們在開發新的功能時可以重用已經存在的數據.
讓咱們專一於上面多個管道中的其中一個.
假設geoip(地理位置)數據庫是很是慢的. Kafka會在這個階段以前緩衝全部的數據,因此即便很慢,也不會丟失任何數據.
可是查詢geoip會拖慢整個管道的速度. 因此你會部署一個很快速的geoip數據庫. 可是這並不能幫你太多, 由於你每次
都是從find_ip_address的輸出結果中一條接着一條地查詢. 你真正須要的是並行!
Kafka支持在你的Kafka管道中添加子管道(sub-pipes). 你能夠將全部以1結束的IP地址發送到第一個子管道,將全部以
2結束的IP地址發送到第二個管道,等等. 如今你的請求可以經過round-robin的方式發送到數據庫中. 看起來是這樣的:
Kafka管道中的數字0到9表示全部以這個數字結束的IP地址,會被放到相同的管道中(圖中每一個geoip_lookup都是一個子管道)
每一個geoip_lookup做業都只會從find_ip_address管道中讀取一部分數據,能夠容許你以並行的方式查詢:一次10個線程.
這種方式應該能知足你在geoip階段快速地在地球圖形上繪點, 這下你滿意了吧!
Kafka稱全部的這些是partitions
. 它容許你將數據以邏輯的分組方式分到多個通道中,可是每一個函數都是獨立的.
一批數據會分散到多個節點, 每一個節點之間都作一樣的工做. 可是它們之間不會相互影響的.
仔細看看上面的例子,你會發現Kafka的管道這個角色是很小的.Kafka管道並不會作過濾IP地址的工做,不會作查詢IP地址的工做,
也不會對很大的數字作因式分解. 這都取決於你. Kafka作的事情是將你的全部工具都聯繫在一塊兒.這樣看來它就像膠水/粘合物.
可是它這個粘合物可以讓你構建出不少有趣的東西. Kafka負責不少平凡的事情,而這些是做爲事情的解決者的你並不肯意去作的.
它可以幫你保存數據,能在任何一個點開始讀取數據,能夠從多個數據源聚合數據,並將數據同時發送給多個目標.
Kafka這種能力讓你從新思考解決問題的方式. 將一個問題分解成多個階段,每一個階段能夠單獨開發實現,並獨立地測試.
這一切都是基於Kafka能將全部的組件都粘合在一塊兒. 並且Kafka能夠在網絡之間完成這些事情, 因此你甚至能夠將你的計算組件
分佈在多個節點, 也就有了水平擴展, 分佈式處理, 高可用性等特色.
這種將一個大問題分解成多個小問題的思想和Unix的哲學是一致的. 實際上Unix管道的發明人Doug McIlroy這麼說過:
This is the Unix philosophy: Write programs that do one thing and do it well.
Write programs to work together. Write programs to handle text streams,
because that is a universal interface.
Kafka容許你將Unix哲學運用到工程師急待解決的大數據量,低延遲,網絡之間的問題.
在這篇文章中,我簡化了一些事情,如今咱們解釋下以前遺留的東西.
EOF.