RabbitMQ的使用
1.MQ是什么?
MQ:Message Queue :消息队列
队列:简单的说就是一种数据结构,先进先出,Redis(lpush+rpop)
消息队列:简单来说就是用来进行消息传输的
消息中间件:简单来说,就是用来传输消息的中间载体
中间件只有一个作用:就是将你的消息发送到接收方,他并不关心你发送的数据长啥样,就类似于生活中的快递员一样
而RabbitMQ就是这样的一个消息中间件
2.使用这个MQ能做什么?
2.1 流量消峰
如果有多个请求同时访问我们的服务器,利用消息中间件,将所有的并行请求串行化,然后再去访问咱们的服务器
如果一个队列有多个消费者,那么这些消费者的消费的数据之和才是队列中的所有数据
2.2 模块之间的异步通信
将消息放入到中间件中,所有的消费者都去消费这个消息,他们所消费的信息都是相同的
3.常见的消息中间件
ActiveMQ----JMS(sun公司提供的规范)java message Server
RabbitMQ
RocketMQ---- 阿里的
kafka ---用的比较多,最初的设计是用来完成分布式下日志的收集框架
4.RabbitMQ的安装
#1.安装之前需要的环境
yum install epel-release
yum install erlang
#2.安装RabbitMQ
下载rpm文件
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.15/rabbitmq-server-3.6.15-1.el7.noarch.rpm
#3.下载完成需要安装
yum install rabbitmq-server-3.6.15-1.el7.noarch.rpm
#4.设置开机启动
systemctl enable rabbitmq-server.service
#5.查看服务的状态
systemctl status rabbitmq-server.service
#6.启动这个服务
systemctl start rabbitmq-server.service
#7.查看当前所有的用户
rabbitmqctl list_users
#8.查看guest用户所有拥有的权限
rabbitmqctl list_user_permissions guest
#9.删除原来的guest用户
rabbitmqctl delete_user guest
#10.添加一个新的用户
rabbitmqctl add_user wangzi 12345678
#11.给wangzi设置个角色(tag)
rabbitmqctl set_user_tags wangzi administrator
#12.给wangzi赋予权限
rabbitmqctl set_permissions -p / wangzi ".*" ".*" ".*"
#查看用户所拥有的权限
rabbitmqctl list_user_permissions wangzi
#开启web的管理端
rabbitmq-plugins enable rabbitmq_management (运行两次)
#查看端口
netstat -apn | grep 5672
5.RabbitMQ中的五种通信模型
5.1 Helloworld模型
意思是:生产者将消息发送到队列,然后队列将这个消息发送给消费者
1. 导包
<!--导入rabbitmq相关的包-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.4.3</version>
</dependency>
2.编写工具类
public class RabbitMQUtils {
public static Connection getConnection() throws IOException, TimeoutException {
// 申明链接工厂
ConnectionFactory factory = new ConnectionFactory();
// 设置连接的主机(ip地址)
factory.setHost("192.168.1.19");
// 设置虚拟机
factory.setVirtualHost("/");
// 设置访问的用户名
factory.setUsername("");
// 设置密码
factory.setPassword("");
// 设置请求的端口
factory.setPort(5672);
// 创建链接
return factory.newConnection();
}
}
3.生产者
public class Producter {
// 队列的名字
private static final String QUEUE_NAME="helloworld";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建数据输出的通道
Channel channel = connection.createChannel();
// 3.申明队列
/**
* 第一个参数的名字:队列的名字
* 第二个参数:是否持久化,比如发送到队列的消息,如果没有持久化,重启队列之后,数据会丢失,队列都会被删除
* 持久化以后,重启以后,数据还在
* 第三个参数:是否排外:
* 1. 链接关闭以后,这个队列是否自动删除 true:删除
* 2.是否允许其他通道来进行访问这个数据
* 第四个参数:是否允许自动删除 true:自动删除
* 当最后一个链接断开的时候,这个时候是否允许删除这个队列
* 第五个参数:申明队列的时候,要附带的一些参数
*/
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 4.发送数据到队列
/**
* 第一个参数:exchange 交换机 没有就设置为空值
* 第二个参数:routingKey:路由的key,现在没有key,直接使用队列的名字
* 第三个参数:发送到队列的时候,是否要带一些参数,没有带任何参数
* 第四个参数:向队列发送的数据
*/
channel.basicPublish("",QUEUE_NAME,null,"rabbitMQ通信模型之helloworld111".getBytes());
channel.close();
connection.close();
}
}
4.消费者
public class Consumer {
// 队列的名字
private static final String QUEUE_NAME="helloworld";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取连链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建通道
final Channel channel = connection.createChannel();
// 3.申明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 4.消费者的声明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
/**
*
* @param consumerTag :这个消息的唯一标记
* @param envelope :信封:请求的消息的属性的一些封装
* @param properties 队列的属性值 (前面队列带过来的值)
* @param body :接受到的消息
* @throws IOException
*/
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("接收到的消息是"+new String(body));
// 进行手动应答
/**
* 第一个参数:自动应答的消息的标记
* 第二个参数:false:就相当于告诉队列收到消息了
*/
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
// 5.绑定这个消费者
/**
* 第一个参数:队列的名字
* 第二个参数:是否自动应答
* 当消费者获取到这个消息以后,如果你设置了自动应答(给谁应答:给队列应答),如果设置为
* true,那么就会告诉队列收到了这个消息,队列就会自动将这个消息删除(防止消息重复消费)
* 手动应答:当收到消息,你需要自己调用这个代码完成这个消费
* 第三个参数:消费者的声明
*/
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
// 如果没有关闭链接,则消费者会一直等待消息
}
}
5.2 work模型
一个生产者将消息发送到队列,有多个消费者消费消息,多个消费者消费的消息之和等于总的消息数据(流量消峰)
3.生产者
public class Producer {
private static final String QUEUE_NAME="work-queue";
public static void main ( String[] args ) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
// 创建数据输出的通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 发送数据到队列
for (int i = 0; i <100 ; i++) {
channel.basicPublish("",QUEUE_NAME,null,("work"+i).getBytes());
}
channel.close();
connection.close();
}
}
4.多个消费者
public class Consumer01 {
private static final String QUEUE_NAME = "work-queue";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 获取链接
Connection connection = RabbitMQUtils.getConnection();
// 获取通道
Channel channel = connection.createChannel();
// 队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 消费者声明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println(new String(body));
}
};
// 绑定消费者
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
/**
* @Author
* @Date
*/
public class Consumer02 {
private static final String QUEUE_NAME="work-queue";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 获取链接
Connection connection = RabbitMQUtils.getConnection();
// 获取数据传输的通道
Channel channel = connection.createChannel();
// 对列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 消费者的声明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println(new String(body));
}
};
// 绑定这个消费者
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
5.3 发布订阅模型
简单的说,就是队列里面的消息会被几个消费者同时接受到,每个消费者接收到的数据都是相同的,都是生产者的全部消息
模型:适合做模块之间的异步通信
举例: 缓存同步,高并发下的单逻辑
1. 生产者
/**
* @Author
* @Date
*/
public class producter {
private static final String EXCHANGE_NAME = "publish-fanout";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 获取链接
Connection connection = RabbitMQUtils.getConnection();
// 获取通道
Channel channel = connection.createChannel();
// 交换机的声明
/**
* 第一个参数:交换机的名字
* 第二个参数:交换机的类型 如果是发布订阅模式,只能写fanout
*/
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
// 将消息发送到交换机
for (int i = 0; i <100; i++) {
channel.basicPublish(EXCHANGE_NAME,"",null,("publish-fanout"+i).getBytes());
}
channel.close();
connection.close();
}
}
2. 多个消费者
/**
* @Author
* @Date
*/
public class consumer01 {
private static final String QUEUE_NAME01 = "queue01";
private static final String EXCHANGE_NAME = "publish-fanout";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 获取链接
Connection connection = RabbitMQUtils.getConnection();
// 获取通道
Channel channel = connection.createChannel();
// 申明队列
channel.queueDeclare(QUEUE_NAME01,false,false,false,null);
// 申明交换机
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
// 将队列绑定到交换机
/**
* 第一个参数:队列的名字
* 第二个参数:交换机的名字
* 第三个参数:路由的key
*/
channel.queueBind(QUEUE_NAME01,EXCHANGE_NAME,"");
// 消费者申明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("队列一的消息"+":"+new String(body));
}
};
// 绑定消费者
channel.basicConsume(QUEUE_NAME01,true,defaultConsumer);
}
}
/**
* @Author
* @Date 2020/5/4
*/
public class consumer02 {
private static final String QUEUE_NAME02 = "queue02";
private static final String EXCHANGE_NAME = "publish-fanout";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 获取链接
Connection connection = RabbitMQUtils.getConnection();
// 获取通道
Channel channel = connection.createChannel();
// 申明队列
channel.queueDeclare(QUEUE_NAME02,false,false,false,null);
// 申明交换机
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
// 将队列绑定到交换机
/**
* 第一个参数:队列的名字
* 第二个参数:交换机的名字
* 第三个参数:路由的key
*/
channel.queueBind(QUEUE_NAME02,EXCHANGE_NAME,"");
// 消费者申明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("队列二的消息"+":"+new String(body));
}
};
// 绑定消费者
channel.basicConsume(QUEUE_NAME02,true,defaultConsumer);
}
}
5.4 路由模型
通过不同的路由,接受到不同的消息
1. 生产者
/**
* @Author
* @Date
*/
public class Producter {
private static final String EXCHANGE_NAME = "routing-direct";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 获取链接
Connection connection = RabbitMQUtils.getConnection();
// 获取通道
Channel channel = connection.createChannel();
// 如果是路由模型,交换机的类型的只能是direct
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
// 发送信息到交换机
for (int i = 0; i <100 ; i++) {
// 路由的键是可以随便设置的
channel.basicPublish(EXCHANGE_NAME,"blank",null,("路由模型的值"+i).getBytes());
channel.basicPublish(EXCHANGE_NAME,"wangzi",null,"nihao".getBytes());
}
channel.close();
connection.close();
}
}
2. 多个消费者
/**
* @Author
* @Date
*/
public class Consumer01 {
private static final String QUEUE_NAME= "route-queue001";
private static final String EXCHANGE_NAME = "routing-direct";
public static void main ( String[] args ) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"blank");
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("队列一接受到的消息"+ new String(body));
}
};
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
/**
* @Author
* @Date
*/
public class Consumer02 {
private static final String QUEUE_NAME= "route-queue002";
private static final String EXCHANGE_NAME = "routing-direct";
public static void main ( String[] args ) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"wangzi");
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("队列二接受到的消息"+ new String(body));
}
};
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
5.5 topic模式
topic模式相当于是对路由模型的一个升级,topic模式主要就是在匹配的规则上可以模糊匹配
# 匹配一个或多个词
* 匹配一个词
eg: user.* 能够匹配到user.add
user.# 能够匹配到user.add.delete
1. 生产者的写法
/**
* @Author
* @Date
*/
public class Producter {
private static final String EXCHANGE_NAME = "routing-topic";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 获取链接
Connection connection = RabbitMQUtils.getConnection();
// 获取通道
Channel channel = connection.createChannel();
// 如果是路由模型,交换机的类型的只能是direct
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
// 发送信息到交换机
for (int i = 0; i <100 ; i++) {
// 路由的键是可以随便设置的
// 如果使用了交换机,发送完数据,没有消费者的话,数据会丢失,所以需要持久化MessageProperties.PERSISTENT_TEXT_PLAIN
channel.basicPublish(EXCHANGE_NAME,"blank.blank", MessageProperties.PERSISTENT_TEXT_PLAIN,("路由模型的值"+i).getBytes());
channel.basicPublish(EXCHANGE_NAME,"wangzi.wang.zi",MessageProperties.PERSISTENT_TEXT_PLAIN,"nihao".getBytes());
}
channel.close();
connection.close();
}
}
2.多个消费者
/**
* @Author
* @Date
*/
public class Consumer01 {
private static final String QUEUE_NAME= "topic-queue001";
private static final String EXCHANGE_NAME = "routing-topic";
public static void main ( String[] args ) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"blank.*");
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("队列一接受到的消息"+ new String(body));
}
};
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
/**
* @Author
* @Date
*/
public class Consumer02 {
private static final String QUEUE_NAME= "topic-queue002";
private static final String EXCHANGE_NAME = "routing-topic";
public static void main ( String[] args ) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"wangzi.#");
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("队列二接受到的消息"+ new String(body));
}
};
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
5.6 通信模型的备注
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments)
durable :是否持久化,准确的说,持久化的是队列,并不是里面的数据
如果要数据持久化的话应该这么设置:MessageProperties.PERSISTENT_TEXT_PLAIN
channel.basicPublish(EXCHANGE_NAME,"wangzi.wang.zi",MessageProperties.PERSISTENT_TEXT_PLAIN,"nihao".getBytes());
6.RabbitMQ的一些高级属性
6.0 参数的含义
持久化: 如果不持久化,那么RabbitMQ重启以后,数据会丢失,队列都会被删除
是否排外:链接一旦关闭,就会删除这个队列 channel.close(); connection.close();
是否自动删除:当最后一个消费者的链接断开的时候,这个队列自动删除
6.1 confirm机制
问题:放到队列中的消息,怎么保证就一定是成功的放入了队列
引入了confirm机制,这个机制的意思就是,只要消息放到queue是成功的,那么这个队列就一定会进行反馈
1.生产者的编写
public class Producter {
// 队列的名字
private static final String QUEUE_NAME="helloworld-confirm";
public static void main ( String[] args ) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
// 开启confirm消息确认
channel.confirmSelect();
// 对消息的可能性进行实时监听
// 对消息的签收情况进行确认
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck ( long l, boolean b ) throws IOException {
System.out.println("发送成功的监听。。");
}
@Override
public void handleNack ( long l, boolean b ) throws IOException {
System.out.println("发送失败的监听。。");
}
});
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.basicPublish("",QUEUE_NAME,null,"rabbitMQ通信模型之helloworld111".getBytes());
}
}
2.消费者的编写
public class Consumer {
// 队列的名字
private static final String QUEUE_NAME="helloworld-confirm";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取连链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建通道
final Channel channel = connection.createChannel();
// 3.申明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 4.消费者的声明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
/**
*
* @param consumerTag :这个消息的唯一标记
* @param envelope :信封:请求的消息的属性的一些封装
* @param properties 队列的属性值 (前面队列带过来的值)
* @param body :接受到的消息
* @throws IOException
*/
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("接收到的消息是"+new String(body));
// 进行手动应答
/**
* 第一个参数:自动应答的消息的标记
* 第二个参数:false:就相当于告诉队列收到消息了
*/
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
// 5.绑定这个消费者
/**
* 第一个参数:队列的名字
* 第二个参数:是否自动应答
* 当消费者获取到这个消息以后,如果你设置了自动应答(给谁应答:给队列应答),如果设置为
* true,那么就会告诉队列收到了这个消息,队列就会自动将这个消息删除(防止消息重复消费)
* 手动应答:当收到消息,你需要自己调用这个代码完成这个消费
* 第三个参数:消费者的声明
*/
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
// 如果没有关闭链接,则消费者会一直等待消息
}
}
6.2 return机制
场景:我们在发送消息的时候,我们指定的交换机不存在,或者指定的路由的key不存在,这种时候,我们需要监听着各种不可达的消息。这个时候,return机制就产生了
前提:必须有消费者存在
mandatory:如果设置为ture:就表示的是要监听不可达的消息 进行处理
如果设置为false 那么队列端会直接删除这个消息
1. 生产者的编写
/**
* @Author
* @Date 2020/5/4
*/
public class Produter {
private static final String EXCAHNGE_NAME = "return";
private static final String ROUTE_KEY="return.wangzi";
private static final String ERROR_KEY = "abc.zi";
public static void main ( String[] args ) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
// 添加return监听
channel.addReturnListener(new ReturnListener() {
/**
*
* @param i :队列相响应给浏览器的状态码
* @param s :表示的状态码对应的文本信息
* @param s1 :交换机的名字
* @param s2 :路由的key
* @param basicProperties :消息的属性
* @param bytes :消息体的内容
* @throws IOException
*/
@Override
public void handleReturn ( int i, String s, String s1, String s2, AMQP.BasicProperties basicProperties, byte[] bytes ) throws IOException {
System.out.println("监听到不可达的消息");
System.out.println(s2);
}
});
channel.exchangeDeclare(EXCAHNGE_NAME,"topic");
channel.basicPublish(EXCAHNGE_NAME,ERROR_KEY,true,null,"测试消息".getBytes());
}
}
2.消费者的编写
/**
* @Author
* @Date
*/
public class Consumer {
private static final String EXCAHNGE_NAME = "return";
private static final String ROUTE_KEY="return.#";
private static final String QUEUE_NAME = "return_queue";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 获取链接
Connection connection = RabbitMQUtils.getConnection();
// 获取通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME,true,false,false,null);
// 声明交换机
channel.exchangeDeclare(EXCAHNGE_NAME,"topic");
// 绑定
channel.queueBind(QUEUE_NAME,EXCAHNGE_NAME,ROUTE_KEY);
// 声明消费者
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("收到这个消息了");
}
};
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
6.3 消费端的限流
场景:消费者死了,队列里面一瞬间就积累了上万条数据,当我们再打开客户端的时候,瞬间就有巨量的信息推送过来,但是我们的客户端是没有办法同时处理这么多数据的,结果就是消费者又死了
这种场景下,我们就需要对消费端进行限流
1生产者的编写
public class Producter {
// 队列的名字
private static final String QUEUE_NAME="limit-helloworld";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建数据输出的通道
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
for (int i = 0; i <100 ; i++) {
channel.basicPublish("",QUEUE_NAME,null,("helloworld"+i).getBytes());
}
channel.close();
connection.close();
}
}
2. 消费者的编写
public class Consumer {
// 队列的名字
private static final String QUEUE_NAME="limit-helloworld";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取连链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建通道
final Channel channel = connection.createChannel();
// 3.申明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 限流机制
/**
* 第一个参数:消息本身的大小如果设置为0,那么表示对消息本身的大小不限制
* 第二个参数:告诉RabbitMQ不要一次性给消费者大于n个的消息,你要推送的前提是
* 现在这n个消息已经手动确认
* 第三个参数:true或者false,是否将上面的设置,应用于整个通道
* true:整个通道里面都是这个策略
* false:当前的Consumer是这个策略
*/
channel.basicQos(0,5,false);
// 如果不设置的话,这个任务一开始就分配好了,还有必须手动确认消息是否完成
// 4.消费者的声明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接收到的消息是"+new String(body));
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
// 如果没有关闭链接,则消费者会一直等待消息
}
}
// 第二个消费者
public class Consumer02 {
// 队列的名字
private static final String QUEUE_NAME="limit-helloworld";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取连链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建通道
final Channel channel = connection.createChannel();
// 3.申明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 4.消费者的声明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("接收到的消息是"+new String(body));
// 进行手动应答
/**
* 第一个参数:自动应答的消息的标记
* 第二个参数:false:就相当于告诉队列收到消息了
*/
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
// 如果没有关闭链接,则消费者会一直等待消息
}
}
6.4 TTL(Time to Live)队列
场景:我要下单,下单之后在一定的时间内,我的订单如果没有被处理,那么就会自动失效
备注:简单的说,就是咱们的队列中的消息是有时间限制的,如果超时那么这个消息会被队列给删除
1. 生产者的编写
public class Producter {
// 队列的名字
private static final String QUEUE_NAME="ttl-helloworld";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建数据输出的通道
Channel channel = connection.createChannel();
// 只要给下面的队列设置好属性,那么就变成ttl队列
Map<String,Object> map = new HashMap();
// 如果5s的时间内,没有消费者消费,那么这个数据就会失效
map.put("x-message-ttl",5000);
channel.queueDeclare(QUEUE_NAME,false,false,false,map);
for (int i = 0; i <100 ; i++) {
channel.basicPublish("",QUEUE_NAME,null,("helloworld"+i).getBytes());
}
channel.close();
connection.close();
}
}
2.消费者的编写
public class Consumer02 {
// 队列的名字
private static final String QUEUE_NAME="ttl-helloworld";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取连链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建通道
final Channel channel = connection.createChannel();
// 3.申明队列
Map<String,Object> map = new HashMap();
map.put("x-message-ttl",5000);
channel.queueDeclare(QUEUE_NAME,false,false,false,map);
// 4.消费者的声明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("接收到的消息是"+new String(body));
// 进行手动应答
/**
* 第一个参数:自动应答的消息的标记
* 第二个参数:false:就相当于告诉队列收到消息了
*/
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
// 如果没有关闭链接,则消费者会一直等待消息
}
}
6.5 死信队列
什么是死信队列?
当消息在队列中变成死信以后,可以定义它重新push到另外一个交换机上,这个交换机也有自己对应的队列,这个队列被称为死信队列
死信:发送到队列中的消息被拒绝;消息的ttl时间过期;队列达到了最大长度,再往里面放信息
在满足上面死信的前提下,现在我们定义一个队列,这个队列专门处理死信
死信队列也是一个正常的交换机,和一般的交换机没有什么区别
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yRJKkQWo-1603889821189)(/1588596446943.png)]
1. 生产者的编写
/**
* @Author
* @Date
* 生产者发消息是发送到了正常的交换机上面
*/
public class Producter {
// 定义正常的交换机
private static final String NORMAL_EXCHANGE = "die_ttl_exchange";
// 路由的key
private static final String ROUTE_KEY = "die.ttl";
public static void main ( String[] args ) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
/**
* 有一个正常的交换机用来发送消息,队列:正常情况下的队列(ttl队列)
* 还得有一个队列,就是死信队列(过期之后,自动路由的队列)
*/
for (int i = 0; i <5 ; i++) {
channel.basicPublish(NORMAL_EXCHANGE,ROUTE_KEY,false,null,("nihao"+i).getBytes());
}
}
}
2.消费者的编写
public class Consumer {
// 正常的交换机
private static final String NORMAL_EXCHANGE = "die_ttl_exchange";
// 定义死性队列交换机的名称
private static final String ERROR_EXCHANGE = "die_exchange";
private static final String ROUTE_KEY = "die.#";
// 正常的队列(ttl队列)
private static final String QUEUE_NAME = "die_ttl_queue";
// 死性队列
private static final String ERROR_QUEUE_NAME = "die_queue";
public static void main ( String[] args ) throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(NORMAL_EXCHANGE,"topic",true);
// 声明队列为ttl队列
Map<String,Object> map = new HashMap();
map.put("x-message-ttl",5000);
// 添加死性的属性
map.put("x-dead-letter-exchange",ERROR_EXCHANGE);
channel.queueDeclare(QUEUE_NAME,true,false,false,map);
channel.queueBind(QUEUE_NAME,NORMAL_EXCHANGE,ROUTE_KEY);
//
channel.exchangeDeclare(ERROR_EXCHANGE,"topic");
// 声明死性队列
channel.queueDeclare(ERROR_QUEUE_NAME,true,false,false,null);
// 进行死性队列的绑定
channel.queueBind(ERROR_QUEUE_NAME,ERROR_EXCHANGE,"#");
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("获取到数据"+new String (body));
}
};
channel.basicConsume(QUEUE_NAME,true ,defaultConsumer);
// 死性队列的监听
channel.basicConsume(ERROR_QUEUE_NAME,true,defaultConsumer);
}
}
6.6 消费者端手动签收和消息重回队列
场景:消费者端接收到了咱们队列中的数据,但是在进行业务逻辑处理的时候,发现一个问题,业务逻辑处理失败?怎么办?
手动签收应答,也可以手动拒绝,让消息重回队列
1. 生产者的编写
public class Producter {
// 队列的名字
private static final String QUEUE_NAME="ack_helloworld";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建数据输出的通道
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.basicPublish("",QUEUE_NAME,null,"rabbitMQ通信模型之helloworld111".getBytes());
channel.close();
connection.close();
}
}
2.消费者的编写
public class Consumer {
// 队列的名字
private static final String QUEUE_NAME="ack_helloworld";
public static void main ( String[] args ) throws IOException, TimeoutException {
// 1.获取连链接
Connection connection = RabbitMQUtils.getConnection();
// 2.创建通道
final Channel channel = connection.createChannel();
// 3.申明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 4.消费者的声明
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
System.out.println("接收到的消息是"+new String(body));
// 进行手动应答
/**
* 第一个参数:自动应答的消息的标记
* 第二个参数:false:就相当于告诉队列收到消息了
*/
// channel.basicAck(envelope.getDeliveryTag(),false);
// 也可以拒绝签收
// 第三个参数表示拒绝签收之后,是否重回队列
channel.basicNack(envelope.getDeliveryTag(),false,true);
}
};
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
// 如果没有关闭链接,则消费者会一直等待消息
}
}