真的,kafka 入門看這一篇準沒錯!

程式設計師cxuan發表於2021-01-13

什麼是 Kafka

Kafka 是一個分散式流式平臺,它有三個關鍵能力

  1. 訂閱釋出記錄流,它類似於企業中的訊息佇列企業訊息傳遞系統
  2. 以容錯的方式儲存記錄流
  3. 實時記錄流

Kafka 的應用

  1. 作為訊息系統
  2. 作為儲存系統
  3. 作為流處理器

Kafka 可以建立流資料管道,可靠性的在系統或應用之間獲取資料。

建立流式應用傳輸和響應資料。

Kafka 作為訊息系統

Kafka 作為訊息系統,它有三個基本元件

file

  • Producer : 釋出訊息的客戶端
  • Broker:一個從生產者接受並儲存訊息的客戶端
  • Consumer : 消費者從 Broker 中讀取訊息

在大型系統中,會需要和很多子系統做互動,也需要訊息傳遞,在諸如此類系統中,你會找到源系統(訊息傳送方)和 目的系統(訊息接收方)。為了在這樣的訊息系統中傳輸資料,你需要有合適的資料管道

file

這種資料的互動看起來就很混亂,如果我們使用訊息傳遞系統,那麼系統就會變得更加簡單和整潔

file

  • Kafka 執行在一個或多個資料中心的伺服器上作為叢集執行
  • Kafka 叢集儲存訊息記錄的目錄被稱為 topics
  • 每一條訊息記錄包含三個要素:鍵(key)、值(value)、時間戳(Timestamp)

核心 API

Kafka 有四個核心API,它們分別是

  • Producer API,它允許應用程式向一個或多個 topics 上傳送訊息記錄
  • Consumer API,允許應用程式訂閱一個或多個 topics 並處理為其生成的記錄流
  • Streams API,它允許應用程式作為流處理器,從一個或多個主題中消費輸入流併為其生成輸出流,有效的將輸入流轉換為輸出流。
  • Connector API,它允許構建和執行將 Kafka 主題連線到現有應用程式或資料系統的可用生產者和消費者。例如,關聯式資料庫的聯結器可能會捕獲對錶的所有更改

file

Kafka 基本概念

Kafka 作為一個高度可擴充套件可容錯的訊息系統,它有很多基本概念,下面就來認識一下這些 Kafka 專屬的概念

topic

Topic 被稱為主題,在 kafka 中,使用一個類別屬性來劃分訊息的所屬類,劃分訊息的這個類稱為 topic。topic 相當於訊息的分配標籤,是一個邏輯概念。主題好比是資料庫的表,或者檔案系統中的資料夾。

partition

partition 譯為分割槽,topic 中的訊息被分割為一個或多個的 partition,它是一個物理概念,對應到系統上的就是一個或若干個目錄,一個分割槽就是一個 提交日誌。訊息以追加的形式寫入分割槽,先後以順序的方式讀取。

file

注意:由於一個主題包含無數個分割槽,因此無法保證在整個 topic 中有序,但是單個 Partition 分割槽可以保證有序。訊息被迫加寫入每個分割槽的尾部。Kafka 通過分割槽來實現資料冗餘和伸縮性

分割槽可以分佈在不同的伺服器上,也就是說,一個主題可以跨越多個伺服器,以此來提供比單個伺服器更強大的效能。

segment

Segment 被譯為段,將 Partition 進一步細分為若干個 segment,每個 segment 檔案的大小相等。

broker

Kafka 叢集包含一個或多個伺服器,每個 Kafka 中伺服器被稱為 broker。broker 接收來自生產者的訊息,為訊息設定偏移量,並提交訊息到磁碟儲存。broker 為消費者提供服務,對讀取分割槽的請求作出響應,返回已經提交到磁碟上的訊息。

broker 是叢集的組成部分,每個叢集中都會有一個 broker 同時充當了 叢集控制器(Leader)的角色,它是由叢集中的活躍成員選舉出來的。每個叢集中的成員都有可能充當 Leader,Leader 負責管理工作,包括將分割槽分配給 broker 和監控 broker。叢集中,一個分割槽從屬於一個 Leader,但是一個分割槽可以分配給多個 broker(非Leader),這時候會發生分割槽複製。這種複製的機制為分割槽提供了訊息冗餘,如果一個 broker 失效,那麼其他活躍使用者會重新選舉一個 Leader 接管。

file

producer

生產者,即訊息的釋出者,其會將某 topic 的訊息釋出到相應的 partition 中。生產者在預設情況下把訊息均衡地分佈到主題的所有分割槽上,而並不關心特定訊息會被寫到哪個分割槽。不過,在某些情況下,生產者會把訊息直接寫到指定的分割槽。

consumer

消費者,即訊息的使用者,一個消費者可以消費多個 topic 的訊息,對於某一個 topic 的訊息,其只會消費同一個 partition 中的訊息

file

在瞭解完 Kafka 的基本概念之後,我們通過搭建 Kafka 叢集來進一步深刻認識一下 Kafka。

確保安裝環境

安裝 Java 環境

在安裝 Kafka 之前,先確保Linux 環境上是否有 Java 環境,使用 java -version 命令檢視 Java 版本,推薦使用Jdk 1.8 ,如果沒有安裝 Java 環境的話,可以按照這篇文章進行安裝(https://www.cnblogs.com/zs-notes/p/8535275.html)

安裝 Zookeeper 環境

Kafka 的底層使用 Zookeeper 儲存後設資料,確保一致性,所以安裝 Kafka 前需要先安裝 Zookeeper,Kafka 的發行版自帶了 Zookeeper ,可以直接使用指令碼來啟動,不過安裝一個 Zookeeper 也不費勁

Zookeeper 單機搭建

Zookeeper 單機搭建比較簡單,直接從 https://www.apache.org/dyn/closer.cgi/zookeeper/ 官網下載一個穩定版本的 Zookeeper ,這裡我使用的是 3.4.10,下載完成後,在 Linux 系統中的 /usr/local 目錄下建立 zookeeper 資料夾,使用xftp 工具(xftp 和 xshell 工具都可以在官網 https://www.netsarang.com/zh/xshell/ 申請免費的家庭版)把下載好的 zookeeper 壓縮包放到 /usr/local/zookeeper 目錄下。

如果下載的是一個 tar.gz 包的話,直接使用 tar -zxvf zookeeper-3.4.10.tar.gz解壓即可

如果下載的是 zip 包的話,還要檢查一下 Linux 中是否有 unzip 工具,如果沒有的話,使用 yum install unzip 安裝 zip 解壓工具,完成後使用 unzip zookeeper-3.4.10.zip 解壓即可。

解壓完成後,cd 到 /usr/local/zookeeper/zookeeper-3.4.10 ,建立一個 data 資料夾,然後進入到 conf 資料夾下,使用 mv zoo_sample.cfg zoo.cfg 進行重新命名操作

然後使用 vi 開啟 zoo.cfg ,更改一下dataDir = /usr/local/zookeeper/zookeeper-3.4.10/data ,儲存。

進入bin目錄,啟動服務輸入命令 ./zkServer.sh start 輸出下面內容表示搭建成功
file

關閉服務輸入命令,./zkServer.sh stop

file

使用 ./zkServer.sh status 可以檢視狀態資訊。

Zookeeper 叢集搭建

準備條件

準備條件:需要三個伺服器,這裡我使用了CentOS7 並安裝了三個虛擬機器,併為各自的虛擬機器分配了1GB的記憶體,在每個 /usr/local/ 下面新建 zookeeper 資料夾,把 zookeeper 的壓縮包挪過來,解壓,完成後會有 zookeeper-3.4.10 資料夾,進入到資料夾,新建兩個資料夾,分別是 datalog資料夾

注:上一節單機搭建中已經建立了一個data 資料夾,就不需要重新建立了,直接新建一個 log 資料夾,對另外兩個新增的服務需要新建這兩個資料夾。

設定叢集

新建完成後,需要編輯 conf/zoo.cfg 檔案,三個檔案的內容如下

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper/zookeeper-3.4.10/data
dataLogDir=/usr/local/zookeeper/zookeeper-3.4.10/log
clientPort=12181
server.1=192.168.1.7:12888:13888
server.2=192.168.1.8:12888:13888
server.3=192.168.1.9:12888:13888

server.1 中的這個 1 表示的是伺服器的標識也可以是其他數字,表示這是第幾號伺服器,這個標識要和下面我們配置的 myid 的標識一致可以。

192.168.1.7:12888:13888 為叢集中的 ip 地址,第一個埠表示的是 master 與 slave 之間的通訊介面,預設是 2888,第二個埠是leader選舉的埠,叢集剛啟動的時候選舉或者leader掛掉之後進行新的選舉的埠,預設是 3888

現在對上面的配置檔案進行解釋

tickTime: 這個時間是作為 Zookeeper 伺服器之間或客戶端與伺服器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會傳送一個心跳。

initLimit:這個配置項是用來配置 Zookeeper 接受客戶端(這裡所說的客戶端不是使用者連線 Zookeeper 伺服器的客戶端,而是 Zookeeper 伺服器叢集中連線到 Leader 的 Follower 伺服器)初始化連線時最長能忍受多少個心跳時間間隔數。當已經超過 5個心跳的時間(也就是 tickTime)長度後 Zookeeper 伺服器還沒有收到客戶端的返回資訊,那麼表明這個客戶端連線失敗。總的時間長度就是 5*2000=10 秒

syncLimit: 這個配置項標識 Leader 與Follower 之間傳送訊息,請求和應答時間長度,最長不能超過多少個 tickTime 的時間長度,總的時間長度就是5*2000=10秒

dataDir: 快照日誌的儲存路徑

dataLogDir: 事務日誌的儲存路徑,如果不配置這個那麼事務日誌會預設儲存到dataDir指定的目錄,這樣會嚴重影響zk的效能,當zk吞吐量較大的時候,產生的事務日誌、快照日誌太多

clientPort: 這個埠就是客戶端連線 Zookeeper 伺服器的埠,Zookeeper 會監聽這個埠,接受客戶端的訪問請求。

建立 myid 檔案

在瞭解完其配置檔案後,現在來建立每個叢集節點的 myid ,我們上面說過,這個 myid 就是 server.1 的這個 1 ,類似的,需要為叢集中的每個服務都指定標識,使用 echo 命令進行建立

# server.1
echo "1" > /usr/local/zookeeper/zookeeper-3.4.10/data/myid
# server.2
echo "2" > /usr/local/zookeeper/zookeeper-3.4.10/data/myid
# server.3
echo "3" > /usr/local/zookeeper/zookeeper-3.4.10/data/myid

啟動服務並測試

配置完成,為每個 zk 服務啟動並測試,我在 windows 電腦的測試結果如下

啟動服務(每臺都需要執行)

cd /usr/local/zookeeper/zookeeper-3.4.10/bin
./zkServer.sh start

檢查服務狀態

使用 ./zkServer.sh status 命令檢查服務狀態

192.168.1.7 --- follower

file

192.168.1.8 --- leader

file

192.168.1.9 --- follower

file

zk叢集一般只有一個leader,多個follower,主一般是相應客戶端的讀寫請求,而從主同步資料,當主掛掉之後就會從follower裡投票選舉一個leader出來。

Kafka 叢集搭建

準備條件

/usr/local 下新建 kafka 資料夾,然後把下載完成的 tar.gz 包移到 /usr/local/kafka 目錄下,使用 tar -zxvf 壓縮包 進行解壓,解壓完成後,進入到 kafka_2.12-2.3.0 目錄下,新建 log 資料夾,進入到 config 目錄下

我們可以看到有很多 properties 配置檔案,這裡主要關注 server.properties 這個檔案即可。

file

kafka 啟動方式有兩種,一種是使用 kafka 自帶的 zookeeper 配置檔案來啟動(可以按照官網來進行啟動,並使用單個服務多個節點來模擬叢集http://kafka.apache.org/quickstart#quickstart_multibroker),一種是通過使用獨立的zk叢集來啟動,這裡推薦使用第二種方式,使用 zk 叢集來啟動

修改配置項

需要為每個服務都修改一下配置項,也就是server.properties, 需要更新和新增的內容有

broker.id=0 //初始是0,每個 server 的broker.id 都應該設定為不一樣的,就和 myid 一樣 我的三個服務分別設定的是 1,2,3
log.dirs=/usr/local/kafka/kafka_2.12-2.3.0/log

#在log.retention.hours=168 下面新增下面三項
message.max.byte=5242880
default.replication.factor=2
replica.fetch.max.bytes=5242880

#設定zookeeper的連線埠
zookeeper.connect=192.168.1.7:2181,192.168.1.8:2181,192.168.1.9:2181

配置項的含義

broker.id=0  #當前機器在叢集中的唯一標識,和zookeeper的myid性質一樣
port=9092 #當前kafka對外提供服務的埠預設是9092
host.name=192.168.1.7 #這個引數預設是關閉的,在0.8.1有個bug,DNS解析問題,失敗率的問題。
num.network.threads=3 #這個是borker進行網路處理的執行緒數
num.io.threads=8 #這個是borker進行I/O處理的執行緒數
log.dirs=/usr/local/kafka/kafka_2.12-2.3.0/log #訊息存放的目錄,這個目錄可以配置為“,”逗號分割的表示式,上面的num.io.threads要大於這個目錄的個數這個目錄,如果配置多個目錄,新建立的topic他把訊息持久化的地方是,當前以逗號分割的目錄中,那個分割槽數最少就放那一個
socket.send.buffer.bytes=102400 #傳送緩衝區buffer大小,資料不是一下子就傳送的,先回儲存到緩衝區了到達一定的大小後在傳送,能提高效能
socket.receive.buffer.bytes=102400 #kafka接收緩衝區大小,當資料到達一定大小後在序列化到磁碟
socket.request.max.bytes=104857600 #這個引數是向kafka請求訊息或者向kafka傳送訊息的請請求的最大數,這個值不能超過java的堆疊大小
num.partitions=1 #預設的分割槽數,一個topic預設1個分割槽數
log.retention.hours=168 #預設訊息的最大持久化時間,168小時,7天
message.max.byte=5242880  #訊息儲存的最大值5M
default.replication.factor=2  #kafka儲存訊息的副本數,如果一個副本失效了,另一個還可以繼續提供服務
replica.fetch.max.bytes=5242880  #取訊息的最大直接數
log.segment.bytes=1073741824 #這個引數是:因為kafka的訊息是以追加的形式落地到檔案,當超過這個值的時候,kafka會新起一個檔案
log.retention.check.interval.ms=300000 #每隔300000毫秒去檢查上面配置的log失效時間(log.retention.hours=168 ),到目錄檢視是否有過期的訊息如果有,刪除
log.cleaner.enable=false #是否啟用log壓縮,一般不用啟用,啟用的話可以提高效能
zookeeper.connect=192.168.1.7:2181,192.168.1.8:2181,192.168.1.9:2181 #設定zookeeper的連線埠

啟動 Kafka 叢集並測試

  • 啟動服務,進入到 /usr/local/kafka/kafka_2.12-2.3.0/bin 目錄下
# 啟動後臺程式
./kafka-server-start.sh -daemon ../config/server.properties
  • 檢查服務是否啟動
# 執行命令 jps
6201 QuorumPeerMain
7035 Jps
6972 Kafka
  • kafka 已經啟動
  • 建立 Topic 來驗證是否建立成功
# cd .. 往回退一層 到 /usr/local/kafka/kafka_2.12-2.3.0 目錄下
bin/kafka-topics.sh --create --zookeeper 192.168.1.7:2181 --replication-factor 2 --partitions 1 --topic cxuan

對上面的解釋

--replication-factor 2 複製兩份

--partitions 1 建立1個分割槽

--topic 建立主題

檢視我們的主題是否出建立成功

bin/kafka-topics.sh --list --zookeeper 192.168.1.7:2181

file

啟動一個服務就能把叢集啟動起來

在一臺機器上建立一個釋出者

# 建立一個broker,釋出者
./kafka-console-producer.sh --broker-list 192.168.1.7:9092 --topic cxuantopic

在一臺伺服器上建立一個訂閱者

# 建立一個consumer, 消費者
bin/kafka-console-consumer.sh --bootstrap-server 192.168.1.7:9092 --topic cxuantopic --from-beginning

注意:這裡使用 --zookeeper 的話可能出現 zookeeper is not a recognized option 的錯誤,這是因為 kafka 版本太高,需要使用 --bootstrap-server 指令

測試結果

釋出

file

消費

file

其他命令

顯示 topic

bin/kafka-topics.sh --list --zookeeper 192.168.1.7:2181

# 顯示
cxuantopic

檢視 topic 狀態

bin/kafka-topics.sh --describe --zookeeper 192.168.1.7:2181 --topic cxuantopic

# 下面是顯示的詳細資訊
Topic:cxuantopic PartitionCount:1 ReplicationFactor:2 Configs:
Topic: cxuantopic Partition: 0 Leader: 1 Replicas: 1,2 Isr: 1,2

# 分割槽為為1  複製因子為2   主題 cxuantopic 的分割槽為0 
# Replicas: 0,1   複製的為1,2

Leader 負責給定分割槽的所有讀取和寫入的節點,每個節點都會通過隨機選擇成為 leader。

Replicas 是為該分割槽複製日誌的節點列表,無論它們是 Leader 還是當前處於活動狀態。

Isr 是同步副本的集合。它是副本列表的子集,當前仍處於活動狀態並追隨Leader。

至此,kafka 叢集搭建完畢。

驗證多節點接收資料

剛剛我們都使用的是 相同的ip 服務,下面使用其他叢集中的節點,驗證是否能夠接受到服務

在另外兩個節點上使用

bin/kafka-console-consumer.sh --bootstrap-server 192.168.1.7:9092 --topic cxuantopic --from-beginning

然後再使用 broker 進行訊息傳送,經測試三個節點都可以接受到訊息。

配置詳解

在搭建 Kafka 的時候我們簡單介紹了一下 server.properties 中配置的含義,現在我們來詳細介紹一下引數的配置和概念

常規配置

這些引數是 kafka 中最基本的配置

  • broker.id

每個 broker 都需要有一個識別符號,使用 broker.id 來表示。它的預設值是 0,它可以被設定成其他任意整數,在叢集中需要保證每個節點的 broker.id 都是唯一的。

  • port

如果使用配置樣本來啟動 kafka ,它會監聽 9092 埠,修改 port 配置引數可以把它設定成其他任意可用的埠。

  • zookeeper.connect

用於儲存 broker 後設資料的地址是通過 zookeeper.connect 來指定。 localhost:2181 表示執行在本地 2181 埠。該配置引數是用逗號分隔的一組 hostname:port/path 列表,每一部分含義如下:

hostname 是 zookeeper 伺服器的服務名或 IP 地址

port 是 zookeeper 連線的埠

/path 是可選的 zookeeper 路徑,作為 Kafka 叢集的 chroot 環境。如果不指定,預設使用跟路徑

  • log.dirs

Kafka 把訊息都儲存在磁碟上,存放這些日誌片段的目錄都是通過 log.dirs 來指定的。它是一組用逗號分隔的本地檔案系統路徑。如果指定了多個路徑,那麼 broker 會根據 "最少使用" 原則,把同一分割槽的日誌片段儲存到同一路徑下。要注意,broker 會向擁有最少數目分割槽的路徑新增分割槽,而不是向擁有最小磁碟空間的路徑新增分割槽。

  • num.recovery.threads.per.data.dir

對於如下 3 種情況,Kafka 會使用可配置的執行緒池來處理日誌片段

伺服器正常啟動,用於開啟每個分割槽的日誌片段;

伺服器崩潰後啟動,用於檢查和截斷每個分割槽的日誌片段;

伺服器正常關閉,用於關閉日誌片段

預設情況下,每個日誌目錄只使用一個執行緒。因為這些執行緒只是在伺服器啟動和關閉時會用到,所以完全可以設定大量的執行緒來達到井行操作的目的。特別是對於包含大量分割槽的伺服器來說,一旦發生崩憤,在進行恢復時使用井行操作可能會省下數小時的時間。設定此引數時需要注意,所配置的數字對應的是 log.dirs 指定的單個日誌目錄。也就是說,如果 num.recovery.threads.per.data.dir 被設為 8,並且 log.dir 指定了 3 個路徑,那麼總共需要 24 個執行緒。

  • auto.create.topics.enable

預設情況下,Kafka 會在如下 3 種情況下建立主題

當一個生產者開始往主題寫入訊息時

當一個消費者開始從主題讀取訊息時

當任意一個客戶向主題傳送後設資料請求時

  • delete.topic.enable

如果你想要刪除一個主題,你可以使用主題管理工具。預設情況下,是不允許刪除主題的,delete.topic.enable 的預設值是 false 因此你不能隨意刪除主題。這是對生產環境的合理性保護,但是在開發環境和測試環境,是可以允許你刪除主題的,所以,如果你想要刪除主題,需要把 delete.topic.enable 設為 true。

主題預設配置

Kafka 為新建立的主題提供了很多預設配置引數,下面就來一起認識一下這些引數

  • num.partitions

num.partitions 引數指定了新建立的主題需要包含多少個分割槽。如果啟用了主題自動建立功能(該功能是預設啟用的),主題分割槽的個數就是該引數指定的值。該引數的預設值是 1。要注意,我們可以增加主題分割槽的個數,但不能減少分割槽的個數。

  • default.replication.factor

這個引數比較簡單,它表示 kafka儲存訊息的副本數,如果一個副本失效了,另一個還可以繼續提供服務default.replication.factor 的預設值為1,這個引數在你啟用了主題自動建立功能後有效。

  • log.retention.ms

Kafka 通常根據時間來決定資料可以保留多久。預設使用 log.retention.hours 引數來配置時間,預設是 168 個小時,也就是一週。除此之外,還有兩個引數 log.retention.minutes 和 log.retentiion.ms 。這三個引數作用是一樣的,都是決定訊息多久以後被刪除,推薦使用 log.retention.ms。

  • log.retention.bytes

另一種保留訊息的方式是判斷訊息是否過期。它的值通過引數 log.retention.bytes 來指定,作用在每一個分割槽上。也就是說,如果有一個包含 8 個分割槽的主題,並且 log.retention.bytes 被設定為 1GB,那麼這個主題最多可以保留 8GB 資料。所以,當主題的分割槽個數增加時,整個主題可以保留的資料也隨之增加。

  • log.segment.bytes

上述的日誌都是作用在日誌片段上,而不是作用在單個訊息上。當訊息到達 broker 時,它們被追加到分割槽的當前日誌片段上,當日志片段大小到達 log.segment.bytes 指定上限(預設為 1GB)時,當前日誌片段就會被關閉,一個新的日誌片段被開啟。如果一個日誌片段被關閉,就開始等待過期。這個引數的值越小,就越會頻繁的關閉和分配新檔案,從而降低磁碟寫入的整體效率。

  • log.segment.ms

上面提到日誌片段經關閉後需等待過期,那麼 log.segment.ms 這個引數就是指定日誌多長時間被關閉的引數和,log.segment.ms 和 log.retention.bytes 也不存在互斥問題。日誌片段會在大小或時間到達上限時被關閉,就看哪個條件先得到滿足。

  • message.max.bytes

broker 通過設定 message.max.bytes 引數來限制單個訊息的大小,預設是 1000 000, 也就是 1MB,如果生產者嘗試傳送的訊息超過這個大小,不僅訊息不會被接收,還會收到 broker 返回的錯誤訊息。跟其他與位元組相關的配置引數一樣,該引數指的是壓縮後的訊息大小,也就是說,只要壓縮後的訊息小於 mesage.max.bytes,那麼訊息的實際大小可以大於這個值

這個值對效能有顯著的影響。值越大,那麼負責處理網路連線和請求的執行緒就需要花越多的時間來處理這些請求。它還會增加磁碟寫入塊的大小,從而影響 IO 吞吐量。

如果這篇文章讀者們覺得還不錯,跪求點贊、留言,你的支援將是我繼續肝文的動力!

另外,新增我的微信 becomecxuan,加入每日一題群,每天一道面試題分享,更多內容請參見我的 Github,成為最好的 bestJavaer

我自己肝了六本 PDF,微信搜尋「程式設計師cxuan」關注公眾號後,在後臺回覆 cxuan ,領取全部 PDF,這些 PDF 如下

六本 PDF 連結

相關文章