RabbitMQ平台创建用户 zxj 123,创建vhost /vhost_mmr
1、工具类
package com.zxj.utils;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author zxj
* @date 2020/6/2 16:38
*/
public class ConnectionUtil {
public static Connection getConnection() throws IOException, TimeoutException {
//定义一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("127.0.0.1");
//设置端口号
factory.setPort(5672);
//设置vhost
factory.setVirtualHost("/vhost_mmr");
//用户名
factory.setUsername("zxj");
//密码
factory.setPassword("123");
return factory.newConnection();
}
}
2、点对点方式
2.1、生产者
package com.zxj.simple;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 简单模式 点对点 生产者
* @author zxj
* @date 2020/6/2 16:43
*/
public class Send {
private static final String QUEUE_NAME="test_simple_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取一个连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取一个通道
Channel channel = connection.createChannel();
//创建队列声明
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msg = "hello simple";
//发送消息
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
System.out.println("====== send msg : " + msg);
channel.close();
connection.close();
}
}
2.2、消费者
package com.zxj.simple;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 简单模式 点对点 消费者
* @author zxj
* @date 2020/6/2 16:52
*/
public class Recv {
private static final String QUEUE_NAME="test_simple_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
// oldAPI();
newAPI();
}
public static void newAPI() throws IOException, TimeoutException {
//获取一个连接
Connection connection = ConnectionUtil.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 msgRecv = new String(body, "utf-8");
System.out.println("newApi msgRecv : " + msgRecv);
}
};
//监听队列
channel.basicConsume(QUEUE_NAME, true, consumer);
}
public void oldAPI() throws IOException, TimeoutException, InterruptedException{
//获取一个连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取一个通道
Channel channel = connection.createChannel();
//定义一个消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//监听消息队列
channel.basicConsume(QUEUE_NAME, true, consumer);
while (true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msgRecv = new String(delivery.getBody());
System.out.println("Recv msg : " + msgRecv);
}
}
}
3、WorkQueue 轮询分发(默认) 一个生产者对多个消费者
3.1、生产者
package com.zxj.workqueue;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* work queue 工作队列 一个生产者对多个消费者
*
* 默认是轮询分发消息
*
* 生产者
* @author zxj
* @date 2020/6/2 17:22
*/
public class Send {
private static final String QUEUE_NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取通道
Channel channel = connection.createChannel();
//声明消息队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发送消息
for (int i = 0; i < 50; i++) {
String msg = "hello work queue : " + i;
channel.basicPublish("", QUEUE_NAME,null, msg.getBytes());
TimeUnit.MILLISECONDS.sleep(i*20);
}
//关闭通道和连接
channel.close();
connection.close();
}
}
3.2、消费者1与消费者2
package com.zxj.workqueue;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* work queue 工作队列 一个生产者对多个消费者
* 消费者1
* @author zxj
* @date 2020/6/2 17:28
*/
public class Recv1 {
private static final String QUEUE_NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtil.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 msgRecv1 = new String(body, "utf-8");
System.out.println("1 msgRecv1 : " + msgRecv1);
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//监听消息队列
boolean autoAck = true;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
package com.zxj.workqueue;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* work queue 工作队列 一个生产者对多个消费者
* 消费者2
* @author zxj
* @date 2020/6/2 17:28
*/
public class Recv2 {
private static final String QUEUE_NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtil.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 msgRecv2 = new String(body, "utf-8");
System.out.println("2 msgRecv2 : " + msgRecv2);
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//监听消息队列
boolean autoAck = true;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
4、WorkQueue 公平分发,
每个消费者一次只处理一条消息,处理完成后返回ack给消息队列,获取下一条消息, 消费者需要关闭ack的自动应答,改为手动应答
4.1生产者
package com.zxj.workqueuefair;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* work queue 工作队列 一个生产者对多个消费者
*
* 默认是轮询分发消息
* 此处使用公平分发模式,即每个消费者一次只处理一条消息,处理完成后返回ack给消息队列,获取下一条消息,
* 消费者需要关闭ack的自动应答,改为手动应答
*
* 生产者
* @author zxj
* @date 2020/6/2 17:22
*/
public class Send {
private static final String QUEUE_NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取通道
Channel channel = connection.createChannel();
//声明消息队列
boolean durable = false;//是否开启消息持久化,false不开启,true开启
channel.queueDeclare(QUEUE_NAME, durable, false, false, null);
// 生产者开启公平分发
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//发送消息
for (int i = 0; i < 50; i++) {
String msg = "hello work queue : " + i;
channel.basicPublish("", QUEUE_NAME,null, msg.getBytes());
TimeUnit.MILLISECONDS.sleep(i*20);
}
//关闭通道和连接
channel.close();
connection.close();
}
}
4.2、消费者1和消费者2
package com.zxj.workqueuefair;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* work queue 工作队列 一个生产者对多个消费者
* 消费者1
* @author zxj
* @date 2020/6/2 17:28
*/
public class Recv1 {
private static final String QUEUE_NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取通道
final Channel channel = connection.createChannel();
//声明消息队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//消费者开启公平模式,一次只消费一条消息
channel.basicQos(1);
//定义消费者
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msgRecv1 = new String(body, "utf-8");
System.out.println("1 msgRecv1 : " + msgRecv1);
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// ack手动回执
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//监听消息队列
boolean autoAck = false; //true 自动应答, false手动应答 channel.basicAck(。。。)
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
package com.zxj.workqueuefair;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* work queue 工作队列 一个生产者对多个消费者
* 消费者2
* @author zxj
* @date 2020/6/2 17:28
*/
public class Recv2 {
private static final String QUEUE_NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取通道
final Channel channel = connection.createChannel();
//声明消息队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//消费者开启公平模式,一次只消费一条消息
channel.basicQos(1);
//定义消费者
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msgRecv2 = new String(body, "utf-8");
System.out.println("2 msgRecv2 : " + msgRecv2);
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// ack手动回执
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//监听消息队列
boolean autoAck = false; //true 自动应答, false手动应答 channel.basicAck(。。。)
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
5、消息应答与消息持久化
5.1、消息应答
boolean autoAck = true;
true:消息自动应答,当消息队列将消息分发给消费者,消息队列就会将内存中的这条消息删除,一旦正在处理消息的消费者宕机,会发生数据丢失。
false:消息手动应答,当消费者处理完消息后,返回ack给消息队列确认后,消息队列才会删除内存中的这条消息。
消息应答默认是打开的 false
5.2、消息持久化
//声明消息队列
boolean durable = false; //是否开启消息持久化,false不开启,true开启
channel.queueDeclare(QUEUE_NAME, durable, false, false, null);
在RabbitMQ中,消息队列一旦被定义,是不可以修改参数的,如上消息队列定义为不开启消息持久化,如果将参数durable改为true,运行会报错,除非删掉该消息队列或者重新创建一个其他名称的消息队列
6、订阅模式 -------- 一个消息被多个消费者消费
上图解读:
① 一个生产者,多个消费者
② 每个消费者都有自己的消息队列
③ 生产者将消息发送到Exchange交换机上,而不是之前直接发送到消息队列
④ 每个消息队列都要绑定到交换机上
⑤ 生产者将消息发送给交换机,经过消息队列,就可以实现一个消息被多个消费者消费
6.1、消息发布者
package com.zxj.pubsub;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 发布订阅模式 fanout 分发方式
* 发布者
* @author zxj
* @date 2020/6/3 10:12
*/
public class Publish {
private static final String EXCHANGE_NAME = "test_exchange_fanout";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机,交换机没有存储能力
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分发方式
//发送消息
String msg = "hello ps";
channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());
System.out.println("Pub msg : " + msg);
channel.close();
connection.close();
}
}
6.2、消息订阅者1和订阅者2
package com.zxj.pubsub;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 订阅者1
* @author zxj
* @date 2020/6/3 10:20
*/
public class Subscribe1 {
private static final String EXCHANGE_NAME = "test_exchange_fanout";
private static final String QUEUE_NAME = "test_exchange_fanout_email";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.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 {
System.out.println("1 Sub email : " + new String(body, "utf-8"));
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
package com.zxj.pubsub;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 订阅者2
* @author zxj
* @date 2020/6/3 10:20
*/
public class Subscribe2 {
private static final String EXCHANGE_NAME = "test_exchange_fanout";
private static final String QUEUE_NAME = "test_exchange_fanout_msg";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.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 {
System.out.println("2 Sub msg : " + new String(body, "utf-8"));
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
7、Exchange(交换机)
7.1 、作用:接收生产者发布的消息,将消息推送到与之绑定的消息队列
7.2、匿名转发和非匿名转发
7.3、类型
7.3.1、Fanout类型(不处理路由键,只是简单的将消息队列绑到交换机上),接收到消息后,会转发给所有与之绑定的消息队列,类似广播,是消息转发最快的方式
代码实现 见 《6、订阅模式 -------- 一个消息被多个消费者消费》
7.3.2、 Direct类型
发布者的routingKey与订阅者的routingKey完全匹配,交换机才会转发消息到对应的消息队列
代码实现:
生产者
package com.zxj.routing;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 路由模式 完全匹配路由键
* @author zxj
* @date 2020/6/3 10:48
*/
public class Send {
private static final String EXCHANGE_NAME = "test_exchange_direct";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "direct");//路由模式
String msg = "hello direct";
//路由键
// String routingKey = "error";
String routingKey = "info";
// String routingKey = "warning";
channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());
System.out.println("Send msg : " + msg);
channel.close();
connection.close();
}
}
消费者1和消费者2
package com.zxj.routing;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @author zxj
* @date 2020/6/3 10:51
*/
public class Recv1 {
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct_1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicQos(1);//公平
//绑定队列到交换机
String routingKey = "error";
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("1 Recv1 msg : " + new String(body, "utf-8"));
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//监听消息队列
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
package com.zxj.routing;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @author zxj
* @date 2020/6/3 10:51
*/
public class Recv2 {
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct_2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicQos(1);//公平
//绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "info");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "warning");
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("2 Recv2 msg : " + new String(body, "utf-8"));
try {
TimeUnit.MILLISECONDS.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//监听消息队列
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
7.3.3、Topic类型 主题模式
路由键匹配模式
# 一个或多个字符
* 一个字符
代码实现
生产者
package com.zxj.topic;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* Topic 主题匹配模式
* @author zxj
* @date 2020/6/3 11:25
*/
public class Send {
private static final String EXCHANGE_NAME = "test_exchange_topic";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "topic");//路由模式
String msg = "hello topic";
//路由键
// String routingKey = "goods.add"; // 消费者1,2都能收到
String routingKey = "goods.delete"; // 消费者1收不到,消费者2 能收到
channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());
System.out.println("Send msg : " + msg);
channel.close();
connection.close();
}
}
消费者1和消费者2
package com.zxj.topic;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 消费者1 只能接收 goods.add 路由键
* @author zxj
* @date 2020/6/3 11:26
*/
public class Recv1 {
private static final String EXCHANGE_NAME = "test_exchange_topic";
private static final String QUEUE_NAME = "test_queue_topic_1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicQos(1);//公平
//绑定队列到交换机
String routingKey = "goods.add";
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("1 Recv1 msg : " + new String(body, "utf-8"));
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//监听消息队列
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
package com.zxj.topic;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 消费者2 能接收 goods.# 路由键
* @author zxj
* @date 2020/6/3 11:26
*/
public class Recv2 {
private static final String EXCHANGE_NAME = "test_exchange_topic";
private static final String QUEUE_NAME = "test_queue_topic_2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicQos(1);//公平
//绑定队列到交换机
String routingKey = "goods.#";
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("2 Recv2 msg : " + new String(body, "utf-8"));
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//监听消息队列
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
8、RabbitMQ消息确认机制(事务 + confirm)
8.1 在rabbitmq中可以通过消息持久化,解决rabbitmq服务当即导致的数据丢失问题
8.2 rabbitmq 默认情况下不知道生产者发布的消息有没有到达rabbitmq服务器,处理方式两种:
AMQP 实现了事务机制
Confirm 模式 --------- 异步
8.3 事务机制 ------------- 降低了RabbitMQ的吞吐量
txSelect:用于将当前channel设置为开启事务模式
txCommit:用于事务提交
txRollback:用于回滚事务
代码实现
生产者
package com.zxj.transaction;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 事务机制 通过事务方式确认消息有没有到达,
*
* 缺点: 降低RabbitMQ的消息吞吐量
*
* @author zxj
* @date 2020/6/3 11:40
*/
public class TxSend {
private static final String QUEUE_NAME = "test_queue_tx";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false,false,false, null);
String msg = "hello tx";
try {
//channel 开启事务
channel.txSelect();
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
//模拟异常
int i = 1/0;
//提交事务
channel.txCommit();
} catch (Exception e){
//回滚事务
channel.txRollback();
System.out.println(" send msg txRollback !!!");
}
System.out.println("Send msg : " + msg);
channel.close();
connection.close();
}
}
消费者
package com.zxj.transaction;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author zxj
* @date 2020/6/3 11:46
*/
public class TxRecv {
private static final String QUEUE_NAME = "test_queue_tx";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
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("txRecv msg : " + new String(body, "utf-8"));
}
});
}
}
8.4、Confirm模式 --------- 异步
原理:
生产者将channel设置成confirm模式,所有在该channel上发布的消息都会被分配一个唯一ID(从1开始),一旦消息被分发到指定的队列后,RabbitMQ服务器就会发送一个确认消息(包含消息的唯一ID)给生产者,这就使得生产者知道消息已经正确到达消息队列。
如果开启了消息持久化,那么确认消息会在消息写入磁盘持久化成功后发送给生产者,确认消息中deliver-tag域包含了消息的序列号,此外,RabbitMQ服务器也可以设置basic.ack的multiple域,表示到这个序列号之前的所有消息都已经得到了处理。
模式:
普通 发一条消息 waitForConfirm()
批量 发一批消息 waitForConfirm()
异步 提供回调方法
代码实现:
普通 发一条消息
生产者
package com.zxj.confirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 普通模式 发送一条
* @author zxj
* @date 2020/6/3 13:18
*/
public class SendNormal {
private static final String QUEUE_NAME = "test_queue_confirm";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false,false,false, null);
//开启confirm普通模式
channel.confirmSelect();
String msg = "hello confirm";
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
if(!channel.waitForConfirms()){
System.out.println("msg send failed");
} else {
System.out.println("msg send success");
}
System.out.println("Send msg : " + msg);
channel.close();
connection.close();
}
}
消费者
package com.zxj.confirm;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author zxj
* @date 2020/6/3 13:22
*/
public class Recv {
private static final String QUEUE_NAME = "test_queue_confirm";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
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("confirm Recv msg : " + new String(body, "utf-8"));
}
});
}
}
批量 发送一批消息
生产者
package com.zxj.confirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 普通模式 发送一条
* @author zxj
* @date 2020/6/3 13:18
*/
public class SendBatch {
private static final String QUEUE_NAME = "test_queue_confirmBatch";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false,false,false, null);
//开启confirm普通模式
channel.confirmSelect();
String msg = "hello confirmBatch";
//批量发送
for (int i = 0; i < 10; i++) {
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
}
if(!channel.waitForConfirms()){
System.out.println("batch msg send failed");
} else {
System.out.println("batch msg send success");
}
System.out.println("Send msg : " + msg);
channel.close();
connection.close();
}
}
消费者同 普通 模式
异步模式
生产者
package com.zxj.confirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;
/**
* 异步模式
* 生产者维护一个SortedSet集合,每发送一条消息到Rabbtimq,同时王SortedSet集合中插入一个标记,
* 当收到确认ack时,在集合中移除对应消息的标记
* 当未收到ack消息时,根据业务做对应处理
* @author zxj
* @date 2020/6/3 13:18
*/
public class SendAsync {
private static final String QUEUE_NAME = "test_queue_confirmAsync";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false,false,false, null);
//开启confirm普通模式
channel.confirmSelect();
//创建集合用于存储消息标记
final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
//添加通道监听
channel.addConfirmListener(new ConfirmListener() {
//ok的ack
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
//移除集合中对应的标记
if(multiple){
System.out.println(" handleAck multiple ");
confirmSet.headSet(deliveryTag + 1).clear();
} else {
System.out.println(" handleAck un multiple ");
confirmSet.remove(deliveryTag);
}
}
// 失败处理
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
//根据业务进行处理失败的数据,此处也做移除集合中对应的标记
if(multiple){
System.out.println(" handleNack multiple ");
confirmSet.headSet(deliveryTag + 1).clear();
} else {
System.out.println(" handleNack un multiple ");
confirmSet.remove(deliveryTag);
}
}
});
String msg = "hello confirm";
while (true){
long seqNo = channel.getNextPublishSeqNo();
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
System.out.println("Send msg : " + msg);
confirmSet.add(seqNo);
}
}
}
消费者
package com.zxj.confirm;
import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author zxj
* @date 2020/6/3 13:22
*/
public class Recv {
// private static final String QUEUE_NAME = "test_queue_confirm";
// private static final String QUEUE_NAME = "test_queue_confirmBatch";
private static final String QUEUE_NAME = "test_queue_confirmAsync";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
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("confirm Recv msg : " + new String(body, "utf-8"));
}
});
}
}