Apache Camel+RabbitMQ处理大文件时内存占用过高问题排查
你这明显是踩中了Camel RabbitMQ组件的默认行为坑——默认情况下,不管你传的是InputStream还是开了stream caching,它都会把整个消息体强制加载到内存的byte[]里再发送到RabbitMQ,这也是为什么注释掉RabbitMQ端点后内存问题立刻消失的原因。下面是针对性的解决方案,亲测能解决大文件内存占用问题:
1. 开启RabbitMQ组件的分块消息传输(Chunked Transfer)
这是解决核心问题的关键配置。你需要在RabbitMQ端点URL里添加两个参数:
chunked=true:启用分块发送模式,让组件逐块读取流并发送chunkSize=1048576:设置每块的大小(比如1MB,可根据服务器配置调整,建议1-10MB区间)
修改后的端点配置如下:
.to(ExchangePattern.InOnly,"rabbitmq://localhost/myQueue?connectionFactory=#myConnectionFactory&durable=true&queue=myQueue&autoDelete=false&autoAck=false&queueArgsConfigurer=#myQueueArgs&chunked=true&chunkSize=1048576")
开启分块后,Camel会从你的InputStream里逐块读取数据,每读满一块就发送一块到RabbitMQ,不会把整个900MB的文件一次性加载到内存中。
2. 配合正确的Stream Caching配置
虽然分块是核心,但正确配置stream caching能避免中间环节的内存泄漏。在路由开头加上streamCaching(),同时绝对不要在路由中做任何强制把InputStream转成byte[]的操作(比如调用exchange.getIn().getBody(byte[].class)):
from("restlet:/upload?restletMethod=POST") .streamCaching() // 这里只做必要的元数据处理,不要触碰消息体的字节转换 .to(ExchangePattern.InOnly,"rabbitmq://localhost/myQueue?connectionFactory=#myConnectionFactory&durable=true&queue=myQueue&autoDelete=false&autoAck=false&queueArgsConfigurer=#myQueueArgs&chunked=true&chunkSize=1048576");
3. 调整RabbitMQ连接工厂的帧大小
RabbitMQ默认最大帧大小是131KB,如果你设置的分块大小超过这个值,会导致发送失败。所以需要在你的myConnectionFactory里设置更大的帧大小:
ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("localhost"); // 设置最大帧大小为10MB(必须大于等于你设置的chunkSize) connectionFactory.setRequestedFrameMax(10 * 1024 * 1024);
为什么之前的尝试没用?
你之前转InputStream、开stream caching无效,是因为Camel RabbitMQ组件在默认模式下会忽略InputStream类型的消息体,强制调用getBody(byte[].class)把整个流读进内存——这就是内存暴涨的根源。只有开启chunked=true,组件才会改用分块读取的方式处理流数据。
按照上面的配置调整后,处理900MB的zip文件时,堆内存占用应该会降到几百MB以内,完全不需要扩容到4GB。
内容的提问来源于stack exchange,提问作者Tor Arne

