我目前在微服务架构上工作。 在我将NATS插入到项目中之前,我想用它来测试一些简单的场景。
在一个场景中,我有一个简单的发布者,它通过在localhost:4222上运行的基本Nats服务器上的for循环发布100.000条消息。
最大的问题是订户。当他收到30,000到40.000条消息时,我的整个main.go程序和所有其他的例程都停止并且什么都不做。我可以用ctrl + c退出。但发布者仍在继续发送消息。当我打开一个新的终端并启动一个新的用户实例时,一切都运行良好,直到订阅者收到大约30000条消息。最糟糕的是,甚至没有一个错误,也没有服务器上的日志,所以我不知道最新情况。
之后我尝试用QueueSubscribe方法替换Subscribe-method,一切正常。
Subscribe和QueueSubscribe之间的主要区别是什么?
NATS-Streaming是一个更好的机会吗?或者在哪些情况下我应该更喜欢Streaming和标准NATS-Server
这是我的代码:
出版商:
package main
import (
"fmt"
"log"
"time"
"github.com/nats-io/go-nats"
)
func main() {
go createPublisher()
for {
}
}
func createPublisher() {
log.Println("pub started")
nc, err := nats.Connect(nats.DefaultURL)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
msg := make([]byte, 16)
for i := 0; i < 100000; i++ {
nc.Publish("alenSub", msg)
if (i % 100) == 0 {
fmt.Println("i", i)
}
time.Sleep(time.Millisecond)
}
log.Println("pub finish")
nc.Flush()
}
订户:
package main
import (
"fmt"
"log"
"time"
"github.com/nats-io/go-nats"
)
var received int64
func main() {
received = 0
go createSubscriber()
go check()
for {
}
}
func createSubscriber() {
log.Println("sub started")
nc, err := nats.Connect(nats.DefaultURL)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
nc.Subscribe("alenSub", func(msg *nats.Msg) {
received++
})
nc.Flush()
for {
}
}
func check() {
for {
fmt.Println("-----------------------")
fmt.Println("still running")
fmt.Println("received", received)
fmt.Println("-----------------------")
time.Sleep(time.Second * 2)
}
}
答案 0 :(得分:1)
无限for
循环可能会使垃圾收集器挨饿:https://github.com/golang/go/issues/15442#issuecomment-214965471
我只需运行发布商即可重现此问题。要解决此问题,我建议您使用sync.WaitGroup
。以下是我更新注释中链接的代码以完成它的方法:
package main
import (
"fmt"
"log"
"sync"
"time"
"github.com/nats-io/go-nats"
)
// create wait group
var wg sync.WaitGroup
func main() {
// add 1 waiter
wg.Add(1)
go createPublisher()
// wait for wait group to complete
wg.Wait()
}
func createPublisher() {
log.Println("pub started")
// mark wait group done after createPublisher completes
defer wg.Done()
nc, err := nats.Connect(nats.DefaultURL)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
msg := make([]byte, 16)
for i := 0; i < 100000; i++ {
if errPub := nc.Publish("alenSub", msg); errPub != nil {
panic(errPub)
}
if (i % 100) == 0 {
fmt.Println("i", i)
}
time.Sleep(time.Millisecond * 1)
}
log.Println("pub finish")
errFlush := nc.Flush()
if errFlush != nil {
panic(errFlush)
}
errLast := nc.LastError()
if errLast != nil {
panic(errLast)
}
}
我建议同样更新上述订阅者代码。
Subscribe
和QueueSubscriber
之间的主要区别在于Subscribe
所有订阅者都会收到来自的所有邮件。在QueueSubscribe
中,每条消息只发送QueueGroup
中的一个订阅者。
NATS Streaming的其他功能的一些细节如下: https://nats.io/documentation/streaming/nats-streaming-intro/
我们看到NATS和NATS Streaming在从数据管道到控制平面的各种用例中使用。您的选择应该由您的用例需求驱动。
答案 1 :(得分:0)
如上所述,删除for {}循环。替换为runtime.Goexit()。
对于订阅者,您无需在Go例程中创建订阅者。异步订阅者已经有自己的Go例程用于回调。
还使用原子或互斥锁保护接收到的变量。
也请参见此处的示例。