管理与服务器的交互主要是客户机库的工作,但大多数库也提供了一些对幕后发生的事情的了解。
例如,客户端库可以提供一种机制来获取连接的当前状态:
nc, err := nats.Connect("demo.nats.io", nats.Name("API Example"))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
getStatusTxt := func(nc *nats.Conn) string {
switch nc.Status() {
case nats.CONNECTED:
return "Connected"
case nats.CLOSED:
return "Closed"
default:
return "Other"
}
}
log.Printf("The connection is %v\n", getStatusTxt(nc))
nc.Close()
log.Printf("The connection is %v\n", getStatusTxt(nc))
如果对连接状态感兴趣,很可能对了解状态何时更改也有兴趣。大多数(如果不是全部的话)NATS客户端库提供了一种方法来监听与连接及其状态相关的事件。
这些侦听器的实际API依赖于语言,但以下示例显示了一些更常见的用例。有关更具体的说明,请参阅正在使用的客户端库的API文档。
连接事件可能包括正在关闭、断开或重新连接的连接。重新连接涉及断开连接和连接,但根据库的实现,在客户端尝试查找服务器或服务器重新启动时,还可能包括多个断开连接。
// There is not a single listener for connection events in the NATS Go Client.
// Instead, you can set individual event handlers using:
nc, err := nats.Connect("demo.nats.io",
nats.DisconnectErrHandler(func(_ *nats.Conn, err error) {
log.Printf("client disconnected: %v", err)
}),
nats.ReconnectHandler(func(_ *nats.Conn) {
log.Printf("client reconnected")
}),
nats.ClosedHandler(func(_ *nats.Conn) {
log.Printf("client closed")
}))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
DisconnectHandler(cb ConnHandler)
ReconnectHandler(cb ConnHandler)
ClosedHandler(cb ConnHandler)
DiscoveredServersHandler(cb ConnHandler)
ErrorHandler(cb ErrHandler)
使用群集时,可以添加或更改服务器。某些客户端允许您侦听此通知:
// Be notified if a new server joins the cluster.
// Print all the known servers and the only the ones that were discovered.
nc, err := nats.Connect("demo.nats.io",
nats.DiscoveredServersHandler(func(nc *nats.Conn) {
log.Printf("Known servers: %v\n", nc.Servers())
log.Printf("Discovered servers: %v\n", nc.DiscoveredServers())
}))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
客户端库可以将服务器到客户端的错误与事件分开。许多服务器事件不由应用程序代码处理,导致连接关闭。监听错误对于调试问题非常有用。
// Set the callback that will be invoked when an asynchronous error occurs.
nc, err := nats.Connect("demo.nats.io",
nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, err error) {
log.Printf("Error: %v", err)
}))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
NATS的设计目的是在服务器中快速移动消息。因此,NATS依赖于应用程序来考虑和响应不断变化的消息速率。服务器会进行一些阻抗匹配,但如果客户端速度太慢,服务器最终会通过关闭连接来切断它们。这些切断的连接被称为慢用户。
一些库处理突发消息通信的一种方法是缓冲订阅的传入消息。因此,如果一个应用程序每秒可以处理10条消息,并且有时每秒接收20条消息,则库可以保存额外的10条消息,以便给应用程序足够的时间来赶上。对于服务器,应用程序似乎正在处理消息并认为连接正常。大多数客户端库都会通知应用程序有一个SlowConsumer错误并丢弃消息。
从服务器接收和删除消息可以保持与服务器的连接正常,但会创建应用程序需求。有几种常见的模式:
缓存传入消息的库可以在传入队列或挂起消息上提供两个控件。如果问题是突发的发布服务器,而不是持续的性能不匹配,则这些功能非常有用。在生产中禁用这些限制可能是危险的,尽管将这些限制设置为0可能有助于发现问题,但在生产中也是危险的。
检查库文档中的默认设置,并支持禁用这些限制。
传入的缓存通常是每个订阅服务器的缓存,但请再次检查客户端库的特定文档。
传入队列可以受到限制的第一种方式是消息计数。第二种限制传入队列的方法是按总大小。例如,要将传入缓存限制为1000条消息或5mb(以先到者为准):
nc, err := nats.Connect("demo.nats.io")
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Subscribe
sub1, err := nc.Subscribe("updates", func(m *nats.Msg) {
})
if err != nil {
log.Fatal(err)
}
// Set limits of 1000 messages or 5MB, whichever comes first
sub1.SetPendingLimits(1000, 5*1024*1024)
// Subscribe
sub2, err := nc.Subscribe("updates", func(m *nats.Msg) {
})
if err != nil {
log.Fatal(err)
}
// Set no limits for this subscription
sub2.SetPendingLimits(-1, -1)
// Close the connection
nc.Close()
当检测到慢速使用者并且消息即将丢弃时,库可能会通知应用程序。此过程可能与其他错误类似,也可能涉及自定义回调。
有些库(如Java)不会在每个丢弃的消息上发送此通知,因为这可能会比较烦躁。相反,通知可能会在订户每次落后时发送一次。库还可以提供一种方法来获取丢弃消息的计数,以便应用程序至少可以检测到问题正在发生。
// Set the callback that will be invoked when an asynchronous error occurs.
nc, err := nats.Connect("demo.nats.io", nats.ErrorHandler(logSlowConsumer))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection