前言
for循環(huán)是Go語(yǔ)言唯一的循環(huán)結(jié)構(gòu),最近在做一個(gè)基于RabbitMQ的應(yīng)用,由于官方的qos沒(méi)有g(shù)olang的版本,所以出了一點(diǎn)問(wèn)題。
問(wèn)題代碼如下:
_, ch, err := component.NewRabbitMQ()
if err != nil {
panic(err)
}
if err := ch.Qos(10, 0, true); err != nil {
panic(err)
}
msgs, err := ch.Consume("push", "", false, false, false, false, nil)
if err != nil {
panic(err)
}
for m := range msgs {
go func(d *amqp.Delivery) {
defer func() { d.Ack(false) }
// 處理消息
}(m)
}
發(fā)現(xiàn)消費(fèi)到10條消息,進(jìn)程就退出了,但是exit code為0,表示系統(tǒng)是正常退出,由于做了日志記錄可以確定消費(fèi)了10條,所以初步確定是qos相關(guān)問(wèn)題。
排查過(guò)程
- 首先是把d的tag打印出來(lái),發(fā)現(xiàn)全部是一樣的,可以確定是重復(fù)的一條消息
- 一開(kāi)始想到可能是經(jīng)典的go協(xié)程執(zhí)行在for循環(huán)結(jié)束以后導(dǎo)致的,但是看我的代碼不屬于這種情況,有使用m保證每一條消息都是不同循環(huán)傳入的。所以判斷可能是for循環(huán)的傳遞問(wèn)題。
- 確定方向之后開(kāi)始寫了一個(gè)測(cè)試項(xiàng)目用來(lái)驗(yàn)證我的想法是否正確。
測(cè)試代碼
package main
import "fmt"
func main() {
ch := make(chan int, 10)
for i := 0; i 10; i++ {
ch - i
}
close(ch)
for v := range ch {
fmt.Println(v)
}
}
執(zhí)行輸出
0xc420086008
0xc420086008
0xc420086008
0xc420086008
0xc420086008
0xc420086008
0xc420086008
0xc420086008
0xc420086008
0xc420086008
到這里才煥然大悟,for循環(huán)中,如果循環(huán)變量不是指針,那么每次的變量是同一個(gè),不過(guò)值變了。,所以上例中的RabbitMQ go協(xié)程消費(fèi)消息那里,需要直接傳遞值而不是指針,經(jīng)過(guò)測(cè)試之后發(fā)現(xiàn),問(wèn)題確實(shí)解決了。
題外話
測(cè)試代碼那里,如果不close掉channel是會(huì)發(fā)生死鎖的,原因是 當(dāng)for循環(huán)讀完channel的10個(gè)值之后會(huì)繼續(xù)嘗試讀取下一個(gè),而由于channel為空又沒(méi)關(guān)閉,會(huì)一直阻塞形成死鎖
TOOD
研究RabbitMQ Consumer部分的源碼來(lái)看看消費(fèi)channel被關(guān)閉的問(wèn)題。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
您可能感興趣的文章:- 基于golang channel實(shí)現(xiàn)的輕量級(jí)異步任務(wù)分發(fā)器示例代碼
- golang實(shí)現(xiàn)基于channel的通用連接池詳解
- Golang優(yōu)雅關(guān)閉channel的方法示例
- golang中單向channel的語(yǔ)法介紹
- golang判斷chan channel是否關(guān)閉的方法
- Golang中channel使用的一些小技巧
- Golang中channel的原理解讀(推薦)