最近項目需要進行實時讀取服務端信息,在網上看到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的話也可以,這塊我沒寫,後面有更新我再補充