RabbitMQ持久化编码注意事项
以Java语言,MQ客户端为amqp-client作为示例
1、基本原则
direct模式,由生产者声明队列名,消费者也声明队列名
topic模式,由生产者声明交换器名,由消费者声明队列名+交换器名+绑定关系
即生产者只负责生产消息,至于消息要投递到哪里由消费者指定
2、队列、交换器、消息的持久化配置
队列声明持久化
public void queueDeclare(String queue) {
try {
if (conn == null) {
conn = connectionFactory.newConnection();
}
Channel channel = conn.createChannel();
// 声明队列,如果队列不存在则创建之
boolean durable = true;
boolean exclusive = false;
boolean autoDelete = false;
Map<String, Object> arguments = null;
channel.queueDeclare(queue, durable, exclusive, autoDelete, arguments);
channel.close();
} catch (IOException e) {
logger.error("IOException:", e);
} catch (TimeoutException e) {
logger.error("TimeoutException:", e);
}
}
交换器声明持久化
// 声明topic交换器
public void topicExchangeDeclare(String exchange) {
String type = "topic";
boolean durable = true;
exchangeDeclare(exchange, type, durable);
} private void exchangeDeclare(String exchange, String type, boolean durable) {
try {
if (conn == null) {
conn = connectionFactory.newConnection();
}
Channel channel = conn.createChannel(); // 声明交换器
channel.exchangeDeclare(exchange, type, durable); channel.close();
} catch (IOException e) {
logger.error("IOException:", e);
} catch (TimeoutException e) {
logger.error("TimeoutException:", e);
}
}
消息发送时指定持久化
// 发送消息
public void send(String exchange, String routingKey, JSONObject json) {
try {
if (conn == null) {
conn = connectionFactory.newConnection();
}
Channel channel = conn.createChannel(); String msg = json.toJSONString();
channel.basicPublish(exchange, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes("utf-8"));
channel.close();
} catch (IOException e) {
logger.error("IOException:", e);
} catch (TimeoutException e) {
logger.error("TimeoutException:", e);
}
}
3、网络闪断、RabbitMQ重启时App的自恢复编码
首先,必须已经指定了队列和交换器的持久化,否则在自恢复时,由于无法找到队列及交换器和绑定关系会报错
需要注意的是,RabbitMQ推荐尽量共用Connection,多个线程之间用不同的Channel
<bean id="connectionFactory" class="com.rabbitmq.client.ConnectionFactory">
<property name="automaticRecoveryEnabled" value="true"></property>
<property name="host" value="${RABBITMQ.SERVER_IP}"></property>
<property name="port" value="${RABBITMQ.SERVER_PORT}"></property>
<property name="username" value="${RABBITMQ.USERNAME}"></property>
<property name="password" value="${RABBITMQ.PASSWORD}"></property>
<property name="virtualHost" value="${RABBITMQ.VIRTUAL_HOST}"></property>
</bean>
设置automaticRecoveryEnabled为true
public class MQConsumer implements Runnable, Consumer {
static Logger logger = LoggerFactory.getLogger(MQConsumer.class);
protected Connection connection;
protected Channel channel;
protected String queue;
protected ConsumerExecutor executor;// 执行器
private MQConfig config;
public MQConsumer(MQConfig config, String queue, ConsumerExecutor executor) {
this.config = config;
this.queue = queue;
this.executor = executor;
}
@Override
public void run() {
try {
init();
try {
channel.basicConsume(queue, true, this);
} catch (IOException e) {
logger.error("MQ消费处理失败:", e);
}
} catch (Exception e) {
logger.error("mq init() error", e);
}
}
protected void init() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(config.getIp());
factory.setPort(config.getPort());
factory.setUsername(config.getUserName());
factory.setPassword(config.getPassword());
factory.setVirtualHost(config.getvHost());
factory.setAutomaticRecoveryEnabled(true);
connection = factory.newConnection();
channel = connection.createChannel();
}
@Override
public void handleDelivery(String consumerTag, Envelope env, BasicProperties props, byte[] body) throws IOException {
String msg = new String(body, "utf-8");
logger.debug("从队列[" + queue + "] 接收消息: " + msg);
try {
executor.consume(msg);
} catch (Exception e) {
logger.error("handleDelivery error:", e);
}
}
@Override
public void handleCancel(String consumerTag) {
logger.info("handleCancel:" + consumerTag);
}
@Override
public void handleCancelOk(String consumerTag) {
logger.info("handleCancelOk:" + consumerTag);
}
@Override
public void handleConsumeOk(String consumerTag) {
logger.info("handleConsumeOk:" + consumerTag);
}
@Override
public void handleRecoverOk(String consumerTag) {
logger.info("handleRecoverOk:" + consumerTag);
}
@Override
public void handleShutdownSignal(String consumerTag, ShutdownSignalException e) {
logger.info("handleShutdownSignal:" + consumerTag);
}
}
消费者代码示例,只要automaticRecoveryEnabled为true,而且queue和exchange都是持久化的,能够自动恢复,不用手工处理。
4、auto_ack问题
在auto_ack为true时,数据流是这样的:
App从MQ取消息->删除消息->App业务逻辑处理(包括读写数据库等)->发送处理结果(如果有需要)
可以看出当App业务逻辑处理失败时,消息已经被删除了,很多情况下,这是不安全的,所以改为:
App从MQ取消息->App业务逻辑处理(包括读写数据库等)->发送ACK删除消息 ->发送处理结果(如果有需要)
但是由于性能问题一般出现在业务逻辑部分,如果这部分处理慢又会造成拥塞,所以要自已权衡
try {
channel.basicConsume(queue, true, this);
boolean autoAck = false;
channel.basicConsume(queue, autoAck, this);
} catch (IOException e) {
logger.error("MQ消费处理失败:", e);
}
try{
channel.basicAck(env.getDeliveryTag(), true);
}catch(Exception e){
logger.error("basicAck error:", e);
}
5、超时处理
采用MQ解耦后系统之间虽然是异步处理,但正常情况下响应速度跟同步处理接近。特殊情况下响应慢时很可能消息从发送到被处理已经过去了很长一段时间,前端极可能已经重复提交并完成了业务,所以需要加个快速失败机制。即消息生产者将消息的创建时间带到消息体里,消费者拿到消息后,判断如果是已经过去了指定间隔的消息,则直接失败返回。
RabbitMQ持久化编码注意事项的更多相关文章
- 消息中间件-RabbitMQ持久化机制、内存磁盘控制
RabbitMQ持久化机制 RabbitMQ内存控制 RabbitMQ磁盘控制 RabbitMQ持久化机制 重启之后没有持久化的消息会丢失 package com.study.rabbitmq.a13 ...
- RabbitMQ持久化
我们知道,如果消息接收端挂了,消息会保存在队列里.下次接收端启动就会接收到消息. 如果RabbitMQ挂了怎么办呢?这时候需要将消息持久化到硬盘 消息发送端:producer ........... ...
- RabbitMQ学习笔记(6)----RabbitMQ 持久化和非持久化
持久化:将交换机或队列数据保存到磁盘,服务器宕机或重启之后依然存在. 非持久化:将交换机或队列的数据保存到内存中,服务器宕机或重启之后数据将不存在. 在RabbitMQ中也提供了持久化和非持久化方式. ...
- rabbitmq 持久化 事务 发送确认模式
部分内容来自:http://blog.csdn.net/hzw19920329/article/details/54315940 http://blog.csdn.net/hzw19920329/ar ...
- RabbitMQ持久化机制、内存磁盘控制(四)
一.持久化 如果看到这一篇文章的朋友,都是有经验的开发人员,对持久化的概念就不用再做过多的解析了,经过前面的几篇文章,其实不难发现RabbitMQ 的持久化其实就只分交换器持久化.队列持久化和消息持久 ...
- 消费RabbitMQ时的注意事项,如何禁止大量的消息涌到Consumer
按照官网提供的订阅型写法( Retrieving Messages By Subscription ("push API")) 我发现,RabbitMQ服务器会在短时间内发送大量的 ...
- PHP 下载导出中文名的文件的编码注意事项
我的页面全部都为utf-8 在代码中我的文件名是中文名. 在创建文件时,就要将utf-8转码成gbk(用以支持中文) $file = iconv('utf-8',"gbk",$fi ...
- web.xml文件--编码注意事项
写在前面: 最近发布项目的时候,要修改web.xml文件的内容,然后我在本机的web.xml文件中是有注释的,但是到了服务器上面,就说编码不同.我也没有怎么注意.就继续启动服务器,但是访问网站,一直报 ...
- python rabbitMQ持久化队列消息
import pika connection = pika.BlockingConnection( pika.ConnectionParameters('localhost'))#建立一个最基本的so ...
随机推荐
- 学习OpenCV,看这些!
OpenCV简介: OpenCV 是一款功能强大的跨平台计算机视觉开源库,可以用于解决人机交互.物体检测.人脸识别等领域的问题.库本身是采用 C++ 编写的,但是同时也对 Python, Java, ...
- JavaScript进阶(四)
现在说说什么是函数.函数的作用可以写一次代码,然后反复的重用这个代码.如:我们要完成多组数和的功能.var sum;sum=3+2;alert(sum); sum=7+8;alert(sum);... ...
- http缓存与cdn相关技术
阅读目录 一 http缓存 二.Http缓存概念解析 三.cdn相关技术 摘要:最近要做这个主题的组内分享,所以准备了一个星期,查了比较多的资料.准备的过程虽然很烦很耗时间,不过因为需要查很多的资料, ...
- CoreJavaE10V1P3.1 第3章 Java的基本编程结构-3.1 Java 最简程序
3.1Java最简程序 FirstSample.java public class FirstSample { public static void main(String[] args) { Sys ...
- 一个App从创意到最终上架到App Store里的整个过程是怎样的?
一个App从创意到最终上架到App Store里的整个过程是怎样的? 制作App需要什么软件?应该看什么书?需要哪些设备?推到App Store里,需要注册什么网站?是否需要付费?需要什么证书之类的? ...
- [MFC美化] SkinMagic使用详解2- SkinMagic常用函数介绍
SkinMagic常用函数介绍 (1)InitSkinMagicLib函数:初始化SkinMagic int InitSkinMagicLib( //初始化SkinMagic工具库 HINSTANCE ...
- PHP在浏览器上跟踪调试的方法以及使用ChromePhp、FirePHP的简单介绍
之前用ThinkPHP时发现有个 trace 函数可以跟踪调试,感觉很有意思,网上搜索了下类似的东西,发现了 ChromePhp ,以前没想过这样来调试 PHP 程序,感觉非常方便,很有用. Thin ...
- Mac OS启动服务优化高级篇(launchd tuning)
Mac下的启动服务主要有三个地方可配置:1,系统偏好设置->帐户->登陆项2,/System/Library/StartupItems 和 /Library/StartupItems/3, ...
- 凭借5G研究优势,诺基亚将携手菲律宾将其应用于VR/AR领域
目前,很多人都在抱怨网速不行,影响视频的流畅播放,未来这些问题可以通过5G解决.近日,诺基亚和PLDT的全资子公司Smart首次在菲律宾一个"现场"网络演示上实现了5G速度,该网络 ...
- string 数字序列大小比较
string 数字序列大小比较 string.compare string a = "022"; string b="1"; 比较结果 '022' < ' ...