生产者
package com.ly.liyong.rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class PublisherController {
private static final String EXCHANGE_NAME = "exchange_ly_demo";
private static final String ROUTING_KEY = "routing_ly_demo";
private static final String QUEUE_NAME = "queue_ly_demo";
private static final String IP_ADDRESS = "47.105.121.99";
private static final int PORT = 5672;
public static void main(String[] args) throws IOException,
TimeoutException, InterruptedException {
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(IP_ADDRESS);
factory.setPort(PORT);
factory.setUsername("lpadmin");
factory.setPassword("lpadmin");
//创建连接
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//direct模式的持久化、非自动删除的交换器
channel.exchangeDeclare(EXCHANGE_NAME, "direct", true, false, null);
//持久化、非排他的、非自动删除的队列
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//将交换器与队列通过路由键绑定
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);
for (int i = 1; i < 5; i++) {
//发送一条持久的消息
String msg = "大帅哥,你好!" + i;
byte[] msgByte = msg.getBytes();
System.out.println("send: " + msg);
// channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, msgByte);
//设置消息相关属性
//delivery_mode设置为2,即消息会被持久化(即存入磁盘)在服务器中
//priority设置这条消息的优先级为0
// channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY,
// new AMQP.BasicProperties.Builder()
// .contentType("text/plain")
// .deliveryMode(2)
// .priority(1)
// .build(), msgByte);
//发送带有headers的消息,并设置消息过期时间为10s
Map headers = new HashMap();
headers.put("localtion", "here");
headers.put("time", "tody");
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY,
new AMQP.BasicProperties.Builder()
.headers(headers)
.expiration("10000")
.build(), msgByte);
}
//关闭资源
channel.close();
connection.close();
}
}
basicPublish()中特殊参数讲解:
mandatory:参数为true时,交换器无法找到对应的队列时,RabbitMq会调用Basic.Return命令将消息返回给生产者(客户端可通过ReturnListener监听这个事件)。为false时,消息直接被丢弃。(当备份交换器一起使用的时候,此参数设置失效)
immediate:参数为true时,如果交换器在将消息路由到队列上并不存在任何消费者,那么这条消息将不会存入队列中。当路由键匹配的所有队列都么有消费者时,该消息会通过Basic.Return返回至生产者(RabbitMq3.0版本开始去掉了对immediate参数的支持,原因是此参数会影响镜像队列的性能,增加了代码复杂性,建议采用TTL和DLX的方法替代)
TTL:
1.队列过期时间和消息过期时间
2.以上两个同时使用时,则消息的过期时间以两者之间较小的那个数值为准
3.如果将消息的过期时间设置为0,则表示除非此时可以直接将消息投递到消费者,否则改消息会被立即丢弃(此属性可以代替immediate参数)
4.队列的过期删除不能保证很及时。在RabbitMq重启后,持久化的队列的过期时间会被重新计算切TTL不能设置为0
消费者(推模式)
package com.ly.liyong.rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class ConsumerController {
private static final String QUEUE_NAME="queue_ly_demo";
private static final String IP_ADDRESS="47.105.121.99";
private static final int PORT=5672;
public static void main(String[] args) throws IOException,
TimeoutException, InterruptedException {
Address[] addresses = new Address[]{
new Address(IP_ADDRESS, PORT)
};
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("lpadmin");
factory.setPassword("lpadmin");
//创建连接
Connection connection = factory.newConnection(addresses);
//创建信道
final Channel channel = connection.createChannel(50);
//设置客户端最多接收未被ack的消息的个数
channel.basicQos(64);
//推模式
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties,
byte[] body)
throws IOException {
System.out.println("推模式: " + new String(body));
//1s后消费
try{
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//手动ack确认消费
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
//消费模式
channel.basicConsume(QUEUE_NAME, consumer);
//拉模式(每次只拉取一条消息)
// GetResponse response = channel.basicGet(QUEUE_NAME, false);
// System.out.println("拉模式: " + new String(response.getBody()));
// channel.basicAck(response.getEnvelope().getDeliveryTag(), false);
//请求Broker重新发送未被确认的消息(1次)
// channel.basicRecover();
//等待回调函数执行完毕之后,关闭资源
// TimeUnit.SECONDS.sleep(5);
// channel.close();
// connection.close();
}
}
basicConsume()
queue:队列名
autoAck:设置是否自动确认。建议设置false,即不自动确认
consumerTag:消费者标签,用来区分多个消费者
noLocal:设置为true则表示不能将同一个Connection中生产者发送的消息传递给这个Connection中的消费者
exclusive:是否排他
arguments:设置消费者的其他参数
callback:设置消费者的回调函数。用来处理RabbitMq推送过来的消息,比如DefaultConsumer,使用时需要客户端重写(override)其中的方法。
注意:
1.和生产者一样,消费者客户端同样需要考虑线程安全的问题。消费者客户端的这些callback会被分配到Channel不同的线程池上,这意味着消费者客户端可以安全第调用这些阻塞方法,比如:channel.queueDeclare()、channel.basicCancel()等
2.每个Channel都拥有自己独立的线程。最常用的做法是一个Channel对应一个消费者,也就是意味着消费者彼此之间没有任何关联。当然也可以在一个Channel钟维持多个消费者,但是要注意一个问题,如果Channel中一个消费者一直运行,那么其他消费者的callback会被“耽搁”。
3.在投递(推)模式期间,RabbitMq会不断地推送消息给消费者,当然推送消息的个数还是会收到Basic.Qos的限制。
4.如果只想从队列获得单条消息而不是持续订阅,建议还是使用Basic.Get进行消费。