RabbitMQ客户端开发

连接RabbitMQ

        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost(IP_ADDRESS);
        factory.setPort(PORT);
        factory.setUsername("root");
        factory.setPassword("root");
        Connection connection=factory.newConnection();//创建连接
        ConnectionFactory factory=new ConnectionFactory();//uri方式
        factory.setUri("amqp://userName:password@ipAddress:portNumber/virtualHost");
        Connection conn=factory.newConnection();//创建连接
        Channel channel = conn.createChannel();

Connection可以用来创建多个Channel实例,但是Channel实例不能在线程间共享,应用程序应该为每一个线程开辟一个Channel。多线程间共享Channel实例是非线程安全的。可能会导致在网络上出现错误的通信帧交错

Channel或Connection中有个isOpen方法可以用来检测其是否已处于开启状态,但是不推荐在生产环境使用,有可能会产生竞争。

Channel的API方法都是可以重载的,比如exchangeDeclarequeueDeclare

声明交换器

Exchange.DeclareOk exchangeDeclare(String exchange
                                    ,String type
                                    ,boolean durable
                                    ,boolean autoDelete
                                    ,boolean internal
                                    ,Map arguments
                                   ) throws IOException;
  • exchange:交换器名称。
  • type:交换器的类型。
  • durable:是否持久化,true持久化,false不持久化。
  • autoDelete:是否自动删除,true自动删除(前提是至少有一个队列或交换机与这个交换器绑定,之后所有与之绑定的交换器或者队列都与它解绑),false不自动删除。
  • internal:是否是内置的,内置的交换器,客户端程序无法直接发送消息到这个交换器中,只能通过别的交换器来路由到本交换器。
  • arguments:其他一些结构化参数。如alternate-exchange备份交换机。

删除交换器

Exchange.DeleteOk exchangeDelete(String exchange, boolean ifUnused) throws IOException;
void exchangeDeleteNoWait(String exchange, boolean ifUnused) throws IOException;
Exchange.DeleteOk exchangeDelete(String exchange) throws IOException;

ifUnused用来设置是否在交换器没有被使用的情况下删除。true表示只有在此交换器没有被使用的情况下才会被删除,false表示无论如何这个交换器都要被删除。

声明队列

Queue.DeclareOk queueDeclare() throws IOException

不带任何参数的queueDeclare方法默认创建一个由RabbitMQ命名的、排他的、自动删除的、非持久化的队列(匿名队列)。

Queue.DeclareOk queueDeclare(String queue
                            ,boolean durable
                            ,boolean exclusive
                            ,boolean autoDelete
                            ,Map arguments
                            ) throws IOException;
  • queue:队列的名称。
  • durable:是否持久化。true持久化,false不持久化。
  • exclusive:是否排他。true排他,false不排他。排他是指该队列对首次声明它的连接可见,并在连接断开时自动删除。排他队列是基于连接可见的,同一个连接的不同信道是可以同时访问同一连接创建的排他队列。”首次“是指如果一个连接已经声明了一个排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同;即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会自动被删除,这种队列适用于一个客户端同事发送和读取消息的应用场景。
  • autoDelete:是否自动删除,true自动删除(前提是至少有一个消费者连接到这个队列,之后所有与这个队列连接的消费者都断开时,才会自动删除),false不自动删除。
  • arguments:设置队列的其他一些参数,如x-message-ttl(设置队列的生存时间)、x-expires(设置队列的到期时间,指定时间内没有被访问就会被删除)、x-max-length(限定队列的消息的最大值长度,超过指定长度将会把最早的几条删除掉)、x-max-length-bytes(限定队列最大占用的空间大小)、x-dead-letter-exchange(当队列消息长度大于最大长度、或者过期的等,将从队列中删除的消息推送到指定的死信交换机中去而不是丢弃掉)、x-dead-letter-routing-key(将死信推送到指定交换机的指定路由键的队列中去)、x-max-priority(优先级队列,声明队列时先定义最大优先级值,在发布消息的时候指定该消息的优先级, 优先级更高(数值更大的)的消息先被消费)、x-queue-mode=lazy(先将消息保存到磁盘上,不放在内存中,当消费者开始消费的时候才加载到内存中)等。

