连接RabbitMQ,创建Connection:
ConnectionFactory factory = new ConnectionFactory();
//配置访问地址与端口
factory.setHost(IP_ADDRESS);
factory.setPort(PORT);
//配置账号密码
factory.setUsername("guest");
factory.setPassword("guest");
//配置VirtualHost(其实是分区)
factory.setVirtualHost("test");
return factory.newConnection();
信道 channel:
Channel channel = connection.createChannel();
Connection 可以用来创建多个Channel实例,但是Channel实例不能在线程之间共享,应用程序应该为每一个线程开辟一个channel 。某些情况下Chanel 的操作可以并发运行,但是在其他的情况下会导致在网络上,出现错误的通信。同时也会影响发送方确认机制的运行。所以,多线程之间共享 Channel 是非线程安全的。
交换机 Exchange:
exchangeDeclare有多个重载方法,这些重载的方法都是由下面这个方法中缺省的某些参数构成的。
Exchange.DeclareOk exchangeDeclare(String exchange,
BuiltinExchangeType type,
boolean durable,
boolean autoDelete,
boolean internal,
Map<String, Object> arguments) throws IOException;
这个方法的返回值是Exchange.DeclareOk,用来标识成功生成了一个交换机。
- exchange:交换机的名称
- BuiltinExchangeType:交换机进行分发的类型。
- DIRECT
- FANOUT
- TOPIC
- HEADERS
- durable:设备是否持久化。durable设置为true表示持久化,反之非持久化。持久化可以将交换机存盘,在服务器重启的时候不会丢失信息
- autoDelete:设备是否自动删除。autoDelete的值为true,表示自动删除。自动删除的前提是至少有一个队列或者交换器与这个交换器绑定,之后与这个交换器绑定的队列或者交换器都与此交换器解绑。注意:不能错误的把这个参数理解为:“当与此交换器连接的客户端都断开时,RabbitMQ会自动删除本交换机”
- internal:设置是否是内置交换机。如果为true,则表示是内置的交换机。客户端程序无法发送消息到此交换机中。只能通过交换机路由到队列中。
- arguments:其他一些结构化参数。
检测交换机是否存在:
Exchange.DeclareOk exchangeDeclarePassive(String name) throws IOException;
这个方法在实际开发过程中还是非常有用的,它用来检测相应的交换机是否存在。如果存在,则正常返回。如果不存在则抛出异常:404 channel Exception。同时channel也会关闭。
删除交换机
(1) Exchange.DeleteOk exchangeDelete(String exchange) throws IOException;
(2) void exchangeDeleteNoWait(String exchange, boolean ifUnused) throws IOException;
(3) Exchange.DeleteOk exchangeDelete(String exchange) throws IOException;
ifUnused :用来设置交换机没有使用的情况下删除交换机。如果ifUnused设置为true,则只有在此交换机没有被使用的情况下才会被删除;如果设置为false,则无论如何这个交换机都要被删除。
创建队列
相对于交换机的多个方法,创建队列只有两个方法。
(1) Queue.DeclareOk queueDeclare();
(2) Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments);
无参的方法默认创建了一个由RabbitMQ命名的、排他的、自动删除的、非持久化的队列。这种队列也被称为匿名队列。
- queue:队列名称
- durable:是否持久化。为true则设置队列持久化。持久化的队列会存盘,在服务器重启的时候可以保证不丢失相关信息。
- exclusive:设置是否排他。为true则设置队列为排他的。如果一个队列被声明为排他队列,则队列则仅对声明它的连接可见。并在连接断开时自动删除。这里需要注意三点
- 排他队列是基于连接可见的,同一个连接的不同信道是同时访问同一连接创建的排他队列;
- “首次”是指如果一个连接已经声明了一个排他队列,其他连接是不允许建立其他的同名排他队列的,这与普通队列不同。
- 即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除。这种队列适合一个客户端同时发送和读取消息的引用场景。
- autoDelete:设置是否自动删除。为true则设置队列为自动删除。自动删除的前提是:至少有一个消费者连接到这个队列,之后所有与这个队列连接的消费者都断开时,才会自动删除。不能把这个参数错误的理解为当连接到此队列的所有客户端断开时,这个队列自动删除。因为生产者客户端创建这个队列,或者没有消费者客户端与这个队列连接时,都不会自动删除这个队列。
- arguments:设置队列的其他一些参数
注意要点:生产者与消费者都能够使用queueDeclare 来声明一个队列,但是如果消费者在同一个信道上订阅了另外一个队列,就无法再声明队列了。必须先取消订阅,然后将信道设置为“传输”模式,之后才能声明队列
void queueDeclareNoWait(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
方法的返回值是void,表示不需要服务端的任何返回。同样也需要注意,在调用完queueDeclareNoWait方法之后,紧接着使用声明的队列时,有可能会发生异常。
检测队列:
Queue.DeclareOk queueDeclarePassive(String queue) throws IOException;
存在返回Queue。DeclareOK。不存在抛出异常。
删除队列:
1.Queue.DeleteOk queueDelete(String queue) throws IOException;
2.Queue.DeleteOk queueDelete(String queue, boolean ifUnused, boolean ifEmpty) throws IOException;
3.void queueDeleteNoWait(String queue, boolean ifUnused, boolean ifEmpty) throws IOException;
参数基本上与交换机一致
清空队列:
Queue.PurgeOk queuePurge(String queue) throws IOException;
该方法用来清空队列的内容,而不删除队列本身。
发送消息:
void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body)
throws IOException;
- exchange:交换机的名称,指明消息要发送到哪个交换机中。如果设置为空字符串,则消息会发送到RabbitMQ默认的交换器中。
- routingKey:路由键,交换机根据路由键将消息存储到相应的队列中。
- mandatory:
- true:如果exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息,那么broker会调用basic.return方法将消息返回给生产者;
- false:如果出现上述情况,broker会直接将消息丢弃,通俗的讲,mandatory标志告诉broker代理服务器至少将消息route到一个队列中,否则将消息return给生产者。
- immediate:已过期
- props:消息的基本属性集,其中包含14个成员
- content-type:让消费者知道如何解析消息体
- content-encoding:指消息体使用某种特殊的方式进行压缩和编码
- message-id 和correlation-id:表示唯一消息标识和唯一响应标识,用于在工作流中实现消息跟踪
- timestamp:消息创建时间
- expiration:消息过期时间
- delivery-mode:将消息写入磁盘或内存队列
- app-id 和user-id:帮助追踪出现问题的消息发布者应用程序
- type:标识发布者与消费者之间的契约
- reply-on:实现消息响应的路由
- headers:定义自定义格式的属性和实现RabbitMQ路由。
消费消息:
RabbitMQ的消费模式有两种:
- 推模式
- 拉模式
推模式采用Basic.Consume进行消费。拉模式采用Basic.Get方式进行消费
推模式:
在推模式中,可以通过持续订阅的方式来消费消息。使用到的类有:
com.rabbitmq.client.Consumer
com.rabbitmq.client.DefaultConsumer
接收消息一般通过实现Consumer接口或继承DefaultConsumer类来实现。当调用与Consumer相关的API的方法时,不同的订阅采用不同的消费者标签(ConsumeTag)来区分彼此,在同一个Channel中的消费者也需要通过唯一的消费者标签以作区分。
(1) String basicConsume(String queue, Consumer callback) throws IOException;
(2) String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;
(3) String basicConsume(String queue, boolean autoAck, Map<String, Object> arguments, Consumer callback) throws IOException;
(4) String basicConsume(String queue, boolean autoAck, String consumerTag, Consumer callback) throws IOException;
(5) String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map<String, Object> arguments, Consumer callback) throws IOException;
- queue:队列名称
- autoAck:设置是否自动确认。建议改成false,即不自动确认。
- consumer:消费者标签,用来区分多个消费者
- noLocal:设置为true则表示不能将同一个connection中生产者发送的消息传送给这个Connection中的消费者
- exclusive:设置是否排他
- arguments:消费者的其他属性
- callback:设置消费者的回调函数。用来处理RabbitMQ推送过来的消息,比如DefaultConsumer使用时,需要客户端重写(override)其中的方法。
对于消费者客户端来说重写handlerDelivery方法是十分方便的。更复杂的客户端会重写更多的方法。具体如下:
public void handleConsumeOk(String consumerTag)
public void handleCancelOk(String consumerTag)
public void handleCancel(String consumerTag) throws IOException
public void handleShutdownSignal(String consumerTag, ShutdownSignalException sig)
public void handleRecoverOk(String consumerTag)
和生产者一样,消费者客户端同样需要考虑线程安全问题。消费者客户端的这些callback会被分配到与Channel不同的线程池上,这以为这消费者客户端可以安全的的调用这些阻塞方法。
每个channel都有自己独立的线程。最常用的做法是一个channel对应一个消费者。也就意味着每个消费者之间没有任何关联。当然也可以在一个channel中维持多个消费者,但是要注意一个问题。如果channel中的一个消费者一直运行,那么其他消费者的callback会被“耽搁”
拉模式:
拉模式的消费方式:通过channel.basicGet方法可以单条的获取消息,其返回值是GetResponse。
Channel类的basicGet方法没有其他重载方法,只有:
GetResponse basicGet(String queue, boolean autoAck) throws IOException;