RabbitMQ一般情况很少丢失,但是不能排除意外,为了保证我们自己系统高可用,我们必须作出更好完善措施,保证系统的稳定性。
- 消息持久化
- ACK确认机制
- 设置集群镜像模式
- 消息补偿机制 消息持久化
消息持久化
RabbitMQ 的消息默认存放在内存上面,如果不特别声明设置,消息不会持久化保存到硬盘上面的,如果节点重启或者意外crash掉,消息就会丢失。所以就要对消息进行持久化处理。如何持久化,下面具体说明下:
要想做到消息持久化,必须满足以下三个条件,缺一不可。
1) Exchange 设置持久化
2)Queue 设置持久化
3)Message持久化发送:发送消息设置发送模式deliveryMode=2,代表持久化消息
代码如下:
工具类代码:
public class ConnUtil {
public static Connection getConnection() throws IOException, TimeoutException {
//创建一个与MQ的连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.1.118");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
//创建一个连接
return factory.newConnection();
}
public static void close(Channel channel) {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
public static void close(Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
发送消息:
public class Producer03_routing {
//队列名称
private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
private static final String EXCHANGE_ROUTING_INFORM = "exchange_routing_inform";
public static void main(String[] args) {
Connection connection = null;
Channel channel = null;
try {
connection = ConnUtil.getConnection();
channel = connection.createChannel();
//声明exchange并持久化设置
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT, true);
//声明queue并持久化设置
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_EMAIL);
channel.queueBind(QUEUE_INFORM_SMS, EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_SMS);
for (int i = 0; i < 10; i++) {
String messageStr = "email inform to user" + i;
//设置消息持久化
channel.basicPublish(EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_EMAIL, MessageProperties.PERSISTENT_TEXT_PLAIN ,messageStr.getBytes());
System.out.println("Send Message is:'" + messageStr + "'");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
ConnUtil.close(channel);
ConnUtil.close(connection);
}
}
}
当发送消息成功后,将rabbitmq重启,查询消息是否还存在。
ACK确认机制
生产者确认:
public class Producer03_routing01 {
//队列名称
private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
private static final String EXCHANGE_ROUTING_INFORM = "exchange_routing_inform";
public static void main(String[] args) {
Connection connection = null;
Channel channel = null;
try {
connection = ConnUtil.getConnection();
channel = connection.createChannel();
//声明exchange并持久化设置
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT, true);
//声明queue并持久化设置
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_EMAIL);
channel.queueBind(QUEUE_INFORM_SMS, EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_SMS);
// 将channel设置成 confirm 模式
channel.confirmSelect();
for (int i = 0; i < 10; i++) {
String messageStr = "email inform to user" + i;
//设置消息持久化
channel.basicPublish(EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_EMAIL, MessageProperties.PERSISTENT_BASIC ,messageStr.getBytes());
if(channel.waitForConfirms()){
System.out.println("成功");
}else{
System.out.println("失败");
}
System.out.println("Send Message is:'" + messageStr + "'");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
ConnUtil.close(channel);
ConnUtil.close(connection);
}
}
}
消息者确认
public class Consumer03_routing_email {
//队列名称
private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
private static final String EXCHANGE_ROUTING_INFORM = "exchange_routing_inform";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnUtil.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_ROUTING_INFORM, QUEUE_INFORM_EMAIL);
//定义消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
long deliveryTag = envelope.getDeliveryTag();
String exchange = envelope.getExchange();
String message = new String(body, "utf-8");
System.out.println(message);
//false 重新放入队列 true直接丢弃
channel.basicReject(envelope.getDeliveryTag(), true);
}
};
//设置手动确认
channel.basicConsume(QUEUE_INFORM_EMAIL, false, defaultConsumer);
}
}