生产者和消费者都能够使用queueDeclare来声明一个队列,但是如果消费者在同一个信道上订阅了另一个队列,就无法再声明队列了。必须先取消订阅,然后将信道置为“传输”模式,之后才能声明队列。

删除队列

Queue.DeleteOk queueDelete(String queue) throws IOException;
Queue.DeleteOk queueDelete(String queue, boolean ifUnused, boolean ifEmpty) throws IOException;
void queueDeleteNoWait(String queue, boolean ifUnused, boolean ifEmpty) throws IOException;
//queue表示队列名,ifUnused表示该队列是否处于使用中才能删除,ifEmpty表示该队列是否要为空(没有消息堆积)才能删除

清空队列

Queue.PurgeOk queuePurge(String queue) throws IOException;
//该方法用于清空队列中的消息,但不删除队列

队列和交换机绑定

Queue.BindOk queueBind(String queue
                        , String exchange
                        , String routingKey
) throws IOException;

Queue.BindOk queueBind(String queue
                        , String exchange
                        , String routingKey
                        , Map arguments
) throws IOException;

void queueBindNoWait(String queue
                        , String exchange
                        , String routingKey
                        , Map arguments
) throws IOException;
//queue表示队列名,exchange表示交换器名,routingKey表示路由键,arguments表示关于绑定的一些参数

队列和交换机解绑

Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey) throws IOException;
Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey
, Map arguments) throws IOException;

交换器和交换器绑定(调用该方法进行绑定后,消息会从source转发器发送到destination转发器)

Exchange.BindOk exchangeBind(String destination, String source, String routingKey) throws IOException;
Exchange.BindOk exchangeBind(String destination, String source, String routingKey
, Map arguments) throws IOException;
void exchangeBindNoWait(String destination, String source, String routingKey
, Map arguments) throws IOException;
//destination表示目标转发器,source表示原转发器,routingKey表示路由键,arguments表示绑定的一些参数

交换器的使用并不真正耗费服务器的性能,而队列会。如果要衡量RabbitMQ的QPS(每秒查询率)只需要看队列的即可。

发送消息

void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) 
throws IOException;
void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props
, byte[] body) throws IOException;
void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate
, BasicProperties props, byte[] body) throws IOException;
  • exchange:交换器的名称。
  • routingKey:路由键。
  • props:消息的基本属性。其包含14个属性成员,详细可以看com.rabbitmq.client.AMQP.BasicProperties源码,里面分别有contentType、contentEncoding、Map headers、Integer deliveryMode、Integer priority、correlationId、replyTo、expiration、messageId、Date timestamp、type、userId、appId、clusterId这14个属性成员。
  • byte[ ] body:消息体payload,真正需要发送的消息。
  • mandatory:消息如果没有找到匹配的队列则返回消息或者丢弃消息。true返回生产者消息,false丢弃消息。
  • immediate:true时如果队列上并不存在任何消费者,则这条消息不入队,直接返回生产者。3.0版本后不支持,因为会影响镜像队列的性能。

消费消息

RabbitMQ的消费模式分为两种:推模式拉模式。推模式采用Basic.Consume进行消费,而拉模式则是调用Basic.Get进行消费。

推模式中,可以通过持续订阅的方式来消费消息,接收消息一般通过实现Consumer接口或者继承DefaultConsumer类来实现,当调用与Consumer相关的API方法时,不同的订阅采用不同的消费者标签consumerTag来区分彼此,在同一个Channel中的消费者也需要通过唯一的消费者标签以作区分。

String basicConsume(String queue, Consumer callback) throws IOException;

String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal
, boolean exclusive, Map arguments, Consumer callback) throws IOException;
 
String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal
, boolean exclusive, Map arguments, DeliverCallback deliverCallback, 

CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;

queue:队列的名称。

autoAck:设置是否自动确认,建议设为false。

consumerTag:消费者标签,用来区分多个消费者。

noLocal:设置为true则表示不能将同一个Connection中生产者发送的消息传送给这个Connection中的消费者。

