HTTP-сервер Netty, загрузка больших файлов, OutOfMemoryError

Прежде всего извините за мой кривой английский :)

У меня возникли проблемы при написании http-сервера Netty, который отправляет/принимает большие файлы (~2Gb). И я был бы очень признателен за любую помощь или объяснение.

Мое клиентское приложение (веб-браузер) отправляет файлы через 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.code c.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)?

Благодарю вас! С наилучшими пожеланиями.


person hazardhz    schedule 27.12.2011    source источник


Ответы (3)



Да, лучшим способом было бы «протолкнуть» фрагменты byte[] в fs, пока загрузка не будет завершена. Так что вам не нужно держать все в памяти. Имейте в виду, что запись в fs может «блокироваться», поэтому вам следует подумать о добавлении перед ней ExecutionHandler.

person Norman Maurer    schedule 28.12.2011

Тот факт, что он находится в чем-то под названием HeapChannelBuffer, говорит о многом — вы, вероятно, хотите, чтобы org.jboss.netty.buffer.DirectChannelBufferFactory где-то там был, чтобы ваши данные не хранились в куче.

person Tim Boudreau    schedule 28.01.2012