Netty HTTP-服务器,大文件上传,OutOfMemoryError
首先,我对我歪曲的英语感到抱歉:)
我在编写发送/接收大文件(~2Gb)的 Netty http 服务器时遇到了一些问题。我非常感谢任何人的帮助或解释。
我的客户端应用程序(网络浏览器)通过 XMLHttpRequest 发送文件,如下所示:
var sampleFile = document.getElementById("sampleFile").files[0]; //chosen file by <input type="file" .../>
var xhr = new XMLHttpRequest();
xhr.open("POST","http://127.0.0.1:8091/upload/some_file_name.txt", true);
xhr.send(sampleFile);
服务器端是:
class WebSocketServer:
ServerBootstrap bootstrapHttp = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
bootstrapHttp.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
ChannelPipeline pipeline = pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("aggregator", new HttpChunkAggregator(1024*1024*1024));
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("deflater", new HttpContentCompressor());
pipeline.addLast("handler", new HttpRequestServerHandler());
return pipeline;
}
});
bootstrapHttp.bind(new InetSocketAddress(port));
class HttpRequestServerHandler:
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Object msg = e.getMessage();
if (msg instanceof HttpRequest) {
HttpRequest req = (HttpRequest)msg;
if (req.getMethod() != POST) {
return;
}
if (decodedURI.startsWith(UPLOAD_FILE_PATH)) {
HttpRequest request = (HttpRequest) e.getMessage();
RandomAccessFile raf = new RandomAccessFile("foobar.tmp", "rw");
ChannelBuffer buf = request.getContent();
FileChannel fChannel = raf.getChannel();
Channel msgChannel= e.getChannel();
fChannel.write( buf.toByteBuffer() );
raf.close();
fChannel.close();
msgChannel.close();
}
}
当我发送中等大小的文件时,一切都运行良好。问题在于大文件(>300Mb)。一段时间后发生处理异常:
java.lang.OutOfMemoryError: Java heap space 在 org.jboss.netty.buffer.HeapChannelBuffer.(HeapChannelBuffer.java:47) 在 org.jboss.netty.buffer.BigEndianHeapChannelBuffer.(BigEndianHeapChannelBuffer.java:39) 在 org.jboss.netty.buffer.ChannelBuffers.buffer(ChannelBuffers.java:139) 在 org.jboss.netty.buffer.HeapChannelBufferFactory.getBuffer(HeapChannelBufferFactory.java:73) 在 org.jboss.netty.buffer.DynamicChannelBuffer.ensureWritableBytes(DynamicChannelBuffer.java:84) 在 org.jboss.netty.buffer.DynamicChannelBuffer.writeBytes(DynamicChannelBuffer.java:239) 在 org.jboss.netty.buffer.AbstractChannelBuffer.writeBytes(AbstractChannelBuffer.java:457) 在 org.jboss.netty.buffer.AbstractChannelBuffer.writeBytes(AbstractChannelBuffer.java:450) 在 org.jboss.netty.handler.codec.http.HttpChunkAggregator.messageReceived(HttpChunkAggregator.java:140) 在 org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:302) 在 org.jboss.netty.handler.codec.replay.ReplayingDecoder.unfoldAndFireMessageReceived(ReplayingDecoder.java:522) 在 org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:506) 在 org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:443) 在 org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274) 在 org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261) 在 org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:351)
它甚至没有到达我的 messageReceived 处理程序。我可以假设一些内部 ChannelBuffer 溢出了。我尝试增加 HttpChunkAggregator(1024*1024*1024) 参数。然而,这并没有帮助。 我只看到一种解决方案 - 在客户端拆分文件(使用 html5),发送这些块,然后将它们粘贴到服务器上。 但它似乎相当复杂。有没有更简单的方法来修复它(在 Netty 范围内)?
谢谢你! 此致。
First of all sorry for my crooked English :)
I have some problems during writing Netty http server that sends/receives large files (~2Gb). And I would very appreciate anybody help or explanation.
My client application (web-browser) sends files via XMLHttpRequest like this:
var sampleFile = document.getElementById("sampleFile").files[0]; //chosen file by <input type="file" .../>
var xhr = new XMLHttpRequest();
xhr.open("POST","http://127.0.0.1:8091/upload/some_file_name.txt", true);
xhr.send(sampleFile);
Server side is:
class WebSocketServer:
ServerBootstrap bootstrapHttp = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
bootstrapHttp.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
ChannelPipeline pipeline = pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("aggregator", new HttpChunkAggregator(1024*1024*1024));
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("deflater", new HttpContentCompressor());
pipeline.addLast("handler", new HttpRequestServerHandler());
return pipeline;
}
});
bootstrapHttp.bind(new InetSocketAddress(port));
class HttpRequestServerHandler:
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Object msg = e.getMessage();
if (msg instanceof HttpRequest) {
HttpRequest req = (HttpRequest)msg;
if (req.getMethod() != POST) {
return;
}
if (decodedURI.startsWith(UPLOAD_FILE_PATH)) {
HttpRequest request = (HttpRequest) e.getMessage();
RandomAccessFile raf = new RandomAccessFile("foobar.tmp", "rw");
ChannelBuffer buf = request.getContent();
FileChannel fChannel = raf.getChannel();
Channel msgChannel= e.getChannel();
fChannel.write( buf.toByteBuffer() );
raf.close();
fChannel.close();
msgChannel.close();
}
}
When I send middle size file everything works wonderfully. The problem is with large files (>300Mb). After some time processing exception occures:
java.lang.OutOfMemoryError: Java heap space
at org.jboss.netty.buffer.HeapChannelBuffer.(HeapChannelBuffer.java:47)
at org.jboss.netty.buffer.BigEndianHeapChannelBuffer.(BigEndianHeapChannelBuffer.java:39)
at org.jboss.netty.buffer.ChannelBuffers.buffer(ChannelBuffers.java:139)
at org.jboss.netty.buffer.HeapChannelBufferFactory.getBuffer(HeapChannelBufferFactory.java:73)
at org.jboss.netty.buffer.DynamicChannelBuffer.ensureWritableBytes(DynamicChannelBuffer.java:84)
at org.jboss.netty.buffer.DynamicChannelBuffer.writeBytes(DynamicChannelBuffer.java:239)
at org.jboss.netty.buffer.AbstractChannelBuffer.writeBytes(AbstractChannelBuffer.java:457)
at org.jboss.netty.buffer.AbstractChannelBuffer.writeBytes(AbstractChannelBuffer.java:450)
at org.jboss.netty.handler.codec.http.HttpChunkAggregator.messageReceived(HttpChunkAggregator.java:140)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:302)
at org.jboss.netty.handler.codec.replay.ReplayingDecoder.unfoldAndFireMessageReceived(ReplayingDecoder.java:522)
at org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:506)
at org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:443)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261)
at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:351)
It even don't reach my messageReceived handler. As I can assume some inner ChannelBuffer is overflowed. I've tried to increase HttpChunkAggregator(1024*1024*1024) parameter. However, It would not helped.
I see just one solution - to split file on the client side (using html5), send these chunks, and paste them together on server.
But its seems to quite complex. Is there any easier way to fix it (in Netty scope)?
Thank you!
Best regards.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为你不应该不使用
HttpChunkAggregator
。这意味着您必须手动处理 HTTP 块。
参见 Netty 文件上传示例 了解更多详情。
I think you should not use
HttpChunkAggregator
.This means that you will have to manually handle HTTP chunks.
See Netty file upload example for more details.
是的,更好的方法是将 byte[] 块“推送”到文件系统,直到上传完成。所以你不需要将所有内容都保存在内存中。请注意,对 fs 的写入可能会“阻塞”,因此您应该考虑在其前面添加一个 ExecutionHandler。
Yeah a bette way would be to "push" byte[] chunks to the fs until the upload is complete. So you don't need to hold everything in the memory. Be aware that the writing to the fs may "block", so you should think about adding an ExecutionHandler in front of it.
事实上,它位于名为
HeapChannelBuffer
的东西中,这一点说明了很多 - 您可能希望将org.jboss.netty.buffer.DirectChannelBufferFactory
放在其中,这样您的数据就不会被存储在堆上。The fact that it's in something called
HeapChannelBuffer
says a lot - you probably wantorg.jboss.netty.buffer.DirectChannelBufferFactory
somewhere in there, so that your data isn't stored on the heap.