RabbitMQ的Java客户端API指南

目前RabbitMQ官方的Java客户端版本已升至5.0.0,5.x系列的版本需要JDK 8支持,4.x系列的版本支持JDK 6。

1、综述
RabbitMQ Java客户端使用com.rabbitmq.client作为顶层包。有4个关键类和接口:Channel、Connection、ConnectionFactory、Consumer。其中,Channel提供了各种AMQP 0-9-1的协议操作;Connection用于创建信道、注册连接的生命周期事件处理器、关闭连接;ConnectionFactory用于获取Connection,并提供连接配置功能,如配置连接的虚拟主机、用户名等。

2、连接到消息代理
[1] 新建ConnectionFactory
[2] 设置ConnectionFactory,如主机、端口、用户名、密码、虚拟主机等
[3] 获取Connection
[4] 创建信道
[5] 利用信道进行各种协议操作...
[6] 关闭信道
[7] 关闭连接
说明:在设置ConnectionFactory时,可调用方法或采用URI形式进行设置。另外,当连接关闭时,会自动关闭信道,所以在关闭连接前关闭信道不是严格必须的,但通常是一种好的实践。

3、使用交换器和队列
在使用交换器和队列前必须进行声明,声明是为了确保在使用前这些交换器或队列存在,如果不存在的话会创建它们。
[m] exchangeDeclare
[m] queueDeclare
[m] queueBind
上述三种方法都提供了若干不同形参数目的重载版本,这种设计在整个客户端API中会经常遇到。

4、发布消息
[m] basicPublish
[m] AMQP.BasicProperties#builder
说明:如果消息代理发生“资源不足告警”(内存使用过大或磁盘空间不足),那么Channel#basicPublish会阻塞。

5、信道和并发
根据经验,不应该在线程间共享Channel实例,应该一个线程一个Channel。

多个线程在同一Channel上并发发布消息时,可能产生不正确的帧交错,由此产生连接层协议异常并导致连接关闭。线程间共用一个Channel也会干扰生产者确认。所以应该避免多个线程在同一Channel上并发发布消息。但如果共享同一信道的两个线程,一个是生产者、一个是消费者,则是安全的。

消息服务器以push方式投递消息是并发进行的,并提供每个信道上消息接收顺序保证(和消息发送顺序相同)。消息服务器的并发投递是通过给每个连接配置一个ExecutorService实现的,也可以通过设置ConnectionFactory来自定义执行器,自定义的执行器会在从该连接工厂获取的连接间共享。

当消费者手动确认时,要注意是哪个线程进行确认。如果不是接收到消息的线程进行确认,则批量确认可能产生重复确认,由此产生信道层协议异常并导致信道关闭。一次只确认一个消息的则是安全的。

说明:RabbitMQ具有内存和磁盘告警功能,设计告警的目的是为了阻塞生产者、让消费者继续消费。然而,AMQP允许生成者和消费者在同一个信道上操作、也允许在同一连接的不同信道上操作,这种设计是不完美的。在实际中,对大部分应用而言不会造成大问题,因为告警调节只是表现为延迟。不过,其他设计也是允许的,建议对生产者和消费者使用不同的连接。

6、订阅消息(push API)
消费者订阅消息时,用消费者标签(tag)来区分不同的订阅,所以Consumer接口中的方法都必须传入消费者标签字符串。消费者标签可以由客户端产生,也可以由服务端提供。如果希望使用服务端产生的消费者标签(单个节点内具有唯一性),使用不带消费者标签参数的Channel#basicConsume方法或传入一个空字符串作为消费者标签参数,从Channel#basicConsume方法的返回值中就能获取到(服务端产生的)消费者标签。可以用该标签来取消消费者。

不同的消费者实例必须使用不同的消费者标签。如果在一个连接上使用重复的消费者标签,可能导致自动连接恢复有问题并且在监视消费者时不易区分。

子类化(用起来很便利的类)DefaultConsumer即可实现一个Consumer,将该匿名内部类实例传入Channel#basicConsume方法即可建立一个订阅。Consumer中的方法都是回调方法,即当某些事件发生时就会被自动调用,包括:handleDelivery、handleShowdonwSingle、handleConsumeOk、handleCacelOk、handleCancle等。可以通过Channel#basicCancel取消某个消费者。

在Consumer的回调方法中,可以执行Connection或Channel的阻塞方法,因为对Consumer的回调是在线程池中执行的,不是在实例化Channel的线程中执行的。每一个Channel都有自己的分发线程。一般情况下一个信道上一个消费者,这样的话就不会影响其他消费者。如果一个信道上有多个消费者,当其中某个消费者耗时较长的话,会影响对其他消费者上回调(事件处理任务)的分发。

