在上一章中SpringBoot整合RabbitMQ,已經詳細介紹了訊息佇列的作用,這一種我們直接來學習SpringBoot如何整合kafka傳送訊息。
kafka簡介
kafka是用Scala和Java語言開發的,高吞吐量的分散式訊息中介軟體。高吞吐量使它在大資料領域具有天然的優勢,被廣泛用來記錄日誌。
kafka架構分析
注1:圖中的紅色箭頭表示訊息的流動過程,藍色表示分割槽備份,綠色表示kafka叢集註冊到zookeeper。
注2:在kafka0.9版本之前,消費者消費訊息的位置記錄在zookeeper中,在0.9版本之後,消費訊息的位置記錄在kafka的一個topic上。
kafka名詞簡介:
- Producer:訊息生產者
- Consumer:訊息消費者
- Consumer Group(CG):消費者組,一個topic可以有多個CG,每個Partition只會把訊息傳送給GG中的一個Consumer
- Broker:一臺kafka伺服器就是一個broker,一個broker有多個topic
- Topic:訊息主題,訊息分類,可看作佇列
- Partition:分割槽,為了實現擴充套件,一個大的topic可能分佈到多個broker上,一個topic可以分為多個partition,partition中的每條訊息都會被分配一個有序的id(offset),每個partiton中的訊息是有序的。
- Offset:kafka的儲存檔案都是按照offset.kafka來命名的,方便查詢,第一個offset為0000000000.kafka。
- Leader:分割槽具有被備份,主分割槽
- Follower:從分割槽
1. 生產者分割槽策略
- 指定分割槽。
- 沒有指定分割槽但有key值,將key的hash值與當前topic的分割槽個數進行取餘得到分割槽。
- 如果既沒有指定分割槽又沒有指定key,第一次呼叫時隨機生成一個整數(以後呼叫每次在這個整數上自增),將這個隨機數與該topic的分割槽數取餘得到分割槽。
2. 訊息可靠性問題
採用ack確認機制來保證訊息的可靠性。
kafka在傳送訊息後會同步到其他分割槽副本,等所有副本都接收到訊息後,kafka才會傳送ack進行確認。採用這種模式的劣勢就是當其中一個副本當機後,則訊息生產者就不會收到kafka的ack。
kafka採用ISR來解決這個問題。
ISR:Leader維護的一個和leader保持同步的follower集合。
當ISR中的folower完成資料同步之後,leader就會向follower傳送ack,如果follower長時間未向leader同步資料,則該follower就會被踢出ISR,該時間閥值的設定引數為replica.lag.time.max.ms
,預設時間為10s,leader發生故障後,就會從ISR中選舉新的leader。
注:本文所講的kafka版本為0.11,在0.9版本以前成為ISR還有一個條件,就是同步訊息的條數。
ack引數配置
0:生產者不等待broker的ack。
1:leader分割槽接收到訊息向生產者傳送ack。
-1(all):ISR中的leader和follower同步成功後,向生產者傳送ack。
3. 訊息一致性問題
假如leader中有10條訊息,向兩個follower同步資料,follower A同步了8條,follower B同步了9條。這時候leader當機了,follower A和follower B中的訊息是不一致的,剩下兩個follower就會重新選舉出一個leader。
-
LEO(log end offset):每個副本的最後一個offset
-
HW(high watermark):所有副本中最小的offset
為了保證資料的一致性,所有的follower會將各自的log檔案高出HW的部分截掉,然後再從新的leader中同步資料。
4. 訊息重複性問題
在kafka0.11版本中引入了一個新特性:冪等性。啟用冪等性後,ack預設為-1。將生產者中的enable.idompotence
設定為true,即啟用了冪等性。
開啟冪等性的Producer在初始化的時候會被分配一個PID,發往同一Partition的訊息會附帶Sequence Number。Broker端會對<PID,Partition,SeqNumber>做快取,當具有相同主鍵的訊息提交時,Broker只會快取一條。但是每次重啟PID就會發生變化,因此只能保證一次會話同一分割槽的訊息不重複。
5. 消費者組分割槽分配策略
kafka有兩種分配策略,一種是RoundRobin,另一種是Range
RoundRobin是按照消費者組以輪詢的方式去給消費者分配分割槽的方式,前提條件是消費者組中的消費者需要訂閱同一個topic。
Range是kafka預設的分配策略,它是通過當前的topic按照一定範圍來分配的,假如有3個分割槽,消費者組有兩個消費者,則消費者A去消費1和2分割槽,消費者B去消費3分割槽。
6. 消費者offset維護
Kafka 0.9 版本之前,consumer預設將offset儲存在zookeeper中,0.9 版本開始,offset儲存在kafka的一個內建topic中,該topic為_consumer_offsets
。
7. 生產者事務
為了實現跨分割槽會話的事務,需要引入一個全域性唯一的Tracscation ID,並將Producer 獲得的PID與之繫結。這樣當Producer重啟後就可以通過正在進行的Transaction ID獲得原來的PID。
為了管理Transcation ID,kafka引入了一個新的元件Transcation Coordinator。Producer就是通過和Transcation Coordinator互動獲得Transction ID對應的任務狀態。
Spring Boot 整合kafka
1. 引入kafka依賴
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
2. 配置kafka服務資訊
spring:
kafka:
# kafka服務地址
bootstrap-servers: 47.104.155.182:9092
producer:
# 生產者訊息key序列化方式
key-serializer: org.apache.kafka.common.serialization.StringSerializer
# 生產者訊息value序列化方式
value-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer:
# 消費者組
group-id: test-consumer-group
# 消費者訊息value反序列化方式
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
# 消費者訊息value反序列化方式
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
3. 消費者
@Component
@Slf4j
@KafkaListener(topics = {"first-topic"},groupId = "test-consumer-group")
public class Consumer {
@KafkaHandler
public void receive(String message){
log.info("我是消費者,我接收到的訊息是:"+message);
}
}
4. 生產者
@RestController
public class Producer {
@Autowired
private KafkaTemplate kafkaTemplate;
@GetMapping("send")
public void send(){
String message = "你好,我是Java旅途";
// 第一個引數 topic
// 第二個引數 訊息
kafkaTemplate.send("first-topic",message);
}
}
此是spring-boot-route系列的第十四篇文章,這個系列的文章都比較簡單,主要目的就是為了幫助初次接觸Spring Boot 的同學有一個系統的認識。本文已收錄至我的github,歡迎各位小夥伴star
!
github:https://github.com/binzh303/spring-boot-route
點關注、不迷路
如果覺得文章不錯,歡迎關注、點贊、收藏,你們的支援是我創作的動力,感謝大家。
如果文章寫的有問題,請不要吝嗇,歡迎留言指出,我會及時核查修改。
如果你還想更加深入的瞭解我,可以微信搜尋「Java旅途」進行關注。回覆「1024」即可獲得學習視訊及精美電子書。每天7:30準時推送技術文章,讓你的上班路不在孤獨,而且每月還有送書活動,助你提升硬實力!