從零開始學習Kafka

Halburt發表於2019-05-10

簡介

kafka是一個分散式訊息佇列。具有高效能、持久化、多副本備份、橫向擴充套件能力。生產者往佇列裡寫訊息,消費者從佇列裡取訊息進行業務邏輯。一般在架構設計中起到解耦、削峰、非同步處理的作用。

Kafka核心元件-intsmaze

  •   Topic:訊息根據Topic進行歸類,可以理解為一個隊裡。
  •   Producer:訊息生產者,就是向kafka broker發訊息的客戶端。
  •   Consumer:訊息消費者,向kafka broker取訊息的客戶端。
  •   broker:每個kafka例項(server),一臺kafka伺服器就是一個broker,一個叢集由多個broker組成,一個broker可以容納多個topic。
  •   Zookeeper:依賴叢集儲存meta資訊。    大家先看kafka的介紹或者教程啊,上來都顯示一堆長篇大論,各自文字圖片,看著很懵逼,頭暈。搞程式的,要讓ta跑起來,再針對可執行的成果,慢慢了解ta。所以本文會由淺入深,先實踐後理論,結合實踐講理論。

Kafka安裝配置

下載

wget http://mirror.bit.edu.cn/apache/kafka/2.2.0/kafka_2.11-2.2.0.tgz
複製程式碼

解壓

 tar -zxvf kafka_2.11-2.2.0.tgz
 
複製程式碼

修改 kafka-server 的配置檔案

 cd kafka_2.11-2.2.0
 
vim  config/server.properties
複製程式碼

修改其中的:

# The id of the broker. This must be set to a unique integer for each broker.
broker.id=1
# A comma separated list of directories under which to store log files
log.dirs=/data/kafka-logs
複製程式碼

啟動zk【預設埠2181】

bin/zookeeper-server-start.sh config/zookeeper.properties
複製程式碼

image.png

啟動Kafka

使用 kafka-server-start.sh 啟動 kafka 服務:

bin/kafka-server-start.sh config/server.properties
複製程式碼

image.png

測試使用

建立 topic

使用 kafka-topics.sh 建立單分割槽單副本的 topic demo

bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic demo
複製程式碼

image.png

檢視 topic 列表:

bin/kafka-topics.sh --list --zookeeper localhost:2181
複製程式碼

image.png

傳送訊息【生產者】

使用 kafka-console-producer.sh 傳送訊息:

bin/kafka-console-producer.sh --broker-list localhost:9092 --topic demo
複製程式碼

讀取訊息【消費者】

使用 kafka-console-consumer.sh 接收訊息並在終端列印:

 bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic demo --from-beginning
複製程式碼

image.png
注意不要使用 bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning,高版本已經不支援

檢視描述 topics 資訊

bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic demo
複製程式碼
[root@localhost kafka_2.11-2.2.0]# bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic demo
Topic:demo      PartitionCount:1        ReplicationFactor:1     Configs:
        Topic: demo     Partition: 0    Leader: 1       Replicas: 1     Isr: 1

複製程式碼

image.png

第一行給出了所有分割槽的摘要,每個附加行給出了關於一個分割槽的資訊。 由於我們只有一個分割槽,所以只有一行。

  • “Leader”: 是負責給定分割槽的所有讀取和寫入的節點。 每個節點將成為分割槽隨機選擇部分的領導者。
  • “Replicas”: 是複製此分割槽日誌的節點列表,無論它們是否是領導者,或者即使他們當前處於活動狀態。
  • “Isr”: 是一組“同步”副本。這是複製品列表的子集,當前活著並被引導到領導者

擴充套件-叢集配置

Kafka 支援兩種模式的叢集搭建:可以在單機上執行多個 broker 例項來實現叢集,也可在多臺機器上搭建叢集,下面介紹下如何實現單機多 broker 例項叢集,其實很簡單,只需要如下配置即可。

單機多broker 叢集配置

利用單節點部署多個 broker。 不同的 broker 設定不同的 id,監聽埠及日誌目錄。 例如:

cp config/server.properties config/server-2.properties
vi config/server-2.properties
複製程式碼

修改內容:

broker.id=2

listeners = PLAINTEXT://127.0.0.1:9093

log.dirs=/data/kafka-logs2
複製程式碼

