监控
该驱动允许应用程序在发生某些事件时被通知。这些事件组织成以下类别
命令监控
拓扑生命周期
服务器生命周期
服务器心跳
连接池和连接
拓扑和服务器事件是服务器发现和监控(SDAM)的一部分。
命令监控
所有由用户发起发送到服务器的命令都会发布可以被订阅的事件,以获取更详细的信息。监控API为每个命令发布一个保证启动事件,然后是一个成功或失败事件。订阅者必须实现3个方法started
、succeeded
和failed
,每个方法都接受一个事件的单个参数。以下是基于驱动内部使用的日志订阅者示例的日志订阅者示例
class CommandLogSubscriber include Mongo::Loggable def started(event) # The default inspection of a command which is a BSON document gets # truncated in the middle. To get the full rendering of the command, the # ``to_json`` method can be called on the document. log_debug("#{prefix(event)} | STARTED | #{format_command(event.command.to_json)}") end def succeeded(event) log_debug("#{prefix(event)} | SUCCEEDED | #{event.duration}s") end def failed(event) log_debug("#{prefix(event)} | FAILED | #{event.message} | #{event.duration}s") end private def logger Mongo::Logger.logger end def format_command(args) begin args.inspect rescue Exception '<Unable to inspect arguments>' end end def format_message(message) format("COMMAND | %s".freeze, message) end def prefix(event) "#{event.address.to_s} | #{event.database_name}.#{event.command_name}" end end
要注册自定义订阅者,您可以为所有客户端全局或按客户端进行
subscriber = CommandLogSubscriber.new Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::COMMAND, subscriber) client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) client.subscribe( Mongo::Monitoring::COMMAND, subscriber )
示例输出
D, [2018-09-23T13:47:31.258020 #4692] DEBUG -- : COMMAND | 127.0.0.1:27027 | test.hello | STARTED | {"hello"=>1, "$readPreference"=>{"mode"=>"primary"}, "lsid"=>{"id"=><BSON::Binary:0x47111693353080 type=uuid data=0x730341e880dc40a2...>}} D, [2018-09-23T13:47:31.259145 #4692] DEBUG -- : COMMAND | 127.0.0.1:27027 | test.hello | SUCCEEDED | 0.000791175s
服务器发现和监控
Ruby驱动实现服务器发现与监控(SDAM)规范. 并向应用程序提供以下事件
拓扑打开
服务器打开
服务器描述更改
拓扑更改
服务器关闭
拓扑关闭
心跳事件(如下文单独章节所述)
对于除心跳事件之外的所有事件,每个事件订阅者将调用 succeeded
方法,事件作为唯一参数。事件可用数据不同,因此要记录事件,需要为每种事件类型创建一个单独的类。一个简单的SDAM日志订阅者可能如下所示
class SDAMLogSubscriber include Mongo::Loggable def succeeded(event) log_debug(format_event(event)) end private def logger Mongo::Logger.logger end def format_message(message) format("SDAM | %s".freeze, message) end end class TopologyOpeningLogSubscriber < SDAMLogSubscriber private def format_event(event) "Topology type '#{event.topology.display_name}' initializing." end end class ServerOpeningLogSubscriber < SDAMLogSubscriber private def format_event(event) "Server #{event.address} initializing." end end class ServerDescriptionChangedLogSubscriber < SDAMLogSubscriber private def format_event(event) "Server description for #{event.address} changed from " + "'#{event.previous_description.server_type}' to '#{event.new_description.server_type}'." end end class TopologyChangedLogSubscriber < SDAMLogSubscriber private def format_event(event) if event.previous_topology != event.new_topology "Topology type '#{event.previous_topology.display_name}' changed to " + "type '#{event.new_topology.display_name}'." else "There was a change in the members of the '#{event.new_topology.display_name}' " + "topology." end end end class ServerClosedLogSubscriber < SDAMLogSubscriber private def format_event(event) "Server #{event.address} connection closed." end end class TopologyClosedLogSubscriber < SDAMLogSubscriber private def format_event(event) "Topology type '#{event.topology.display_name}' closed." end end
全局订阅SDAM事件
topology_opening_subscriber = TopologyOpeningLogSubscriber.new server_opening_subscriber = ServerOpeningLogSubscriber.new server_description_changed_subscriber = ServerDescriptionChangedLogSubscriber.new topology_changed_subscriber = TopologyChangedLogSubscriber.new server_closed_subscriber = ServerClosedLogSubscriber.new topology_closed_subscriber = TopologyClosedLogSubscriber.new Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::TOPOLOGY_OPENING, topology_opening_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_OPENING, server_opening_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, server_description_changed_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::TOPOLOGY_CHANGED, topology_changed_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_CLOSED, server_closed_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::TOPOLOGY_CLOSED, topology_closed_subscriber)
为单个客户端订阅SDAM事件稍微复杂一些,因为事件可能在客户端构建期间发布
topology_opening_subscriber = TopologyOpeningLogSubscriber.new server_opening_subscriber = ServerOpeningLogSubscriber.new server_description_changed_subscriber = ServerDescriptionChangedLogSubscriber.new topology_changed_subscriber = TopologyChangedLogSubscriber.new server_closed_subscriber = ServerClosedLogSubscriber.new topology_closed_subscriber = TopologyClosedLogSubscriber.new sdam_proc = Proc.new do |client| client.subscribe(Mongo::Monitoring::TOPOLOGY_OPENING, topology_opening_subscriber) client.subscribe(Mongo::Monitoring::SERVER_OPENING, server_opening_subscriber) client.subscribe(Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, server_description_changed_subscriber) client.subscribe(Mongo::Monitoring::TOPOLOGY_CHANGED, topology_changed_subscriber) client.subscribe(Mongo::Monitoring::SERVER_CLOSED, server_closed_subscriber) client.subscribe(Mongo::Monitoring::TOPOLOGY_CLOSED, topology_closed_subscriber) end client = Mongo::Client.new(['127.0.0.1:27017'], database: 'test', sdam_proc: sdam_proc)
示例输出
D, [2018-10-09T13:58:03.489461 #22079] DEBUG -- : SDAM | Topology type 'Unknown' initializing. D, [2018-10-09T13:58:03.489699 #22079] DEBUG -- : SDAM | Server 127.0.0.1:27100 initializing. D, [2018-10-09T13:58:03.491384 #22079] DEBUG -- : SDAM | Server description for 127.0.0.1:27100 changed from 'unknown' to 'unknown'. D, [2018-10-09T13:58:03.491642 #22079] DEBUG -- : SDAM | Server localhost:27100 initializing. D, [2018-10-09T13:58:03.493199 #22079] DEBUG -- : SDAM | Server description for localhost:27100 changed from 'unknown' to 'primary'. D, [2018-10-09T13:58:03.493473 #22079] DEBUG -- : SDAM | Server localhost:27101 initializing. D, [2018-10-09T13:58:03.494874 #22079] DEBUG -- : SDAM | Server description for localhost:27101 changed from 'unknown' to 'secondary'. D, [2018-10-09T13:58:03.495139 #22079] DEBUG -- : SDAM | Server localhost:27102 initializing. D, [2018-10-09T13:58:03.496504 #22079] DEBUG -- : SDAM | Server description for localhost:27102 changed from 'unknown' to 'secondary'. D, [2018-10-09T13:58:03.496777 #22079] DEBUG -- : SDAM | Topology type 'Unknown' changed to type 'ReplicaSetNoPrimary'. D, [2018-10-09T13:58:03.497306 #22079] DEBUG -- : SDAM | Server 127.0.0.1:27100 connection closed. D, [2018-10-09T13:58:03.497606 #22079] DEBUG -- : SDAM | Topology type 'ReplicaSetNoPrimary' changed to type 'ReplicaSetWithPrimary'. # client.close D, [2018-10-09T13:58:05.342057 #22079] DEBUG -- : SDAM | Server localhost:27100 connection closed. D, [2018-10-09T13:58:05.342299 #22079] DEBUG -- : SDAM | Server localhost:27101 connection closed. D, [2018-10-09T13:58:05.342565 #22079] DEBUG -- : SDAM | Server localhost:27102 connection closed. D, [2018-10-09T13:58:05.342693 #22079] DEBUG -- : SDAM | Topology type 'ReplicaSetWithPrimary' closed.
注意
:sdam_proc
客户端选项仅适用于在构建期间提供的客户端。当通过 Client#with
调用更改某些客户端选项时,驱动程序可能会创建一个新的集群,并带有默认的事件订阅者集。如果发生这种情况,提供的 :sdam_proc
不会被调用,应用程序可能会错过事件。
服务器心跳
应用程序可以通过订阅 SERVER_HEARTBEAT 主题来接收每个服务器的心跳通知。服务器心跳监听器必须实现三个方法:started
、succeeded
和 failed
。每次心跳都会在监听器上调用 started
方法,然后根据心跳的结果调用 succeeded
或 failed
方法。
所有心跳事件都包含发送心跳的服务器地址。成功和失败事件包含用于 hello 或 legacy hello 命令的往返时间。失败事件还包含在 hello 或 legacy hello 命令执行过程中引发的异常实例。请查阅 ServerHeartbeatStarted、ServerHeartbeatSucceeded 和 ServerHeartbeatFailed 的 API 文档以获取事件属性详细信息。
以下是一个示例心跳事件订阅器
class HeartbeatLogSubscriber include Mongo::Loggable def started(event) log_debug("#{event.address} | STARTED") end def succeeded(event) log_debug("#{event.address} | SUCCEEDED | #{event.duration}s") end def failed(event) log_debug("#{event.address} | FAILED | #{event.error.class}: #{event.error.message} | #{event.duration}s") end private def logger Mongo::Logger.logger end def format_message(message) format("HEARTBEAT | %s".freeze, message) end end
类似于命令事件,应用程序可以全局订阅心跳事件或特定客户端的心跳事件
subscriber = HeartbeatLogSubscriber.new Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_HEARTBEAT, subscriber) client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) client.subscribe( Mongo::Monitoring::SERVER_HEARTBEAT, subscriber )
示例输出
D, [2018-09-23T13:44:10.707018 #1739] DEBUG -- : HEARTBEAT | 127.0.0.1:27027 | STARTED D, [2018-09-23T13:44:10.707778 #1739] DEBUG -- : HEARTBEAT | 127.0.0.1:27027 | SUCCEEDED | 0.000772381s
心跳事件间隔
当连接到 MongoDB 4.2 及更早版本的服务器时,Ruby 驱动默认在 :heartbeat_frequency
(Ruby 客户端选项)秒内发出心跳,心跳是非重叠的(心跳的成功事件保证在下一个心跳的 started
事件发布之前发布)。当连接到 MongoDB 4.4 及更高版本的服务器时,驱动程序使用多个监控线程和一个更复杂的心跳协议,旨在更快地检测服务器状态的变化;因此,心跳事件间隔可能更不规则,心跳事件可能会重叠。具体来说,一个 awaited heartbeat 可能在一个 non-awaited heartbeat 正在进行时开始或完成,反之亦然。使用 ServerHeartbeatStarted#awaited?
、ServerHeartbeatSucceeded#awaited?
和 ServerHeartbeatFailed#awaited?
方法来区分非等待和等待的心跳。
当客户端尝试执行操作但没有合适的服务器时,部署会更频繁地扫描 - 每个服务器可以每 500 毫秒被轮询一次。应用程序还可以请求手动扫描特定服务器;驱动程序强制执行扫描之间的最小间隔为 500 毫秒。
连接池和连接监控
每个客户端为其所知的每个服务器维护一个连接池,并发布连接池和单个连接的事件。要订阅这些事件,定义一个实现方法 pubished
的订阅者类,该方法接受一个参数,表示正在发布的事件。请注意,驱动程序的后续版本可能通过此机制引入其他事件。
驱动程序目前实现了以下事件,遵循 CMAP 规范
PoolCreated
PoolCleared
PoolClosed
ConnectionCreated
ConnectionReady
ConnectionClosed
ConnectionCheckOutStarted
ConnectionCheckOutFailed
ConnectionCheckOutSucceeded
ConnectionCheckedIn
驱动程序提供了一个日志订阅者,可用于记录所有连接池和连接相关事件。此订阅者默认不启用,因为它将为应用程序执行的每个操作创建日志条目。要全局或按客户端启用此订阅者
Mongo::Monitoring::Global.subscribe( Mongo::Monitoring::CONNECTION_POOL, Mongo::Monitoring::CmapLogSubscriber.new) client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) subscriber = Mongo::Monitoring::CmapLogSubscriber.new client.subscribe( Mongo::Monitoring::CONNECTION_POOL, subscriber )
示例输出
D, [2019-05-06T17:23:21.595412 #8576] DEBUG -- : MONGODB | EVENT: #<PoolCreated address=127.0.0.1:27741 options={...}> D, [2019-05-06T17:23:21.595584 #8576] DEBUG -- : MONGODB | EVENT: #<PoolCleared address=127.0.0.1:27741> D, [2019-05-06T17:23:21.603549 #8576] DEBUG -- : MONGODB | EVENT: #<PoolCreated address=localhost:27741 options={...}> D, [2019-05-06T17:23:21.603616 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionCheckOutStarted address=localhost:27741> D, [2019-05-06T17:23:21.603684 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionCreated address=localhost:27741 connection_id=1> D, [2019-05-06T17:23:21.604079 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionCheckedOut address=localhost:27741 connection_id=1> D, [2019-05-06T17:23:21.605759 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionReady address=localhost:27741 connection_id=1> D, [2019-05-06T17:23:21.605784 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionCheckedIn address=localhost:27741 connection_id=1> D, [2019-05-06T17:23:21.605817 #8576] DEBUG -- : MONGODB | EVENT: #<PoolCleared address=localhost:27741> D, [2019-05-06T17:23:21.605852 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionClosed address=localhost:27741 connection_id=1 reason=stale>
禁用监控
要关闭监控,将客户端监控选项设置为 false
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test', :monitoring => false )
排除和编辑的事件
Ruby 驱动程序不会通过命令监控机制发布某些事件,偶尔还会编辑这些事件。
如果命令属于特定子集的编辑命令,或者包含触发有效载荷编辑的密钥,出于安全考虑,将提供空的有效载荷。可以通过将环境变量
MONGO_RUBY_DRIVER_UNREDACT_EVENTS
设置为1
、true
或yes
来访问完整的有效载荷。以下命令将被编辑authenticate
saslStart
saslContinue
getnonce
createUser
updateUser
copydbgetnonce
copydbsaslstart
copydb
如果命令是握手命令,即非监控连接上的
ismaster
或hello
,则完全不发布事件。通过监控连接(如
ismaster
和hello
)发送的命令不发布命令监控事件。相反,每次检查服务器时都会发布服务器心跳事件。服务器心跳事件不包含命令或回复有效载荷。如果命令是握手命令,并且
speculativeAuthenticate
选项为true
,则命令将被编辑,并提供空的有效载荷。