Kafka Consumer深入理解

最近項目需要進行實時讀取服務端信息,在網上看到kafka可以解決這個問題,開發完成後對kafka做一個簡單的整理,希望可以幫助到剛開始學習kafka 的同學,給自己也做個筆記:

首先說一下kafka是個什麼東西:

kafka是一個分佈式、支持分區的(partition)、多副本的(replica),基於zookeeper協調的分佈式消息系統,它的最大的特性就是可以實時的處理大量數據以滿足各種需求場景:比如基於hadoop的批處理系統、低延遲的實時系統、storm/Spark流式處理引擎,web/nginx日誌、訪問日誌,消息服務等等

Kafka的特性:

- 高吞吐量、低延遲:kafka每秒可以處理幾十萬條消息,它的延遲最低只有幾毫秒,每個topic可以分多個partition, consumer group 對partition進行consume操作。

- 可擴展性:kafka集羣支持熱擴展

- 持久性、可靠性:消息被持久化到本地磁盤,並且支持數據備份防止數據丟失

- 容錯性:允許集羣中節點失敗(若副本數量爲n,則允許n-1個節點失敗)

- 高併發:支持數千個客戶端同時讀寫

Kafka的使用場景:

- 日誌收集:一個公司可以用Kafka可以收集各種服務的log,通過kafka以統一接口服務的方式開放給各種consumer,例如hadoop、Hbase、Solr等。

- 消息系統:解耦和生產者和消費者、緩存消息等。

- 用戶活動跟蹤:Kafka經常被用來記錄web用戶或者app用戶的各種活動,如瀏覽網頁、搜索、點擊等活動,這些活動信息被各個服務器發佈到kafka的topic中,然後訂閱者通過訂閱這些topic來做實時的監控分析,或者裝載到hadoop、數據倉庫中做離線分析和挖掘。

- 運營指標:Kafka也經常用來記錄運營監控數據。包括收集各種分佈式應用的數據,生產各種操作的集中反饋,比如報警和報告。

- 流式處理:比如spark streaming和storm

我使用的是消費端,所以我着重說一下消費端的內容:

bootstrap.servers
連接Kafka集羣的地址,多個地址以逗號分隔
key.deserializer
消息中key反序列化類,需要和Producer中key序列化類相對應
value.deserializer
消息中value的反序列化類,需要和Producer中Value序列化類相對應
group.id
消費者所屬消費組的唯一標識

其他屬性根據個人情況去定。

關於kafka的安裝可以參考這篇文章:https://blog.csdn.net/qq_36245532/article/details/88850493

kafka總是自動關閉問題 

在我啓動kafka後,總是會模型奇妙的進程掉了,我是啓動命令是

 ./kafka-server-start.sh  /home/kafka/kafka_2.11-2.3.1/config/server.properties 

解決辦法:以守護進程的方式啓動

./kafka-server-start.sh -daemon /home/kafka/kafka_2.11-2.3.1/config/server.properties 

加 -daemon 和不加 -daemon 區別在於:


bin/kafka-run-class.sh
 
# Launch mode
if [ "x$DAEMON_MODE" = "xtrue" ]; then
  #加 daemon 會使用該命令
  nohup $JAVA $KAFKA_HEAP_OPTS $KAFKA_JVM_PERFORMANCE_OPTS $KAFKA_GC_LOG_OPTS $KAFKA_JMX_OPTS $KAFKA_LOG4J_OPTS -cp $CLASSPATH $KAFKA_OPTS "$@" > "$CONSOLE_OUTPUT_FILE" 2>&1 < /dev/null &
else
  #不加時使用的命令
  exec $JAVA $KAFKA_HEAP_OPTS $KAFKA_JVM_PERFORMANCE_OPTS $KAFKA_GC_LOG_OPTS $KAFKA_JMX_OPTS $KAFKA_LOG4J_OPTS -cp $CLASSPATH $KAFKA_OPTS "$@"
fi

代碼這塊:

配置:

1.pom.xml文件

        <!-- kafka 消息隊列 -->
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>RELEASE</version>
        </dependency>

2.配置文件