同樣,配置第三個broker:

cp config/server-2.properties config/server-3.properties
vi config/server-3.properties
複製程式碼

修改內容:

broker.id=2

listeners = PLAINTEXT://127.0.0.1:9093

log.dirs=/data/kafka-logs2
複製程式碼

listeners 申明此kafka伺服器需要監聽的埠號,預設會使用localhost的地址,如果是在遠端伺服器上執行則必須配置,例如:          listeners=PLAINTEXT:// 192.168.180.128:9092 並確保伺服器的9092埠能夠訪問

啟動2/3 borker

bin/kafka-server-start.sh config/server-2.properties &
bin/kafka-server-start.sh config/server-3.properties &

複製程式碼

至此,單機多broker例項的叢集配置完畢。

擴充套件-多機多borker叢集

分別在多個節點按上述方式安裝 Kafka,配置啟動多個 Zookeeper 例項。

假設三臺機器 IP 地址是 : 192.168.153.135, 192.168.153.136, 192.168.153.137

分別配置多個機器上的 Kafka 服務,設定不同的 broker id,zookeeper.connect 設定如下:

config/server.properties裡面的 zookeeper.connect

zookeeper.connect=192.168.153.135:2181,192.168.153.136:2181,192.168.153.137:2181
複製程式碼

使用 Kafka Connect 來匯入/匯出資料

從控制檯寫入資料並將其寫回控制檯是一個方便的起點,但您可能想要使用其他來源的資料或將資料從 Kafka 匯出到其他系統。對於許多系統,您可以使用 Kafka Connect 來匯入或匯出資料,而不必編寫自定義整合程式碼。

Kafka Connect 是 Kafka 包含的一個工具,可以將資料匯入和匯出到 Kafka。它是一個可擴充套件的工具,執行 聯結器,實現與外部系統互動的自定義邏輯。在這個快速入門中,我們將看到如何使用簡單的聯結器執行 Kafka Connect,這些聯結器將資料從檔案匯入到 Kafka topic,並將資料從 Kafka topic 匯出到檔案。

參考:

  • http://www.54tianzhisheng.cn/2018/01/04/Kafka/ 
    複製程式碼
  • http://kafka.apache.org/10/documentation/streams/quickstart
    複製程式碼
  • http://kafka.apache.org/20/documentation.html#quickstart
    複製程式碼

程式碼測試

準備測試kafka

cp config/server.properties config/server-idea.properties
vi config/server-idea.properties
 
broker.id=999

listeners = PLAINTEXT://192.168.1.177:9999

log.dirs=/data/kafka-logs-999
複製程式碼

192.168.1.177為kafka所在機器的ip地址,9999埠號是對外提供的埠,下文會使用到

Springboot 傳送訊息、接受訊息原始碼

很簡單的一個小demo,可以直接拷貝使用。

KafkaApplication.java:


import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;

@SpringBootApplication
public class KafkaApplication {

    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(KafkaApplication.class, args);

        KafkaTemplate kafkaTemplate = context.getBean(KafkaTemplate.class);

        for (int i = 0; i < 10; i++) {
            //呼叫訊息傳送類中的訊息傳送方法
            kafkaTemplate.send("mytopic", System.currentTimeMillis() + "傳送" + i);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @KafkaListener(topics = {"mytopic"},groupId = "halburt-demo2")
    public void consumer1(String message) {
        System.out.println("consumer1收到訊息:" + message);
    }

    @KafkaListener(topics = {"mytopic"} ,groupId = "halburt-demo")
    public void consumer2(ConsumerRecord<?, ?> record) {
        System.out.println("consumer2收到訊息");
        System.out.println("    topic" + record.topic());
        System.out.println("    key:" + record.key());
        System.out.println("    value:"+record.value());
    }
}
複製程式碼

application.yml:

server:
  port: 8090
spring:
  kafka:
    consumer:
      auto-commit-interval: 100
      bootstrap-servers: 192.168.1.177:9999
      enable-auto-commit: true
      group-id: halburt-demo
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      max-poll-records: 1
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
    listener:
      concurrency: 5
    producer:
      bootstrap-servers: 192.168.1.177:9999
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
複製程式碼

192.168.1.177:9999即為kafka的配置檔案中配置

pom.xml依賴:

依賴版本:

spring-boot.version:2.1.3.RELEASE spring-kafka.version:2.2.0.RELEASE

【此處有坑】此處依賴版本可以不用這2個版本,但是一定要注意springboot和kafka的版本對應

