RabbitMQ接口分为 通道、交换器、路由、和队列4个模块
生产者/消费者连接消息队列,创建通道,创建交换器,创建队列。把队列绑定到交换器上。
生产者发送消息到交换器,交换器根据路由把消息转到不同队列上。
消费者创建通道后,指定交换器,路由,和队列。
交换器:
DIRECT("direct"), FANOUT("fanout"), TOPIC("topic"), HEADERS("headers");
测试代码
package com.cn.rabbitmq.cp1.exchange;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.impl.ErrorOnWriteListener;
import java.io.IOException;
public class BaseQueue {
public static Connection getConnection() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setUsername("guest");
factory.setPassword("guest");
factory.setErrorOnWriteListener(new ErrorOnWriteListener() {
public void handle(Connection connection, IOException exception) throws IOException {
}
});
return factory.newConnection();
}
}
服务端发送消息:
package com.cn.rabbitmq.cp1.exchange.topic;
import com.cn.rabbitmq.cp1.exchange.BaseQueue;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* 队列参数说明:
* 队列名称 queue :队列名
* 是否持久队列 durable :true 持久队列,该队列将在服务器重新启动后继续存在。当然了,队列的消息内容也会被写入磁盘
* 是否独占队列 (单消费者队列)exclusive : 如果需要消费者独占队列,在队列创建的时候,设定属性exclusive为true。 只能有一个消费者
* 是否自动删除 autoDelete :自动删除队列,客户端连接时自动删除。 服务端会连接会不会删除?
*
*
* 其他参数 arguments
* 参数名 目的
* x-dead-letter-exchange 死信交换器
* x-dead-letter-routing-key 死信消息的可选路由键
* x-expires 队列在指定毫秒数后被删除
* x-ha-policy 创建HA队列
* x-ha-nodes HA队列的分布节点
* x-max-length 队列的最大消息数
* x-message-ttl 毫秒为单位的消息过期时间,队列级别
* x-max-priority 最大优先值为255的队列优先排序功能
*
*/
public class TopicProduct extends BaseQueue {
public static final String TOPIC_EXCHANGE="topic_exchange"; //交换器名称
public static final String BAK_EXCHANGE_NAME ="bak_exchange"; //备用交换器名称
public static final String TOPIC_TOPIC = "topic_queue"; //队列名称
public static void main(String[] args) throws Exception {
Connection connection = getConnection(); //获取连接
Channel channel =connection.createChannel(); //创建通道
Map exchangeArguments = new HashMap();
exchangeArguments.put("alternate-exchange",BAK_EXCHANGE_NAME); //设置备用交换器 ,备用开启后,发送失败和无路由会走备用交换器
channel.exchangeDeclare(TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC,false,false,exchangeArguments); //创建交换器, 名称,类型
//备用交换器
channel.exchangeDeclare(BAK_EXCHANGE_NAME,BuiltinExchangeType.FANOUT, true,false,null);
Map arguments = new HashMap();
arguments.put("x-expires",45*1000); //队列在超过一定时间没有被使用,会被从RabbitMQ中删除 这里要注意是否有权限
arguments.put("x-message-ttl",20*1000);//设定了该队列所有消息的存活时间,时间单位毫秒
//创建队列 当然了,也可以再消费者方创建队列
channel.queueDeclare(TOPIC_TOPIC,false,false,false,arguments); //队列名称,是否持久队列,是否独占队列,是否自动删除,其他参数
//队列绑定交换器
channel.queueBind(TOPIC_TOPIC,TOPIC_EXCHANGE,null,null);//队列名称,交换器名称,路由key,arguments
channel.confirmSelect();//启动生产者确认
// 3种确认方式 channel.waitForConfirms();普通发送方确认模式 channel.waitForConfirmsOrDie();批量确认模式 channel.addConfirmListener异步监听发送方确认模式
channel.addConfirmListener(new ConfirmListener() { //发送结果监听
public void handleAck(long deliveryTag, boolean multiple) throws IOException { //成功
System.out.println("消息发送成功");
}
public void handleNack(long deliveryTag, boolean multiple) throws IOException { //失败
System.out.println("消息发送失败");
}
});
for (int i=0;i<10;i++) {
String[] keys = new String[]{"topic.test","topic.mymsg","topic.mytest"};
Random random = new Random();
String routingKey =keys[random.nextInt(3)];
String message="这是个测试数据"+i+"使用的路由是"+routingKey;
//BasicProperties props消息属性
//发送消息到交换器,路由器
channel.basicPublish(TOPIC_EXCHANGE,routingKey,null,message.getBytes("utf-8")); //交换器名称,路由键,消息属性,消息内容
System.out.println("发送了第"+i+"条消息。消息内容为:"+message);
}
//不可路由到队列的消息监听
channel.addReturnListener(new ReturnListener() {
/**
* int replyCode, 响应吗, 路由成没成功
* String replyText, 回复内容
* String exchange,
* String routingKey,
* AMQP.BasicProperties properties,
* byte[] body 实际的消息体内容
* @param replyCode
* @param replyText
* @param exchange
* @param routingKey
* @param properties
* @param body
* @throws IOException 跑出ioexception 异常
*/
public void handleReturn(int replyCode, String replyText,
String exchange, String routingKey,
AMQP.BasicProperties properties,
byte[] body) {
String message = new String(body);
System.out.println("RabbitMq返回的replyCode: "+replyCode+"RabbitMq返回的replyText: "+replyText);
System.out.println("RabbitMq返回的exchange: "+exchange+"RabbitMq返回的routingKey: "+routingKey);
System.out.println("RabbitMq返回的message: "+message);
}
});
//信道关闭监听
channel.addShutdownListener(new ShutdownListener() {
public void shutdownCompleted(ShutdownSignalException cause) {
System.out.println("关闭了监听连接");
}
});
//连接关闭监听 不知道为什么创建连接的时候没有连接创建监听?
connection.addShutdownListener(new ShutdownListener() { //连接关闭加你听
public void shutdownCompleted(ShutdownSignalException cause) {
}
});
/**
* 这个监听器在服务器内存报警或者硬盘报警的情况下,使用发送接口会产生阻塞,如果发送和接受等使用同一个连接,则会都阻塞,因此我们应该为消费者和生产者使用不同的CachingConnectionFactory,或者设置rabbitTemplate.setUsePublisherConnection(true);
*/
connection.addBlockedListener(new BlockedListener() { //连接阻塞情况的监听器
public void handleBlocked(String reason) throws IOException {
System.out.println("connection blocked, reason: "+reason);
}
public void handleUnblocked() throws IOException { //需要同时解除内存和磁盘的报警才会收到unblock的消息
System.out.println("==============================connection unblocked");
}
});
channel.close();
connection.close();
}
}
创建两个消费端,都是从通道获取的临时队列,并把队列指定路由绑定到交换器上
public class TopicConsumer1 extends BaseQueue {
public static void main(String[] args) throws Exception{
Connection connection = getConnection();
Channel channel =connection.createChannel();
String queueName =channel.queueDeclare().getQueue();//从通道获取队列
channel.queueBind(queueName,TopicProduct.TOPIC_EXCHANGE,"topic.mymsg"); //指定路由绑定到交换器
channel.basicConsume(queueName,true,new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String str = new String(body,"utf-8");
System.out.println(str);
}
});
}
}
public class TopicConsumer2 extends BaseQueue {
public static void main(String[] args) throws Exception{
Connection connection = getConnection();
Channel channel =connection.createChannel();
String queueName =channel.queueDeclare().getQueue();//从通道获取队列
channel.queueBind(queueName,TopicProduct.TOPIC_EXCHANGE,"topic.mymsg"); //指定路由绑定到交换器
channel.basicConsume(queueName,true,new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String str = new String(body,"utf-8");
System.out.println(str);
}
});
}
}
每个客户端都会消费一个临时队列里的消息
springboot 下客户端实现临时队列绑定到交换器路由
@Component(value = "testListener")
public class TestListener{
public final static String ROUTING_KEY = "test.queue";
public final static String TEST_EXCHANGE = "topic_exchange;
//指定监听,绑定队列队列的路由和校验器
@RabbitListener(bindings ={@QueueBinding(value = @Queue,
key = ROUTING_KEY,exchange = @Exchange(value=TEST_EXCHANGE,type = ExchangeTypes.TOPIC))})
@RabbitHandler
public void process(@Payload String message, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel) throws IOException {
System.out.println(message);
channel.basicAck(deliveryTag,false);
}
}