`

Netty源码学习-FileRegion

阅读更多

今天看org.jboss.netty.example.http.file.HttpStaticFileServerHandler.java
可以直接往channel里面写入一个FileRegion对象,而不需要相应的encoder:

		//pipeline(没有诸如“FileRegionEncoder”的handler):
		public ChannelPipeline getPipeline() throws Exception {
			ChannelPipeline pipeline = pipeline();
			pipeline.addLast("decoder", new HttpRequestDecoder());
			pipeline.addLast("aggregator", new HttpChunkAggregator(65536));
			pipeline.addLast("encoder", new HttpResponseEncoder());
			pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());

			pipeline.addLast("handler", new HttpStaticFileServerHandler());
			return pipeline;
		}
		
		public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler {
			public void messageReceived...{
				RandomAccessFile raf = new RandomAccessFile(file, "r");
				long fileLength = raf.length();

				HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
				setContentLength(response, fileLength);
				setContentTypeHeader(response, file);
				setDateAndCacheHeaders(response, file);

				Channel ch = e.getChannel();

				// Write the initial line and the header.
				ch.write(response);

				// Write the content.
				// No encryption - use zero-copy.
				final FileRegion region =
						new DefaultFileRegion(raf.getChannel(), 0, fileLength);
				
				//直接写入FileRegion
				ch.write(region);
			}
		}
		


这是为什么?往channel里面写的数据最后不是都要转成ChannelBuffer吗?

我们一步步的分析:
ch.write(region)会触发downstream事件(把region“装入”MessageEvent),
会一路经过各个handler,最后去到“sink”:

	//我们以NioServerSocketPipelineSink为例:
	private static void handleAcceptedSocket(ChannelEvent e) {
		  if (e instanceof MessageEvent) {
				MessageEvent event = (MessageEvent) e;
				NioSocketChannel channel = (NioSocketChannel) event.getChannel();
				boolean offered = channel.writeBufferQueue.offer(event);
				assert offered;
				channel.worker.writeFromUserCode(channel);
			}
    }

	//最终的写操作在AbstractNioWorker(只保留关键代码):
    protected void write0(AbstractNioChannel<?> channel) {
	        final WritableByteChannel ch = channel.channel;
	        final Queue<MessageEvent> writeBuffer = channel.writeBufferQueue;
			channel.currentWriteEvent = evt = writeBuffer.poll();
			
			/*关键在这里:把FileRegion封装成一个SendBuffer,
			SendBuffer的transferTo调用的是FileRegion的transferTo方法,
			而这个方法调用的是FileChannel的transferTo方法:
			This method is potentially much more efficient than a simple loop that reads from this channel and writes to the target channel. Many operating systems can transfer bytes directly from the filesystem cache to the target channel without actually copying them.
			大体意思就是“Java NIO Channel to Channel Transfers”不需要内存复制,速度更快
			*/
	        channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage());
			buf.transferTo(ch);
	}

	//这里证明了,往channel写入的数据,类型既可以是ChannelBuffer,也可以是FileRegion
	SendBuffer acquire(Object message) {
        if (message instanceof ChannelBuffer) {
            return acquire((ChannelBuffer) message);
        } else if (message instanceof FileRegion) {
            return acquire((FileRegion) message);
        }

        throw new IllegalArgumentException(
                "unsupported message type: " + message.getClass());
    }

	private SendBuffer acquire(FileRegion src) {
        if (src.getCount() == 0) {
            return EMPTY_BUFFER;
        }
        return new FileSendBuffer(src);
    }
	
	class FileSendBuffer {	
		private final FileRegion file;
		public long transferTo(WritableByteChannel ch) throws IOException {
				long localWrittenBytes = file.transferTo(ch, writtenBytes);
				writtenBytes += localWrittenBytes;
				return localWrittenBytes;
			}
	}

	class DefaultFileRegion...{
		 private final FileChannel file;
		 public long transferTo(WritableByteChannel target, long position) throws IOException {
			return file.transferTo(this.position + position, count, target);
		}
	}




最后,记录一下java NIO对大文件的读写方法:

java.nio.channels.FileChannel的map方法可以把FileChannel“包装”成MappedByteBuffer:
public abstract MappedByteBuffer map(FileChannel.MapMode mode,
                   long position,
                   long size)

对于大文件,转成MappedByteBuffer再读写,速度更快
举例:

public class ReadingHugeFilesUsingMemoryMappedBuffer {
    /**
     * use a MappedByteBuffer to wrap a huge file. Using a MappedByteBuffer does
     * not load the file in JVM but reads it directly off the file system
     * memory. The file can be opened in read, write or private mode.
     */
    // to test you can use any video movie file if you dont have any other large
    // file for testing.
    private static String hugeFile = "A Huge File";
 
    public static void main(String[] args) throws IOException {
        File file = new File(hugeFile);
        FileChannel fileChannel = new RandomAccessFile(file, "r").getChannel();
        MappedByteBuffer buffer = fileChannel.map(
                FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
        // the buffer now reads the file as if it were loaded in memory. note
        // that for smaller files it would be faster
        // to just load the file in memory
        // lets see if this buffer is loaded fully into memory
        System.out.println(buffer.isLoaded());
        // the mappedbytebuffer can be used as a normal buffer to do read and/or
        // write operations
        // read the size
        System.out.println(buffer.capacity());
     
    }
}












0
0
分享到:
评论
1 楼 萨琳娜啊 2018-07-10  
Java读源码之Netty深入剖析
网盘地址:https://pan.baidu.com/s/1pdLNtJGkOSd1fGBT_chAqA 密码: 2kfw
备用地址(腾讯微云):https://share.weiyun.com/5Bs3HcR 密码:uu95be

JavaCoder如果没有研究过Netty,那么你对Java语言的使用和理解仅仅停留在表面水平,如果你要进阶,想了解Java服务器的深层高阶知识,Netty绝对是一个必须要过的门槛。

本课程带你从一个Socket例子入手,一步步深入探究Netty各个模块的源码,深入剖析Netty的工作流程和源码设计,让你不但“真懂”也要“会用”

相关推荐

Global site tag (gtag.js) - Google Analytics