    <dependencies>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
    </dependencies>
複製程式碼

啟動kafka並run Application.java

bin/kafka-server-start.sh config/server-idea.properties &
複製程式碼

image.png
已經啟動了zk,此處不用再啟動,如果未啟動,需要啟動zk。
image.png

cd /home/hd/kafka_2.11-2.2.0/
bin/zookeeper-server-start.sh config/zookeeper.properties&
複製程式碼

kafka啟動成功之後,run Application,會看到日誌如下:

image.png
已經接收到訊息了。

如果你是跟著本文從頭開始的,一定注意此處有個坑

如果你是從頭開始跟這個本文學習的,那麼你直接啟動的話,會發現訊息發出去了,但是沒有接收到。 我也是查了好久,看了很多教程,別人都行我就不行。 如果你的zk有其他的topic節點的話,會收不到訊息,直接上解決方案:刪除所有的zk節點。怎麼刪除?

上碼:


/**
 * zookeeper znode遞迴刪除節點
 * @author Halburt
 *
 */
public class DeleteZkNode {
    //zookeeper的地址 
    private static final String connectString = "192.168.1.177:2181";

    private static final int sessionTimeout = 2000;

    private static ZooKeeper zookeeper = null;

    /**
     * main函式
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {

        //呼叫rmr,刪除所有目錄
        rmr("/");
    }

    /**
     * 遞迴刪除 因為zookeeper只允許刪除葉子節點,如果要刪除非葉子節點,只能使用遞迴
     * @param path
     * @throws IOException
     */
    public static void rmr(String path) throws Exception {
        ZooKeeper zk = getZookeeper();
        //獲取路徑下的節點
        List<String> children = zk.getChildren(path, false);
        for (String pathCd : children) {
            //獲取父節點下面的子節點路徑
            String newPath = "";
            //遞迴呼叫,判斷是否是根節點
            if (path.equals("/")) {
                newPath = "/" + pathCd;
            } else {
                newPath = path + "/" + pathCd;
            }
            rmr(newPath);
        }
        //刪除節點,並過濾zookeeper節點和 /節點
        if (path != null && !path.trim().startsWith("/zookeeper") && !path.trim().equals("/")) {
            zk.delete(path, -1);
            //列印刪除的節點路徑
            System.out.println("被刪除的節點為:" + path);
        }
    }

    /**
     * 獲取Zookeeper例項
     * @return
     * @throws IOException
     */
    public static ZooKeeper getZookeeper() throws IOException {
        zookeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent event) {

            }
        });
        return zookeeper;
    }

}
複製程式碼

終端命令檢視訊息

bin/kafka-console-consumer.sh --bootstrap-server 192.168.1.177:9999 --topic mytopic  --from-beginning
複製程式碼

image.png

安利一下視覺化工具Kafka Tool 2

下載地址

Kafka Tool 2是一款Kafka的視覺化客戶端工具,可以非常方便的檢視Topic的佇列資訊以及消費者資訊以及kafka節點資訊。下載地址:www.kafkatool.com/download.ht…

使用

先建立連線

下載安裝之後會彈出一個配置連線的視窗,我們可以看到這個視窗左上角為Add Cluster(新增叢集),但沒關係,對應單節點的Kafka例項來說也是可以的,因為這個軟體監控的是Zookeeper而不是Kafka,Kafka的叢集搭建也是依賴Zookeeper來實現的,所以預設情況下我們都是直接通過Zookeeper去完成大部分操作。

image.png

建立完成之後,連線

我們可以看到已經建立好的Topic。這個軟體預設顯示資料的型別為Byte,可以在設定裡面找到對應的修改選項

image.png
接下來就自己探索吧
image.,接下來就自己探索吧png

理論學習

kafka單節點的結構如下:

image.png
單節點broker包含多個topic主題,而每個topic則包含多個partition副本,每個partition會有序的儲存訊息。

kafka的總體資料流

