通過上一篇文章對 dq
生產者的分析,我們知道 dq
是基於 beanstalk
的封裝。至於 生產者 我們在後續的文章繼續分享,本篇文章先來分析一下 go-queue
中的 kq
。
kq
基於 kafka
封裝,設計之初是為了使 kafka
的使用更人性化。那就來看看 kq
的使用。
上手使用
func main() {
// 1. 初始化
pusher := kq.NewPusher([]string{
"127.0.0.1:19092",
"127.0.0.1:19092",
"127.0.0.1:19092",
}, "kq")
ticker := time.NewTicker(time.Millisecond)
for round := 0; round < 3; round++ {
select {
case <-ticker.C:
count := rand.Intn(100)
m := message{
Key: strconv.FormatInt(time.Now().UnixNano(), 10),
Value: fmt.Sprintf("%d,%d", round, count),
Payload: fmt.Sprintf("%d,%d", round, count),
}
body, err := json.Marshal(m)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
// 2. 寫入
if err := pusher.Push(string(body)); err != nil {
log.Fatal(err)
}
}
}
}
將 kafka cluster
配置以及 topic
傳入,你就得到一個操作 kafka
的 push operator
。
至於寫入訊息,簡單的呼叫 pusher.Push(msg)
就行。是的,就這麼簡單!
當然,目前只支援單個
msg
寫入。可能有人會疑惑,那就繼續往下看,為什麼只能一條一條寫入?
初始化
一起看看 pusher
初始化哪些步驟:
NewPusher(clusterAddrs, topic, opts...)
|- kafka.NewWriter(kfConfig) // 與 kf 之前的連線
|- executor = executors.NewChunkExecutor() // 設定內部寫入的executor為位元組數定量寫入
- 建立與
kafka cluster
的連線。此處肯定就要傳入kafka config
; - 設定內部暫存區的寫入函式以及重新整理規則。
使用 chunkExecutor
作用不言而喻:將隨機寫 -> 批量寫,減少 I/O 消耗;同時保證單次寫入不能超過預設的 1M
或者自己設定的最大寫入位元組數。
其實再往 chunkExecutor
內部看,其實每次觸發插入有兩個指標:
maxChunkSize
:單次最大寫入位元組數flushInterval
:重新整理暫存訊息插入的間隔時間
在觸發寫入,只要滿足任意一個指標都會執行寫入。同時在 executors
都有設定插入間隔時間,以防暫存區寫入阻塞而暫存區內訊息一直不被重新整理清空。
更多關於
executors
可以參看以下:zeromicro.github.io/go-zero/execut...
生產者插入
根據上述初始化對 executors
介紹,插入過程中也少不了它的配合:
func (p *Pusher) Push(v string) error {
// 1. 將 msg -> kafka 內部的 Message
msg := kafka.Message{
Key: []byte(strconv.FormatInt(time.Now().UnixNano(), 10)),
Value: []byte(v),
}
// 使用 executor.Add() 插入內部的 container
// 當 executor 初始化失敗或者是內部發生錯誤,也會將 Message 直接插入 kafka
if p.executor != nil {
return p.executor.Add(msg, len(v))
} else {
return p.produer.WriteMessages(context.Background(), msg)
}
}
過程其實很簡單。那 executors.Add(msg, len(msg))
是怎麼把 msg
插入到 kafka
呢?
插入的邏輯其實在初始化中就宣告瞭:
pusher.executor = executors.NewChunkExecutor(func(tasks []interface{}) {
chunk := make([]kafka.Message, len(tasks))
// 1
for i := range tasks {
chunk[i] = tasks[i].(kafka.Message)
}
// 2
if err := pusher.produer.WriteMessages(context.Background(), chunk...); err != nil {
logx.Error(err)
}
}, newOptions(opts)...)
- 觸發插入時,將暫存區中儲存的
[]msg
依次拿出,作為最終插入訊息集合; - 將上一步的訊息集合,作為一個批次插入
kafka
的topic
中
這樣 pusher -> chunkExecutor -> kafka
一個鏈路就出現了。下面用一張圖形象表達一下:
框架地址
同時在 go-queue
也大量使用 go-zero
的 批量處理工具庫 executors
。
歡迎使用 go-zero
& go-queue
並 star 支援我們!一起構建 go-zero
生態!?
本作品採用《CC 協議》,轉載必須註明作者和本文連結