配置文件可簡單可複雜,最不可或缺的是 

#kafka相關配置
spring.kafka.bootstrap-servers=192.168.183.145:9092

其他可根據自己的需要配置,有些可以寫死

3.消費端代碼

package com.nature.kafka.util;

import com.nature.common.constant.SysParamsCache;
import org.apache.kafka.clients.consumer.*;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.Callable;

public class Consumer implements Callable<Map<String, Object>> {

    private static Properties props = null;
    private static KafkaConsumer<String, String> consumer = null;
    private String topics;
    private Map<String, Object> rtnMap;

    public Consumer(String topics, Map<String, Object> rtnMap) {
        this.topics = topics;
        this.rtnMap = rtnMap;
    }

    @Override
    public Map<String, Object> call() throws Exception {
        return  Consumer.consume(topics, rtnMap);
    }

    private static KafkaConsumer<String, String> getConsumer() {
        props = new Properties();
        String address = SysParamsCache.SYSPARA_BOOTSTRAPSERVER;
        props.put("bootstrap.servers", address);   // "localhost:9092"
        //每個消費者分配獨立的組號
        props.put("group.id", "test");

        //如果value合法,則自動提交偏移量
        props.put("enable.auto.commit", "true");

        //設置多久一次更新被消費消息的偏移量
        props.put("auto.commit.interval.ms", "1000");

        //設置會話響應的時間,超過這個時間kafka可以選擇放棄消費或者消費下一條消息
        props.put("session.timeout.ms", "30000");

        props.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
        consumer = new KafkaConsumer<>(props);
        return consumer;
    }

    public static Map<String, Object> consume(String topics, Map<String, Object> rtnMap) {
        if (consumer == null) {
            consumer = getConsumer();
        }

        consumer.subscribe(Collections.singletonList(topics));

        StringBuffer sb = new StringBuffer();
        ConsumerRecords<String, String> records = null;

        records = consumer.poll(Duration.ofMillis(100));

        for (ConsumerRecord<String, String> record : records) {
            sb.append(record.value());
        }
        rtnMap.put("msg", sb.toString());
        return rtnMap;
    }

    /*public static void main(String[] args) {

        KafkaConsumer<String, String> consumer = null;
        if(consumer == null){
            consumer = getConsumer();
        }

        consumer.subscribe(Collections.singletonList("application_1575964334154_0154"));  //核心函數1:訂閱topic

        while (true) {
            System.err.println("=========");
            //System.out.println(i++);
            //核心函數2:long poll,一次拉取回來多個消息
            *//* 讀取數據,讀取超時時間爲100ms *//*
            ConsumerRecords<String, String> records = consumer.poll(100);
            for (ConsumerRecord<String, String> record : records)
                System.err.printf("offset = %d, key = %s, value = %s\n",
                        record.offset(), record.key(), record.value());
        }
    }*/

}

其實當初在使用這個的時候是要跟springboot集成,但是網上基本上都是自己調用自己用,不輸出,所有我貼一段controll 供大家使用:

/**
     * 獲取kafka消息
     * @param request
     * @return
     */
    @RequestMapping("/getKafkaDate")
    @ResponseBody
    public String getKafkaDate(HttpServletRequest request) throws FileNotFoundException {
        Map<String, Object> rtnMap = new HashMap<>();
        rtnMap.put("code", 500);
        String appId = request.getParameter("appid");
        Consumer consumer = new Consumer(appId,rtnMap);
        FutureTask<Map<String, Object>> ft=new FutureTask<Map<String, Object>>(consumer);
        new Thread(ft,"消費kafka").start();
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            map = ft.get();
            rtnMap.put("msg",map.get("msg"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        rtnMap.put("code", 200);
        return JsonUtils.toJsonNoException(rtnMap);
    }

這樣前臺就可以查詢到kafka的消息了。

這裏說兩個思路:

如果要前臺實時看到日誌,兩個辦法:

1.前臺持續進行調接口請求

2.使用websocket進行長連接獲取內容

我這種辦法是方案1,用websocket的話也可以,這塊我沒寫,後面有更新我再補充

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章