7、获取单个消息(pull API)
Channel#basicGet,支持自动确认和手动确认。

8、处理不可路由的消息
如果发布消息时设置了 mandatory标识(flag),但是不能被路由,消息代理会将其退回给生产者。为了能够得到退回通知,生产者需要在信道上添加一个ReturnListener对象(Channel#addReturnListener),不然该消息就会被无声地丢掉。

9、关闭协议
在AMQP 0-9-1协议中,连接和信道采用相同的方式来处理网络失败、内部失败以及明确的请求关闭。它们都具有3种生命周期状态:open、closing、closed。无论是应用请求关闭、客户端库错误、网络请求关闭还是网络错误等,这些对象最终都会处于closed状态。

在AMQP连接和信道对象中,具有如下与关闭相关的方法:
[m] addShutdownListener/removeShutdownListener
[m] getCloseReason
[m] isOpen
[m] close

ShutdownSignalException对象提供了分析关闭原因的方法。该对象可以通过getCloseReason获取,或者通过ShutdownListener#shutdownCompleted的参数进行访问。

在生产环境代码中应该忽略isOpen方法,因为容易发生竞态条件(race conditions),用户代码无法实现原子性。

10、高级连接选项
在ConnectionFactory和Connection类中提供了一些方法用于自定义连接行为,满足高级定制需求。
* 定制消费者线程池
* 使用主机列表
* 支持服务发现
* 设置心跳检测超时
* Java NIO支持

11、网络异常时自动恢复
当RabbitMQ节点和客户端之间的网络连接发生异常时,客户端可以自动恢复连接和拓扑(队列、交换器、绑定和消费者)。自动恢复过程如下:
[1] 重连
[2] 恢复连接上的监听器
[3] 重新打开信道
[4] 恢复信道上的监听器
[5] 恢复信道的basic.qos设置,发布者确认和事务设置
拓扑恢复(对每个信道进行)过程如下:
[1] 重新声明交换器
[2] 重新声明队列
[3] 恢复所有绑定
[4] 恢复所有消费者

如果自动恢复失败(如RabbitMQ节点仍未正常工作),客户端就会每隔一段时间重试,该时间间隔固定,默认为5秒。如果创建连接时使用了主机列表,那么会将列表打乱并挨个重试。如果打开了自动连接恢复功能,可以在连接和信道上注册恢复监听器来处理恢复事件。

当连接断开时,使用Channel#basicPublish发布的消息会丢失。在连接恢复后,客户端并不会将它们重新入队进行再次发布。要想确保发布的消息到达RabbitMQ消息代理,需要使用发布者确认并对连接失败做相应处理。

当连接断开时,需要过一段时间才能侦测到。所以存在一个时间窗口,Java客户端和应用程序都没有意识到连接断开。在这个时间窗口中,发布的消息仍然会被序列化并写入TCP套接字。它们要想投递成只能靠生成者确认(属于协议扩展)来保证,因为在AMQP 0-9-1中将消息发布完全设计为异步方式。

当开启了自动恢复功能的连接检测到套接字或I/O操作错误,就会在一段时间后(默认5秒)开始恢复。之所以这样设计,是考虑到虽然很多网络失败是瞬态的且一般只存在很短的时间,但并不会马上消失。自动连接恢复会以固定的时间间隔进行重试直到建立新连接。
当连接处于正在恢复状态时,在该连接的信道上进行消息发布会抛出异常。此时客户端不会对这些消息进行任何缓存,应该由应用开发者来记录这些消息并在恢复成功后重新发布。如果生产者不能承受消息丢失的风险,就应该使用生产者确认来确保消息发布成功。
如果因为信道级异常造成信道关闭,不会触发自动连接恢复。这样的异常一般说明是应用级有问题,Java客户端库无法做出一个知情的决定。

当使用消费者(手动)确认时,在消息发送到消费者后、确认完成前,从消费者到RabbitMQ节点的网络连接可能会断开。在自动连接恢复后,RabbitMQ会重置所有信道上的投递标签,此时使用老的投递标签进行basic.ack、basic.nack和basic.reject会产生信道异常。为了避免异常发生,RabbitMQ会记录和更新投递标签确保在恢复前后它们是单调增加的。Channel#basicAck、Channel#basicNack和Chanel#basicReject会将调整后的投递标签转换为RabbitMQ使用的投递标签。使用老投递标签的确认不会被发送。使用消费者确认和自动恢复的消费者必须能够处理二次投递。

12、未处理的异常
关于连接、信道、恢复和消费者生命周期的未处理异常都交由异常处理器完成,使用ConnectionFactory#setExceptionHandler设置。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值