1. 背景
項目中有這樣一個需求:兩個服務之間需要傳遞視頻監控平臺的設備及分組信息。一個視頻監控平臺中通常有10萬數量級的監控設備信息,每個設備的詳細信息可能有二三十個字段,再加上分組信息,傳遞的信息量接近200M左右。以前使用發送多包消息的方式傳遞,這樣發送方與接收方需要以事務的方式處理消息,如接收方沒有收完整,就需要等待超時。如果傳輸過程中產生了丟包,則發送方需要重發,發送方與接收方的邏輯就變得比較複雜。
目前我們使用kafka作爲項目中的消息中間件,考慮將所有發送的設備信息打成一包消息收發,這樣處理上變得簡單。
kafka官方推薦使用消息大小不超過1M時吞吐量最佳。要發送達200M的消息,需要在服務器端,及生產者端及消費者端做一些配置與策略。
2. 配置
2.1 生產者與消費者的配置
需要設置生產者和消費者的配置以接收和發送大消息,在項目的springboot工程中,application.yml文件配置如下:
kafka:
producer:
bootstrap-servers: 172.16.64.159:9092
buffer-memory: 536870912
properties:
request.timeout.ms: 900000
max.request.size: 536870912
compression.type: gzip
consumer:
bootstrap-servers: 172.16.64.159:9092
auto-offset-reset: latest
properties:
max.poll.records: 1
request.timeout.ms: 900000
fetch.max.bytes: 536870912
max.partition.fetch.bytes: 536870912
enable-auto-commit: true
這裏生產者的buffer-memory被設爲500M;消息的壓縮類型被設置爲gzip,可保證傳輸與持久化時壓縮數據量;由於發送消息的時間可能會因消息量變長,因此請求超時時間設置爲900秒。消費者的max.poll.records被設爲1,表示每次只取一個消息;同樣,fetch.max.bytes和max.partition.fetch.bytes都設置爲500M。
2.2 kafka服務器端配置
kafka服務器 端的配置文件也需要修改,對server.properties配置文件配置項更改如下:
message.max.bytes=536870912
replica.fetch.max.bytes=536870912
request.timeout.ms=900000
# The send buffer (SO_SNDBUF) used by the socket server
socket.send.buffer.bytes=1024000
# The receive buffer (SO_RCVBUF) used by the socket server
socket.receive.buffer.bytes=1024000
# The maximum size of a request that the socket server will accept (protection against OOM)
socket.request.max.bytes=536870912
3. 測試情況
從測試情況來看,使用壓縮機制以後,150M的JSON格式消息可壓縮到十分之一,在應用服務與kafka服務器在同一局域網情況下,收發時間並不長。但在應用服務消費時,由於解壓消息,反序列化消息到對象,基本上會消耗至少3倍消息的堆內存,使用jconsole監控,如下圖所示:
可以看到CPU和堆內存出現幾次峯值的地方都是在消費大消息時產生,若JVM初始堆內存設置的不夠大或已經使用較多,會出現OOM的錯誤,因此建議在每次消費大消息之後手工GC一下(system.gc())。
4. 思考
由於我們的項目每天只同步一次監控平臺設備信息,因此kafka每天只收發大消息一次,系統還可支撐。如果要頻繁收發這種方法不可取,容易引起應用OOM,和kafka的崩潰與效率低下。其實我認爲使用折中方案,將應用的大消息按邏輯劃分爲多個包,包的數目在10個左右,包的大小在10M左右,這樣在效率與系統穩定性方面會更好。