Netty HTTP服务器,大文件上传,OutOfMemoryError

时间:2011-12-27 10:06:31

标签: out-of-memory netty httpserver large-file-upload

首先抱歉我的歪英语:)

在编写发送/接收大文件(~2Gb)的Netty http服务器时遇到一些问题。我非常感谢任何人的帮助或解释。

我的客户端应用程序(Web浏览器)通过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);

服务器端是:

类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));

类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堆空间     在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范围内)?

谢谢! 最好的问候。

3 个答案:

答案 0 :(得分:3)

我认为你应该使用HttpChunkAggregator

这意味着您必须手动处理HTTP块。

有关详细信息,请参阅Netty file upload example

答案 1 :(得分:1)

是的,更好的方法是将字节[]块“推”到fs,直到上传完成。因此,您不需要将所有内容保存在内存中。请注意,写入fs可能会“阻止”,因此您应该考虑在其前面添加一个ExecutionHandler。

答案 2 :(得分:0)

事实上,它在一个名为HeapChannelBuffer的东西中说了很多 - 你可能想在那里的某个地方org.jboss.netty.buffer.DirectChannelBufferFactory,这样你的数据就不会存储在堆上。