- pom.xml
引入rabbitmq所需jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>fun.gosuncn</groupId>
<artifactId>rabbitmq</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.10.0</version>
</dependency>
</dependencies>
</project>
- RabbitmqUtils
设置连接信息,获取连接工厂。
import com.rabbitmq.client.ConnectionFactory;
public class RabbitmqUtils {
public static ConnectionFactory getConnectionFactory() {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("10.0.8.6");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");
return factory;
}
}
- Receiver
作为消费者不断的监听队列,消费消息。
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Receiver {
public static void main(String[] args) {
ConnectionFactory factory = RabbitmqUtils.getConnectionFactory();
try {
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("direct-queue", false, false, false, null);
channel.basicConsume("direct-queue", true, (consumerTag, message) -> {
System.out.println("DeliverCallback's consumerTag is {" + consumerTag + "}");
System.out.println("消费消息的内容 = " + new String(message.getBody()));
}, consumerTag -> System.out.println("CancelCallback's consumerTag is {" + consumerTag + "}"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 第一种方式 - 事务方式
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Sender {
public static void main(String[] args) {
ConnectionFactory factory = RabbitmqUtils.getConnectionFactory();
Connection connection = null;
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare("direct-queue", false, false, false, null);
channel.txSelect();
channel.basicPublish("", "direct-queue", null, "hello world".getBytes());
//int i = 1 / 0;
channel.txCommit();
} catch (Exception e) {
if (channel != null) {
try {
channel.txRollback();
// TODO 事务回滚 逻辑 [再次发送、存库操作等]
System.out.println("事务回滚 逻辑 [再次发送、存库操作等]");
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
e.printStackTrace();
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
开启事务channel.txSelect();
。
执行channel.basicPublish("", "direct-queue", null, "hello world".getBytes());
消息的投递。
使用int i = 1 / 0;
模拟上一步消息的投递过程发生了IO异常,此时将不会执行channel.txCommit();
操作。
而是进入channel.txRollback();
代码块,进行事务回滚逻辑操作。
假如,没有发生任何异常,将会执行channel.txCommit();
操作。
以上过程是如何确认消息的投递成功的呢?
首先,客户端开启了事务,客户端将消息投递给服务器,假如投递过程中因网络等原因发生了IOException
,那么将会进入channel.txRollback();
逻辑;假如投递到服务器了,后续的逻辑发生异常,进入channel.txRollback();
依然是属于投递失败,消费者是消费不到消息的。
因此,事务的方式可以处理消息发送失败后的解决方案,以确保对消息的完全掌控。
- 第二种方式 - 同步confirm方式
package fun.gosuncn.test1;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Sender {
public static void main(String[] args) {
ConnectionFactory factory = RabbitmqUtils.getConnectionFactory();
Connection connection = null;
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare("direct-queue", false, false, false, null);
channel.confirmSelect();
channel.basicPublish("", "direct-queue", null, "hello world".getBytes());
//int i = 1 / 0;
if (channel.waitForConfirms()) {
System.out.println("消息已经确认投递成功");
Thread.sleep(3000L);
}
System.out.println("验证waitForConfirms是阻塞方法");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
通过channel.confirmSelect();
开启confirm模式。
而channel.waitForConfirms()
的作用是,阻塞程序,等待确认结果的返回,进行逻辑操作。
假如channel.basicPublish
与channel.waitForConfirms()
之间出现了任何异常错误,消息是有可能投递成功的。
- 第三种方式 - 同步confirm方式(批量)
package fun.gosuncn.test1;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Sender {
public static void main(String[] args) {
ConnectionFactory factory = RabbitmqUtils.getConnectionFactory();
Connection connection = null;
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare("direct-queue", false, false, false, null);
channel.confirmSelect();
channel.basicPublish("", "direct-queue", null, "hello world1".getBytes());
channel.basicPublish("", "direct-queue", null, "hello world2".getBytes());
channel.basicPublish("", "direct-queue", null, "hello world3".getBytes());
if (channel.waitForConfirms()) {
System.out.println("消息已经确认投递成功");
Thread.sleep(3000L);
}
System.out.println("验证waitForConfirms是阻塞方法");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
其实,批量确认和串行确认的区别就是,批量确认是执行多个basicPublish
后,执行一次waitForConfirms
即可。
- 第四种方式 - 异步confirm方式
package fun.gosuncn.test1;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
public class Sender {
public static void main(String[] args) {
ConnectionFactory factory = RabbitmqUtils.getConnectionFactory();
Connection connection = null;
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare("direct-queue", false, false, false, null);
channel.confirmSelect();
channel.addConfirmListener(new ConfirmListener() {
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("ack:deliveryTag:" + deliveryTag + ",multiple:" + multiple);
}
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("nack:deliveryTag:" + deliveryTag + ",multiple:" + multiple);
}
});
channel.basicPublish("", "direct-queue", null, "hello world1".getBytes());
channel.basicPublish("", "direct-queue", null, "hello world2".getBytes());
channel.basicPublish("", "direct-queue", null, "hello world3".getBytes());
} catch (Exception e) {
e.printStackTrace();
}
// finally {
// if (channel != null) {
// try {
// channel.close();
// } catch (IOException | TimeoutException e) {
// e.printStackTrace();
// }
// }
// if (connection != null) {
// try {
// connection.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// }
}
}
使用异步确认的方式需要注意的是:一定不能关闭连接,否则可能会出现异常。
事务方式:42891毫秒
串行确认:40551毫秒
批量确认:688毫秒
异步确认:318毫秒
综上结果,可以看出:事务方式效率最慢,异步确认方式效率最佳
。