20、Error Conditions 错误情况
现在让我们看看在消息系统中可能发生的各种错误,看下EasyNetQ如何处理它们。
你的订阅者挂了
假设你写了一个windows 服务程序来订阅NewCustomerMessage消息。如果这个服务程序挂了会发生什么呢?
为了性能,EasyNetQ为订阅实现了一个内部使用的内存消息队列。EasyNetQ通过网络从RabbitMQ的队列接收到消息后,会把消息放到这个内存队列中。单个订阅线程依次将消息从内存队列中取走,并将它们传递给你所提供的回调方法(委托)——即消息处理方法。
一旦这个回调方法结束后,EasyNetQ会发送'Ack'确认给RabbitMQ。在收到“Ack”确认之前,RabbitMQ不会从其队列中删除消息。如果你的订阅者在处理这个消息时(比如执行回调方法时)挂掉了,这个消息(以及EasyNetQ内存队列中的所有消息)仍呆在RabbitMQ的队列中。直到你的订阅者(windows 服务程序)再次连接上后,RabbitMQ将会重新发送这些消息。
发布快,消费慢怎么办?
EasyNetQ利用RabbitMQ的QoS设置(服务质量),把prefetch-count值设为一个比较合理的值(当前是50)。这意味着最多50条消息存在消费者的内存队列中。这可以防止在订阅应用程序中出现内存溢出的异常(如果prefetch-count值过大,RabbitMQ会发送过多的消息给你,实际上你又一时消费不掉)。
一旦未Ack确认数量累积到这个值后,RabbitMQ将停止为该消费者发送消息,后续消息存储在RabbitMQ队列中。(或者有其他消费者时,发给他们。而不再轮转发送给未Ack的消费者,直到它Ack确认后减少未确认数量)
如果你的消费速度永远跟不上发布速度,最终这个队列将会吃掉RabbitMQ服务器上的所有磁盘空间。你应该在这个地方做下监控,确保在发生这种情况发生前你能够得到警报。
要解决消费速度跟不上发布速度的问题,你要考虑Worker模式,启动更多消费者(即Worker)并行处理消息。处理完成后汇总结果。
在订阅者和RabbitMQ之间发生网络故障
如前面文章《5、Connecting to RabbitMQ连接到RabbitMQ服务器》所述,EasyNetQ实现了一个延迟连接策略。这个策略假设RabbitMQ不总是可用的。当你第一次调用RabbitHutch.CreateBus()方法(去连接RabbitMQ)时,EasyNetQ进入一个尝试连接的循环,如果在连接字符串指定的网络地址上没有可用的RabbitMQ服务器,那么你将会从消息信息日志中看到'Trying to Connect'。
即使RabbitMQ服务器不可用,订阅者仍然能够使用bus.Subscribe方法去订阅。这个订阅的细节会被EasyNetQ缓存起来,当RabbitMQ恢复可用时,这个不断尝试连接的循环将会成功地连接到RabbitMQ,然后所有缓存的订阅会被创建。
同样,当EasyNetQ和RabbitMQ连接中断时,它将返回到循环连接,你在日志中看'Trying to Connect'信息。一旦连接重新建立,被缓存的订阅者将再次被建立。最后结论就是:不管是你的网络连接不稳定,还是你需要重启RabbitMQ服务,只要用EasyNetQ你的订阅者都能正常运行。
当消费一个消息时,订阅者回调方法抛出异常
如果你的订阅者回调方法抛出异常,EasyNetQ会拿着这个正在被消费的消息,封包到一个指定错误消息中。这个错误消息将会被发布到RabbitMQ上由EasyNetQ(订阅者)创建的错误队列(名字是EasyNetQ_Default_Error_Queue)。你应该监控这个错误队列中的所有消息。这个错误消息包括所有必要的信息,例如需要重新发布的原始消息,以及异常的类型,异常信息和堆栈信息。你可能使用EasyNetQ.Hosepipe工具重新发布错误信息。参考下一篇文章EasyNetQ.Hosepipe
英文地址:https://github.com/EasyNetQ/EasyNetQ/wiki/Error-Conditions