kafka對外使用topic的概念,生產者往topic裡寫訊息,消費者從topic讀訊息。為了做到水平擴充套件,一個topic實際是由多個partition組成的,遇到瓶頸時,可以通過增加partition的數量來進行橫向擴容。單個parition內是保證訊息有序。每新寫一條訊息,kafka就是在對應的檔案append寫,所以效能非常高。kafka的總體資料流是這樣的:

2835676-f378607bc841309a.png

Producers往Brokers裡面的指定Topic中寫訊息,Consumers從Brokers裡面拉去指定Topic的訊息,然後進行業務處理。

名詞解析

Producer

消費者: Producer將訊息釋出到指定的Topic中,同時Producer也能決定將此訊息歸屬於哪個partition;比如基於"round-robin"方式或者通過其他的一些演算法等.

Consumer

每個consumer屬於一個consumer group;反過來說,每個group中可以有多個consumer.傳送到Topic的訊息,只會被訂閱此Topic的每個group中的一個consumer消費(對於一條訊息來說,同一組的消費者只會有一個消費者去消費).

如果所有的consumer都具有相同的group,這種情況和queue模式很像;訊息將會在consumers之間負載均衡.  如果所有的consumer都具有不同的group,那這就是"釋出-訂閱";訊息將會廣播給所有的消費者.

在kafka中,一個partition中的訊息只會被group中的一個consumer消費;每個group中consumer訊息消費互相獨立;我們可以認為一個group是一個"訂閱"者,一個Topic中的每個partions,只會被一個"訂閱者"中的一個consumer消費,不過一個consumer可以消費多個partitions中的訊息.kafka只能保證一個partition中的訊息被某個consumer消費時,訊息是順序的。事實上,從Topic角度來說,訊息仍不是有序的。

Topics

一個Topic可以認為是一類訊息,每個topic將被分成多個partition(區),每個partition在儲存層面是append log檔案。任何釋出到此partition的訊息都會被直接追加到log檔案的尾部,每條訊息在檔案中的位置稱為offset(偏移量),offset為一個long型數字,它是唯一標記一條訊息。它唯一的標記一條訊息。kafka並沒有提供其他額外的索引機制來儲存offset,因為在kafka中幾乎不允許對訊息進行“隨機讀寫”。

Partition

topic物理上的分組,一個topic可以分為多個partition,每個partition是一個有序的佇列

以下是單個生產者和消費者從兩個分割槽主題讀取和寫入的簡單示例。

image.png

此圖顯示了一個producer向2個partition分割槽寫入日誌,以及消費者從相同日誌中讀取的內容。日誌中的每條記錄都有一個相關的條目號,稱之為偏移量offset。消費者使用此偏移來記錄其在partitiond讀取日誌的位置。

當然如果存在多個消費者的話,根據groupId分組,同一組的消費者不會重複讀取日誌。

換句話說:
訂閱topic是以一個消費組來訂閱的,一個消費組裡面可以有多個消費者。同一個消費組中的兩個消費者,不會同時消費一個partition。換句話來說,就是一個partition,只能被消費組裡的一個消費者消費,但是可以同時被多個消費組消費。因此,如果消費組內的消費者如果比partition多的話,那麼就會有個別消費者一直空閒。
 
複製程式碼

其實consumer可以使用任意順序消費日誌訊息,它只需要將offset重置為任意值.(offset將會儲存在zookeeper中,kafka叢集幾乎不需要維護任何consumer和producer狀態資訊,這些資訊有zookeeper儲存)

partition有多個.最根本原因是kafka基於檔案儲存.通過分割槽,可以將日誌內容分散到多個partition上,來避免檔案大小達到單機磁碟的上限,每個partiton都會被當前server(kafka例項)儲存;可以將一個topic切分多任意多個partitions,來訊息儲存/消費的效率.此外越多的partitions意味著可以容納更多的consumer,有效提升併發消費的能力.

使用場景

訊息系統、訊息佇列

對於一些常規的訊息系統,kafka是個不錯的選擇;partitons/replication和容錯,可以使kafka具有良好的擴充套件性和效能優勢.不過到目前為止,我們應該很清楚認識到,kafka並沒有提供JMS中的"事務性""訊息傳輸擔保(訊息確認機制)""訊息分組"等企業級特性;kafka只能使用作為"常規"的訊息系統,在一定程度上,尚未確保訊息的傳送與接收絕對可靠(比如,訊息重發,訊息傳送丟失等)

日誌聚合

kafka的特性決定它非常適合作為"日誌收集中心";application可以將操作日誌"批量""非同步"的傳送到kafka叢集中,而不是儲存在本地或者DB中;kafka可以批量提交訊息/壓縮訊息等,這對producer端而言,幾乎感覺不到效能的開支.此時consumer端可以使hadoop等其他系統化的儲存和分析系統.

網站活動追蹤、呼叫鏈系統、事件採集

可以將網頁/使用者操作等資訊傳送到kafka中.並實時監控,或者離線統計分析等

等等其他場景

server.properties配置檔案解讀

############################# Server Basics #############################
# 節點的ID,必須與其它節點不同
broker.id=0
# 選擇啟用刪除主題功能,預設false
#delete.topic.enable=true
############################# Socket Server Settings #############################

# 套接字伺服器堅挺的地址。如果沒有配置,就使用java.net.InetAddress.getCanonicalHostName()的返回值
# FORMAT:
# listeners = listener_name://host_name:port
# EXAMPLE:
# listeners = PLAINTEXT://your.host.name:9092
#listeners=PLAINTEXT://:9092

# 節點的主機名會通知給生產者和消費者。如果沒有設定,如果配置了"listeners"就使用"listeners"的值。
# 否則就使用java.net.InetAddress.getCanonicalHostName()的返回值
#advertised.listeners=PLAINTEXT://your.host.name:9092

# 將偵聽器的名稱對映到安全協議,預設情況下它們是相同的。有關詳細資訊,請參閱配置文件
#listener.security.protocol.map=PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL

# 伺服器用來接受請求或者傳送響應的執行緒數
num.network.threads=3

# 伺服器用來處理請求的執行緒數,可能包括磁碟IO
num.io.threads=8

# 套接字伺服器使用的傳送緩衝區大小
socket.send.buffer.bytes=102400

# 套接字伺服器使用的接收緩衝區大小
socket.receive.buffer.bytes=102400

# 單個請求最大能接收的資料量
socket.request.max.bytes=104857600


############################# Log Basics #############################

# 一個逗號分隔的目錄列表,用來儲存日誌檔案
log.dirs=/tmp/kafka-logs

# 每個主題的日誌分割槽的預設數量。更多的分割槽允許更大的並行操作,但是它會導致節點產生更多的檔案
num.partitions=1

# 每個資料目錄中的執行緒數,用於在啟動時日誌恢復,並在關閉時重新整理。
num.recovery.threads.per.data.dir=1

############################# Internal Topic Settings #############################
# 內部主題設定
# 對於除了開發測試之外的其他任何東西,group後設資料內部主題的複製因子“__consumer_offsets”和“__transaction_state”,建議值大於1,以確保可用性(如3)。
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1

############################# Log Flush Policy #############################



# 在強制重新整理資料到磁碟之前允許接收訊息的數量
#log.flush.interval.messages=10000

# 在強制重新整理之前,訊息可以在日誌中停留的最長時間
#log.flush.interval.ms=1000

############################# Log Retention Policy #############################

# 以下的配置控制了日誌段的處理。策略可以配置為每隔一段時間刪除片段或者到達一定大小之後。
# 當滿足這些條件時,將會刪除一個片段。刪除總是發生在日誌的末尾。

# 一個日誌的最小存活時間,可以被刪除
log.retention.hours=168

# 一個基於大小的日誌保留策略。段將被從日誌中刪除只要剩下的部分段不低於log.retention.bytes。
#log.retention.bytes=1073741824

# 每一個日誌段大小的最大值。當到達這個大小時,會生成一個新的片段。
log.segment.bytes=1073741824

# 檢查日誌段的時間間隔,看是否可以根據保留策略刪除它們
log.retention.check.interval.ms=300000

############################# Zookeeper #############################

zookeeper.connect=localhost:2181

# 連線到Zookeeper的超時時間
zookeeper.connection.timeout.ms=6000


############################# Group Coordinator Settings #############################

group.initial.rebalance.delay.ms=0
複製程式碼

參考文章

www.cnblogs.com/likehua/p/3…

www.jianshu.com/p/d3e963ff8…

如有表述不當之處,敬請指正。

相關文章