exclusive:是否排他。

arguments:设置消费者的其他参数。

callback:设置消费者的回调函数。用来处理RabbitMQ推送过来的消息,比如重写DefaultConsumer的handleDelivery方法。

还可以重写更多的方法,如:handleConsumeOk(会在其他方法之前调用,返回消费者标签)、handleCancelOk、handleCancel、handleShutdownSignal(当Channel或者Connection关闭的时候会调用)、handleRecoverOk。

拉模式

通过channel.basicGet方法可以单条地获取消息,其返回值是GetRespone。Channel类的basicGet方法没有重载。

GetResponse basicGet(String queue, boolean autoAck) throws IOException;

Basic.Consume将信道置为投递模式,直到取消队列的订阅为止,在投递模式期间,RabbitMQ会不断的推送消息给消费者,当然推送消息的个数还是会受到Basic.Qos的限制。如果只是想要获取单条消息而不是持续订阅,建议还是使用Basic.Get进行消费,但是不能通过在循环里调用basicGet来代替basicConsume,这会严重影响RabbitMQ的性能,一般还是使用Basic.Consume,具有较高的吞吐量。

消费端的确认与拒绝

消费者在订阅队列时,可以指定autoAck参数,当autoAck等于false时,RabbitMQ会等待消费者显式地回复确认信号后才从内存(或者磁盘)中移去消息(实质上是先打上删除标记,之后再删除)。当autoAck等于true时,RabbitMQ会自动把发送出去的消息置为确认,然后从内存(或磁盘)中删除,而不管消费者是否真正地消费到了这些消息。

当autoAck=false时,对于RabbitMQ服务器端而言,队列中的消息分成了两部分:一部分是等待投递给消费者的消息;一部分是已经投递给消费者,但是还没有收到消费者ack信号的消息。如果服务器端一直没有收到消费者的ack信号,并且消费此消息的消费者已经断开连接,则服务器端会安排该消息重新进入队列,等待投递给下一个消费者(也可能还是原来的那个消费者)。
RabbitMQ不会为未确认的消息设置超时时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者连接是否已经断开,这么设计的原因是RabbitMQ允许消费者消费一条消息的时间可以很久很久。

在消费者接受到消息后,如果想明确拒绝当前的消息而不是确认,可以调用channel.basicReject方法(Basic.Reject)来告诉RabbitMQ拒绝这个消息。

void basicReject(long deliveryTag, boolean requeue) throws IOException;
//deliveryTag可以看做消息的编号 如果requeue参数为true,则RabbitMQ会将这条消息存入队列,以便可以发送给下一个订阅的消费者
//如果requeue参数为false,则RabbitMQ立即会把消息从队列中溢出,而不会把它发送给新的消费者。

Basic.Reject命令一次只能拒绝一条消息,如果想要批量拒绝消息,则可以使用Basic.Nack这个命令,对应的方法是channel.basicNack。

void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException;
//multiple为false则表示拒绝编号为deliveryTag的这一条消息
//multiple为true则表示拒绝编号deliveryTag之前所有未被当前消费者确认的消息

将channel.basicReject或者channel.basicNack中的requeue设置为false,可以启用死信队列的功能。死信队列可以通过检测被拒绝或者为送达的消息来追踪问题。

AMQP中还有一个命令Basic.Recover具备可重入队列的特性。

Basic.RecoverOk basicRecover(boolean requeue) throws IOException;
//该方法用于请求RabbitMQ重新发送还未被确认的消息。
//如果为true,则未被确认的消息会被重新加入到队列中,对于同一条消息,可能会被分配给与之前不同的消费者。
//如果为false,则同一条消息会被分配给与之前相同的消费者。

关闭连接

Connection和Channel所具备的生命周期如下:

  • Open:开启状态。
  • Closing:正在关闭状态。
  • Closed:已经关闭状态。

Connection和Channel最终都会称为Closed的状态,无论是程序正常调用的关闭方法,或者是客户端的异常,再或者是发生了网络异常。

getCloseReason方法可以让你知道对象关闭的原因。

你可能感兴趣的:(RabbitMQ)