RabbitMQ --基础练习测试
先安装erlong,然后安装RabbitMQ *一定记得版本很容易冲突
安装完这两个以后,浏览器打开访问 localhost:15672 账户密码 都是guest
如果失败,就把 C:\Users\Administrator\AppData\Roaming\RabbitMQ\db 目录下的那两个文件给删除了,然后再重新安装RabbitMQ
添加用户

添加数据库

给用户授权访问数据库

删除用户
点进去用户名,然后下拉到最下面,delete 就好了
使用java 玩转RabbitMQ
导入依赖:
<dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>4.0.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
简单队列
创建一个工具类 ConnectionUtils ,获取MQ连接
public class ConnectionUtils { // 获取mq 连接 public static Connection getConnection() throws IOException, TimeoutException { // 定义一个连接工厂 ConnectionFactory factory = new ConnectionFactory(); // 设置服务地址 factory.setHost("127.0.0.1"); // AMQP协议:5672 factory.setPort(5672); // vohst factory.setVirtualHost("/test"); // 这个/test是刚刚创建的数据库 // 用户名 factory.setUsername("user"); // 密码 factory.setPassword("user"); // 用户名和密码一定要正确 return factory.newConnection(); } }
创建一个类发送消息
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.concurrent.TimeoutException; // 发送 public class Send { private static final String QUEUE_NAME = "test_simple_queue"; public static void main(String[] args) throws IOException, TimeoutException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); //创建队列,声明 channel.queueDeclare(QUEUE_NAME,false,false,false,null); String msg = "hello simple_queue"; // 发送消息 // 要将消息发送到的Exchange(交换器) 路由Key 消息内容 channel.basicPublish("",QUEUE_NAME,null,msg.getBytes()); System.out.println("send msg:"+msg); channel.close(); connection.close(); } }
然后创建一个消费者,只要发送者 发送信息到消息队列里,消费者就获取信息
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer { private static final String QUEUE_NAME = "test_simple_queue"; @SuppressWarnings("deprecation") public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 Channel channel = connection.createChannel(); // 声明队列 // 注意: 生产者和消费者都能够使用queueDeclare 来声明一个队列,但是如果消费者在同一个信道上订阅了另一个队列,就无法再声明队列了。必须先取消订阅,然后将信道置为"传输"模式,之后才能声明队列。 // 不带任何参数的queueDeclare: 方法默认创建一个由RabbitMQ 命名的(类似这种amq.gen-LhQzlgv3GhDOv8PIDabOXA 名称,这种队列也称之为匿名队列)、排他的、自动删除的、非持久化的队列。 // 队列名称 是否持久化 是否排他 是否自动删除 设置队列的其他一些参数,如 x-message-ttl等 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) { String msg = new String(body); System.out.println("new api customer:" + msg); } }; // 监听队列 队列 是否自动监听 channel.basicConsume(QUEUE_NAME,true,consumer); } }
简单队列的缺点:提供者和消费者一 一对应的,如果有多个消费者消费队列里的数据,就不行了,如果队列名变更的话,要同时变更
工作队列
轮询分发
比如,来个提供者发送数据,发送了50条数据
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.concurrent.TimeoutException; // 发送 public class Send { private static final String QUEUE_NAME = "test_work_queue"; public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); //创建队列,声明 channel.queueDeclare(QUEUE_NAME,false,false,false,null); for (int i = 0; i < 50; i++) { String msg = "hello"+i; System.out.println("send "+msg); channel.basicPublish("",QUEUE_NAME,null,msg.getBytes()); Thread.sleep(i*20); } channel.close(); connection.close(); } }
消费者1 给他线程休眠时2秒
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer1 { private static final String QUEUE_NAME = "test_work_queue"; public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 Channel channel = connection.createChannel(); // 声明队列 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("new api customer:" + msg); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,true,consumer); } }
消费者2 线程休眠时1秒
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer2 { private static final String QUEUE_NAME = "test_work_queue"; public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException,InterruptedException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 Channel channel = connection.createChannel(); // 声明队列 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("new api customer:" + msg); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,true,consumer); } }
消费者1 和 消费者2 代码是一样的,只是设置了线程休眠时间不一样,但是最后的结果是两个消费者轮流者获取的消息队列里的消息,你一个我一个的,消费者1 获取的都是奇数,消费者2 获取的都是偶数的,获取数量是一样的,这个是轮询分发。
公平分发
必须要关闭自动监听队列里(true) 改成手动的,改成false
公平分发就是说能者多劳,谁跑得快谁得到的消息就多
提供者发送消息:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.concurrent.TimeoutException; // 发送 public class Send { private static final String QUEUE_NAME = "test_justice_queue"; public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); //创建队列,声明 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 一次只发送一个消息到消息队列,这样的话等于是两个消费者在抢消息 int prefetchCount = 1; channel.basicQos(prefetchCount); // 这里可以省略成一行 // 限流方案 /** 设置限流机制 * param1: prefetchSize,消息本身的大小 如果设置为0 那么表示对消息本身的大小不限制 * param2: prefetchCount,告诉rabbitmq不要一次性给消费者推送大于N个消息 * param3:global,是否将上面的设置应用于整个通道,false表示只应用于当前消费者 */ // channel.basicQos(0, 5, false); for (int i = 0; i < 50; i++) { String msg = "hello"+i; System.out.println("send "+msg); channel.basicPublish("",QUEUE_NAME,null,msg.getBytes()); Thread.sleep(i*20); } channel.close(); connection.close(); } }
消费者1:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer1 { private static final String QUEUE_NAME = "test_justice_queue"; public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException,InterruptedException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 声明队列 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 保证只分发一个 channel.basicQos(1); // 这里可以省略成一行 // 限流方案 /** 设置限流机制 * param1: prefetchSize,消息本身的大小 如果设置为0 那么表示对消息本身的大小不限制 * param2: prefetchCount,告诉rabbitmq不要一次性给消费者推送大于N个消息 * param3:global,是否将上面的设置应用于整个通道,false表示只应用于当前消费者 */ // channel.basicQos(0, 5, false); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("new api customer:" + msg); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { channel.basicAck(envelope.getDeliveryTag(),false); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,false,consumer); } }
消费者2:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer2 { private static final String QUEUE_NAME = "test_justice_queue"; public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException,InterruptedException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 声明队列 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 保证只分发一个 channel.basicQos(1); // 这里可以省略成一行 // 限流方案 /** 设置限流机制 * param1: prefetchSize,消息本身的大小 如果设置为0 那么表示对消息本身的大小不限制 * param2: prefetchCount,告诉rabbitmq不要一次性给消费者推送大于N个消息 * param3:global,是否将上面的设置应用于整个通道,false表示只应用于当前消费者 */ // channel.basicQos(0, 5, false); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("new api customer:" + msg); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }finally { channel.basicAck(envelope.getDeliveryTag(),false); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,false,consumer); } }
消息应答
就是说,消费者里面最后的 监听队列里的配置 的那个boolean 值
// 监听队列
channel.basicConsume(QUEUE_NAME,false,consumer);
如果里面的这个boolean 值是true,就是自动确认模式,一旦消息给了消费者,就会从内存中删除消息,如果消费者瘫痪,或被黑客攻击导致,就会直接丢失正在处理的消息,但是如果把这个boolean 值给成false,就是手动模式,如果这个消费者挂掉了,就不会再给这个消费者消息了。如果是正常的,消费者处理完成消息以后,就会相应给消息队列,这个请求已经处理完成了,可以删除了,
然后rabbitMQ就会从内存中把消息删除
持久化
在写声明队列时候,里面的第二个boolean 值
// 声明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
把这个boolean 值改成 true,就可以持久化了,但是要注意:
*:就算代码是正确的,如果改成true是会报错的,
原因:这个是根据里面的这个:QUEUE_NAME 来判断的,rabbitMQ不允许重新定义一个已经存在的消息队列,除非再写一个,改个名字,的类似于这种方法。
订阅模式
模式解读:
1、一个生产者,多个消费者
2、每一个消费者都有一个自己的队列
3、提供者没有直接把消息发送到队列里,而是发送到了 转发器
4、每个队列都要绑定到这个转发器上
5、提供者发送消息,经过转发器,到达队列,从而就算一个消息被多个消费者消费。
代码实现:
提供者发送消息:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.concurrent.TimeoutException; // 发送 public class Send { private static final String EXCHANGE_NAME = "test_exchange_fanout"; public static void main(String[] args) throws IOException, TimeoutException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); // 声明转发器 channel.exchangeDeclare(EXCHANGE_NAME,"fanout"); // 分发 fanout:不处理路由键 // 发送消息 String msg = "hello ps"; channel.basicPublish(EXCHANGE_NAME,"",null,msg.getBytes()); System.out.println("send msg:"+msg); channel.close(); connection.close(); } }
消费者1:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer1 { private static final String QUEUE_NAME = "test_queue_fanout_email"; // 队列 private static final String EXCHANGE_NAME = "test_exchange_fanout"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 绑定队列到转发器 channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,""); // 保证只分发一个 channel.basicQos(1); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("【1】 api customer:" + msg); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("[1]"); channel.basicAck(envelope.getDeliveryTag(),false); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,false,consumer); } }
消费者2:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer2 { private static final String QUEUE_NAME = "test_queue_fanout_sms"; // 队列 private static final String EXCHANGE_NAME = "test_exchange_fanout"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 绑定队列到转发器 channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,""); // 保证只分发一个 channel.basicQos(1); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("【2】 api customer:" + msg); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("[2]"); channel.basicAck(envelope.getDeliveryTag(),false); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,false,consumer); } }
路由模式
就是分发消息的时候,要指定发送什么类型的,如果消费者里没有设置有这种类型的,就接受不到
发送者:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.concurrent.TimeoutException; // 发送 public class Send { private static final String EXCHANGE_NAME = "test_exchange_direct"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); // 声明转发器 channel.exchangeDeclare(EXCHANGE_NAME,"direct"); // 分发 之前这里是fanout,把路由改成direct // 发送消息 String msg = "hello direct"; // 这里如果写error 的话,消费者1 和2 都能接收到,如果改成info,只有消费者2 才能接收,因为消费者1 里面没有设置 channel.basicPublish(EXCHANGE_NAME,"info",null,msg.getBytes()); System.out.println("send msg:"+msg); channel.close(); connection.close(); } }
消费者1:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer1 { private static final String QUEUE_NAME = "test_queue_direct_1"; // 队列 private static final String EXCHANGE_NAME = "test_exchange_direct"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 绑定队列到转发器 channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error"); // 提供者发送error 时候,只能这个消费者1接受, // 保证只分发一个 channel.basicQos(1); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("【1】 api customer:" + msg); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("[1]"); channel.basicAck(envelope.getDeliveryTag(),false); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,false,consumer); } }
消费者2:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer2 { private static final String QUEUE_NAME = "test_queue_direct_2"; // 队列 private static final String EXCHANGE_NAME = "test_exchange_direct"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException,InterruptedException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 绑定队列到转发器 channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error"); channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"info"); // 提供者发送error 时候,消费者2能接受这三种的 // 保证只分发一个 channel.basicQos(1); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("【2】 api customer:" + msg); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("[2]"); channel.basicAck(envelope.getDeliveryTag(),false); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,false,consumer); } }
主题模式 和路由模式差不多的,只是通过匹配符绑定消费者可以接受哪种消息
发送消息指定执行的什么方法,消费者里要设置方法,没有包含发送者所指定的方法,就接收不到,所以也就无法执行
比如:goods.add 表示添加的方法,goods.delete 就表示删除方法
goods.# 表示可以接受所有的方法
发送者:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.concurrent.TimeoutException; // 发送 public class Send { private static final String EXCHANGE_NAME = "test_exchange_topic"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); // 声明转发器 channel.exchangeDeclare(EXCHANGE_NAME,"topic"); // 分发 之前这里是fanout,把路由改成topic,就是主题模式 // 发送消息 String msg = "商品。。。。。"; channel.basicPublish(EXCHANGE_NAME,"goods.add",null,msg.getBytes()); // 这里的goods.add 是表示添加商品,比如:goods.delete 就是代表删除商品 System.out.println("send msg:"+msg); channel.close(); connection.close(); } }
消费者1:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer1 { private static final String QUEUE_NAME = "test_queue_topic_1"; // 队列 private static final String EXCHANGE_NAME = "test_exchange_topic"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 绑定队列到转发器 channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.add"); // 消费者1 添加时候可以拿到数据 // 保证只分发一个 channel.basicQos(1); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("【1】 api customer:" + msg); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("[1]"); channel.basicAck(envelope.getDeliveryTag(),false); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,false,consumer); } }
消费者2:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer2 { private static final String QUEUE_NAME = "test_queue_topic_2"; // 队列 private static final String EXCHANGE_NAME = "test_exchange_topic"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 绑定队列到转发器 channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.delete"); // channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.#"); // 消费者2 所有的都能拿到 // 保证只分发一个 channel.basicQos(1); // 定义消费者 DefaultConsumer consumer = new DefaultConsumer(channel) { // 一旦队列里有消息就触发这个方法获取消息 @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String msg = new String(body,"utf-8"); System.out.println("【2】 api customer:" + msg); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("[2]"); channel.basicAck(envelope.getDeliveryTag(),false); } } }; // 监听队列 channel.basicConsume(QUEUE_NAME,false,consumer); } }
消息确认机制
在rabbitMQ 中,我们可以通过持久化数据,解决服务器异常的造成数据丢失。
但是,生产者发送消息出去以后,消息有没有到达rabbitMQ 服务器,默认情况是不知道的。
两种方式:
AMQP 实现事务机制
Confirm 模式
事务机制
txSelect 用户将当前channel(就是那个通道) 设置成 事务模式
txCommit 提交事务
txRollback 回滚事务
这些都和mysql 里面一样的
生产者发送消息:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.concurrent.TimeoutException; // 发送 public class TxSend { private static final String QUEUE_NAME = "test_queue_tx"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 发送消息 String msg = "hello tx message"; try { // 设置成事务模式 channel.txSelect(); channel.basicPublish("", QUEUE_NAME, null, msg.getBytes()); int s = 1 / 0; // 【【这个是测试异常用的,如果有了异常,消费者就接受不到消息了,因为事务回滚了】】 System.out.println("send " + msg); // 提交事务 channel.txCommit(); } catch (IOException e) { // 有异常回滚事务 channel.txRollback(); System.out.println("send message txRollback"); } channel.close(); connection.close(); } }
消费者:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class TxCustomer { private static final String QUEUE_NAME = "test_queue_tx"; // 队列 public static void main(String[] args) throws IOException, TimeoutException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME, false, false, false, null); channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("customer[tx] msg =====>> " + new String(body, "utf-8")); } }); } }
这种事务模式的缺点是提交的请求太多,降低了服务器的吞吐量,相比起来,confirm串行模式要好很多
confirm串行模式
confirm模式最大的好处就是,异步执行!
channel.chonfirmSelect() 开启confirm 模式
编程模式:
普通 发一条 waitForConfirms()
批量的 发一批 waitForConfirms()
异步 confirm 模式:提供一个回调方法
普通、单条:
发送消息:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.concurrent.TimeoutException; // 发送 public class Send { private static final String QUEUE_NAME = "test_queue_confirm1"; // 转发器 public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 生产者调用 confirmSelect,将channel设置为confirm 模式 // 切记,如果这个队列是之前的已经存在的队列,这样设置会报异常 channel.confirmSelect(); // 发送消息 String msg = "hello confirm message"; channel.basicPublish("",QUEUE_NAME,null,msg.getBytes()); if (!channel.waitForConfirms()){ System.out.println("发送失败"); }else { System.out.println("发送成功"); } channel.close(); connection.close(); } }
消费者接收:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer { private static final String QUEUE_NAME = "test_queue_confirm1"; // 队列 public static void main(String[] args) throws IOException, TimeoutException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME, false, false, false, null); channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("customer msg =====> " + new String(body, "utf-8")); } }); } }
批量消息:
和单条信息发送的消费者是一样的,只是提供者发送消息时候,用循环发送了多条,然后再 确认
提供者发送消息:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.concurrent.TimeoutException; // 批量发送 public class Send { private static final String QUEUE_NAME = "test_queue_confirm1"; public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME,false,false,false,null); // 生产者调用 confirmSelect,将channel设置为confirm 模式 // 切记,如果这个队列是之前的已经存在的队列,这样设置会报异常 channel.confirmSelect(); // 发送消息 String msg = "hello confirm message"; for (int i = 0; i < 10; i++) { channel.basicPublish("",QUEUE_NAME,null,msg.getBytes()); } // 确认 if ( !channel.waitForConfirms()){ System.out.println("发送失败"); }else { System.out.println("发送成功"); } channel.close(); connection.close(); } }
消费者:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer { private static final String QUEUE_NAME = "test_queue_confirm1"; // 队列 public static void main(String[] args) throws IOException, TimeoutException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME, false, false, false, null); channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("customer msg =================> " + new String(body, "utf-8")); } }); } }
异步:
提供者:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.ConfirmListener; import com.rabbitmq.client.Connection; import java.io.IOException; import java.util.Collections; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.TimeoutException; // 发送 public class Send3 { private static final String QUEUE_NAME = "test_queue_confirm3"; public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { // 创建一个连接 Connection connection = ConnectionUtils.getConnection(); // 从连接中获取一个通道 Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 生产者调用 confirmSelect,将channel设置为confirm 模式 // 切记,如果这个队列是之前的已经存在的队列,这样设置会报异常 channel.confirmSelect(); // 未确认的消息标识 final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>()); // 通道添加监听 channel.addConfirmListener(new ConfirmListener() { // 没有问题的 handleAck @Override public void handleAck(long l, boolean b) { if (b) { System.out.println("handleAck------multiple"); confirmSet.headSet(l + 1).clear(); } else { System.out.println("handleAck------multiple false"); confirmSet.remove(l); } } @Override public void handleNack(long l, boolean b) { if (b) { System.out.println("handleNack------multiple"); confirmSet.headSet(l + 1).clear(); } else { System.out.println("handleNack------multiple false"); confirmSet.remove(b); } } }); // 发送消息 String msg = "ssssssssssss"; while (true) { long segNo = channel.getNextPublishSeqNo(); channel.basicPublish("", QUEUE_NAME, null, msg.getBytes()); confirmSet.add(segNo); } } }
消费者:
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; // 消费者 public class Customer { private static final String QUEUE_NAME = "test_queue_confirm3"; // 队列 public static void main(String[] args) throws IOException, TimeoutException { // 获取链接 Connection connection = ConnectionUtils.getConnection(); // 创建通道 final Channel channel = connection.createChannel(); // 队列声明 channel.queueDeclare(QUEUE_NAME, false, false, false, null); channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("customer msg ==============> " + new String(body, "utf-8")); } }); } }
spring集成rabbitMQ
依赖:
<dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>1.7.5.RELEASE</version> </dependency>
在spring的xml配置文件里配置,这个文件名声context.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit" xsi:schemaLocation="http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.7.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!--定义rabbit MQ的连接工厂--> <rabbit:connection-factory id="connectionFactory" host="127.0.0.1" port="5672" username="user" password="user" virtual-host="/test"/> <!--定义rabbit模板,指定连接工厂以及定义exchange--> <rabbit:template id="template" connection-factory="connectionFactory" exchange="fanoutExchange"/> <!--mq 管理,包括队列,交换器声明等--> <rabbit:admin connection-factory="connectionFactory"/> <!--定义队列,自动声明--> <rabbit:queue name="myQueue" auto-declare="true" durable="true"/> <!--声明交换器,自动声明--> <rabbit:fanout-exchange name="fanoutExchange" auto-declare="true"> <rabbit:bindings> <rabbit:binding queue="myQueue"/> </rabbit:bindings> </rabbit:fanout-exchange> <!--队列监听--> <rabbit:listener-container connection-factory="connectionFactory"> <rabbit:listener ref="foo" method="listen" queue-names="myQueue"/> </rabbit:listener-container> <!--消费者--> <bean id="foo" class="com.biao.jiChengSpring.Consumer"/> </beans>
然后简单测试一下:
public class SpringMain { public static void main(String[] args) throws InterruptedException { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:context.xml"); // rabbitMQ 模板 RabbitTemplate template = ctx.getBean(RabbitTemplate.class); // 发送消息 template.convertAndSend("hello world"); Thread.sleep(1000); // 销毁,关闭 ((ClassPathXmlApplicationContext) ctx).destroy(); } }
消费者:
public class Consumer { // 具体执行业务的方法 public void listen(String foo){ System.out.println("消费者:"+foo); } }

浙公网安备 33010602011771号