创建链接
- 声明连接工厂,设置host、port、username、password、VirtualHost等信息,VirtualHost默认为"/",可以不设置
ConnectionFactory factory = new ConnectionFactory();
// 自己的服务器配置
factory.setHost("192.168.0.1");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
Connection conn = factory.newConnection();
Channel channel = conn.createChannel();
定义交换器
- 这里声明了一个名字为user.exchange的、direct类型的、持久化的、非自动删除的队列,代码如下:
// 成员变量声明、之后代码也会调用此处成员变量
private static final String EXCHANGE_NAME = "user.exchange";
private static final String USER_QUEUE = "user.queue";
private static final String ROUTING_KEY = "direct_routingkey";
channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);
- 参数详解
exchange:交换器名称
type:交换器类型
durable:是否持久化,默认否、如需请声明为true
autoDelete:是否自动删除交换器,删除条件为:autoDelete设置为true,至少有一个队列或交换器与这个交换器绑定(被使用过)、之后所有与之绑定的队列或交换器都与之解绑。并不是当与之连接的客户端都断开时,RabbitMQ自动删除本交换器。默认为否
internal:设置是否是内置交换器。如果为true,表示是内置交换器,客户端程序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式。默认非内置
arguments:其他一些结构化参数,比如 alternate-exchange,暂且不关注这个字段,传null就好
- exchangeDeclare的重载方法。一般地,使用exchangeDeclare(String exchange, String type, boolean durable)就够了,其余参数视情况而定
Exchange.DeclareOk exchangeDeclare(String exchange, String type) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange,
String type,
boolean durable,
boolean autoDelete,
boolean internal,
Map<String, Object> arguments) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange,
BuiltinExchangeType type,
boolean durable,
boolean autoDelete,
boolean internal,
Map<String, Object> arguments) throws IOException;
- exchangeDeclareNoWait方法
exchangeDeclare与exchangeDeclareNoWait很相似,exchangeDeclareNoWait多了一个noWait参数,意思是:在服务器声明了一个exchange之后,不需要返回Declare-OK指令、exchange就可以使用,效率稍许提升,可是带来一个问题:当刚刚声明一个exchange之后(还未确认OK)、立即使用exchange就会抛出异常,因此这个方法一般不考虑。 - exchangeDeclarePassive方法
这个方法用来检测相应的交换器是否存在。如果存在就正常返回,如果不存在就抛出异常,同时channel也会关闭。 - exchangeDelete方法
删除交换器,ifUnused:true,代表如果此交换器被使用中、则删除失败。有三种实现形式,如下:
Exchange.DeleteOk exchangeDelete(String exchange, boolean ifUnused) throws IOException;
void exchangeDeleteNoWait(String exchange, boolean ifUnused) throws IOException;
Exchange.DeleteOk exchangeDelete(String exchange) throws IOException;
定义队列
- 这里定义了一个名字为user.queue、持久化存储的、非排他的、非自动删除的队列
channel.queueDeclare(USER_QUEUE, true, false, false, null);
- queueDeclare的重载方法不多、连带noWait方法/Passive方法一起、如下:
Queue.DeclareOk queueDeclare() throws IOException;
Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
void queueDeclareNoWait(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
其中无参方法实现为:
@Override
public com.rabbitmq.client.AMQP.Queue.DeclareOk queueDeclare()
throws IOException
{
return queueDeclare("", false, true, true, null);
}
- 参数详解
queue:队列名称
durable:是否持久化、默认false
exclusive:是否排他、默认true。如果一个队列被声明为排他队列,该队列仅对首次声明它的链接可见,并在该connection断开连接时删除。这里需要注意三点:排他队列是基于连接( Connection) 可见的,同一个连接的不同信道 (Channel)是可以同时访问同一连接创建的排他队列; "首次"是指如果一个连接己经声明了 一个排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同:即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除,这种队列适用于一个客户端同时发送和读取消息的应用场景
autoDelete:是否自动删除、默认true
arguments:设置队列的其他一些参数,如 x-rnessage-ttl 、 x-expires 、x -rnax-length 、 x-rnax-length-bytes 、 x-dead-letter-exchange 、 x-deadletter-routing-key, x-rnax-priority 等
- 删除队列
Queue.PurgeOk queuePurge(String queue) throws IOException;
队列交换器绑定
- 如下,将队列USER_QUEUE和交换器EXCHANGE_NAME绑定,路由键为ROUTING_KEY
channel.queueBind(USER_QUEUE, EXCHANGE_NAME, ROUTING_KEY);
- queueBind有两个重载方法
Queue.BindOk queueBind(String queue, String exchange, String routingKey) throws IOException;
Queue.BindOk queueBind(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException;
- 属性详解
queue:队列名
exchange:交换器名
arguments:定义绑定的一些参数 - queueBind还有一个noWait方法。还有解绑的两个重载方法,如下:
void queueBindNoWait(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException;
Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey) throws IOException;
Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException;
交换器与交换器绑定
我们不仅可以将交换器与队列绑定,也可以将交换器与交换器绑定,用法极为类似,接口如下:
Exchange.BindOk exchangeBind(String destination, String source, String routingKey) throws IOException;
Exchange.BindOk exchangeBind(String destination, String source, String routingKey, Map<String, Object> arguments) throws IOException;
void exchangeBindNoWait(String destination, String source, String routingKey, Map<String, Object> arguments) throws IOException;
Exchange.UnbindOk exchangeUnbind(String destination, String source, String routingKey) throws IOException;
Exchange.UnbindOk exchangeUnbind(String destination, String source, String routingKey, Map<String, Object> arguments) throws IOException;
void exchangeUnbindNoWait(String destination, String source, String routingKey, Map<String, Object> arguments) throws IOException;
- 交换器绑定交换器的意义在于:多个生产者需要输出到同一个队列,供同一类型的消费者消费,多个生产者就可以通过绑定交换器、最终将消息输出到同一交换器的队列。
发送消息
- 发送消息时,指定交换器名、路由键、待序列化的数据即可,当然还有很多附加字段
// 发送对象、注意:对象必须实现Serialization接口
User user = new User();
user.setId(2);
user.setUsername("陈独秀");
user.setAge(25);
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, null, SerializationUtils.serialize(user));
// 发送字符串
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, null, "Hello 世界".getBytes());
- basicPublish共有三个重载方法
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:路由键
mandatory:当设置为true时,交换器无法根据自身的类型和路由键找到一个符合条件的队列,那么RabbitMQ会调用Basic.Return命令将消息返回给生产者。
immediate:当设置为true时,如果交换器在将消息路由到队列时发现队列上并不存在任何消费者,那么这条消息将不会存储队列中。当与路由键匹配的所有队列都没有消费者时,该消息会通过Basic.Return返回给生产者。
概括来说 , mandatory 参数告诉服务器至少将该消息路由到一个队列 中, 否则将消息返回给生产者。 immediate参数告诉服务器 , 如果该消息关联的队列上有消费者, 则 立刻投递:如果所有匹配的队列上都没有消费者,则直接将消息返还给生产者 , 不用将消息存入队列而等待消费者了。
但是RabbitMQ3.0版本开始去掉了对immediate的支持,对此RabbitMQ官网解释是:immediate参数会影响镜像队列的性能,增加了代码的复杂性,建议采用TTL和DLX的方法替代。
props:消息的基本属性集。其包含 14 个属性成员,分别有 contentType 、content Encoding 、 headers (Map<String , Object>) 、 deliveryMode 、 priority 、correlationld 、 replyTo 、 expiration 、 messageld、 timestamp 、 type 、 userld 、appld 、 clusterld
body:消息体,payload,待发送的消息主体
消费消息
RabbitMQ消费消息模式分为两种:推模式(PUSH、Basic.Consume)、拉模式(PULL、Basic.Get)。
- 推模式
Channel finalChannel = channel;
channel.basicConsume(QUEUE_NAME, false, "myConsumerTag",
new DefaultConsumer(finalChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(SerializationUtils.deserialize(body));
finalChannel.basicAck(envelope.getDeliveryTag(), false);
}
});
TimeUnit.SECONDS.sleep(5);
上边代码显式的将第二个参数(autoack)设置为false,表示不自动确认,然后再接受完消息体之后,通过finalChannel.basicAck(envelope.getDeliveryTag(), false);来主动确认消息。这样在主动确认消息之前、RabbitMQ不会删除消息,可以防止消息不必要的丢失。
- basicConsumer的重载方法很多,如下:
- 主要属性详解
queue:队列的名字
autoACK:设置为true表示自动确认,消息发出即删除
consumerTag:消费者标签,用来区分多个消费者
noLocal:设置为true表示不能将同一个Connection中生产者发送的消息传送至这个Connection的消费者
exclusive:是否排他
arguments:其他参数
callback:回调函数,上边示例代码的回调函数为:new DefaultConsumer(finalChannel)
String basicConsume(String queue, Consumer callback) throws IOException;
String basicConsume(String queue, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException;
String basicConsume(String queue, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
String basicConsume(String queue, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;
String basicConsume(String queue, boolean autoAck, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, Map<String, Object> arguments, Consumer callback) throws IOException;
String basicConsume(String queue, boolean autoAck, Map<String, Object> arguments, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, Map<String, Object> arguments, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, Map<String, Object> arguments, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, Consumer callback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map<String, Object> arguments, Consumer callback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map<String, Object> arguments, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map<String, Object> arguments, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map<String, Object> arguments, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException;
对于消费者而言,重写handleDelivery()方法可以很方便的消费消息,还有几个可选重写的方法:
void handleConsumerOk(String consumerTag);
void handleCancelOk(String consumerTag);
void handleCancel(String consumerTag) throws IOExceotion;
void handleShutdownSingal(String consumerTag, ShutdownSignalException sig);
void handleRecoverOk(String consumerTag);
- 拉模式
GetResponse response = channel.basicGet(QUEUE_NAME, false);
System.out.println(SerializationUtils.deserialize(response.getBody()));
channel.basicAck(response.getEnvelope().getDeliveryTag(), false);
关闭链接
- 先关闭channel、然后关闭connection。关闭逻辑应在finally中定义
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (IOException e) {
e.printStackTrace();
}
}