Netty的各种简单介绍,总体架构就不介绍了,假设大家感觉的确须要,给我留言我再追加。
这里再推广一个自己做得netty+spring的集成方案,优化netty配置启动,并提供基础server搭建的配置+极少代码的实现方案。
http://download.csdn.net/detail/jackieliyido/9497093
回归正事:咱们直接从从Netty的核心类ByteBuf開始看起。
先看抽象类定义,后面的方法我们详细看核心实现AbstractByteBuf
先看ByteBuf源代码。
@SuppressWarnings("ClassMayBeInterface") public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf>
先告诉编译器忽略ClassMayBeInterface警告。
然后看到ByteBuf类本身实现了一个netty的引用计数器,以及compareble接口。为什么要写这里,由于看类名的时候第一反应是这个类继承自ByteBuffer.
然后再看凝视,凝视非常重要,作为netty刚開始学习的人是最重要的guide line。
* <h3>Creation of a buffer</h3> * * It is recommended to create a new buffer using the helper methods in * {@link Unpooled} rather than calling an individual implementation's * constructor.
推荐使用helper 的方法去创建一块新的buffer,不推荐使用实现类自己的构造器。
下一段:
* <h3>Random Access Indexing</h3> * * Just like an ordinary primitive byte array, {@link ByteBuf} uses * <a href="http://en.wikipedia.org/wiki/Zero-based_numbering">zero-based indexing</a>. * It means the index of the first byte is always {@code 0} and the index of the last byte is * always {@link #capacity() capacity - 1}. For example, to iterate all bytes of a buffer, you * can do the following, regardless of its internal implementation: * * <pre> * {@link ByteBuf} buffer = ...; * for (int i = 0; i < buffer.capacity(); i ++) { * byte b = buffer.getByte(i); * System.out.println((char) b); * } * </pre>
这一段比較水。事实上说的bytebuf是遵循以0为第一位的计数方式。第一眼看到zero-based indexing 没反应过来。后来一看就是说从0開始计数。
嗯。作者非常负责。同一时候学了一个装逼的词 zero-based indexing...
同一时候咱不漏过上面的一句话,buffer.capacity(),这种方法是获取buffer的容量。
buffer.getByte(i),获取指定位置的字节数据
重头戏開始来来了,来看以下一段:
* <h3>Sequential Access Indexing</h3> * * {@link ByteBuf} provides two pointer variables to support sequential * read and write operations - {@link #readerIndex() readerIndex} for a read * operation and {@link #writerIndex() writerIndex} for a write operation * respectively. The following diagram shows how a buffer is segmented into * three areas by the two pointers: * * <pre> * +-------------------+------------------+------------------+ * | discardable bytes | readable bytes | writable bytes | * | | (CONTENT) | | * +-------------------+------------------+------------------+ * | | | | * 0 <= readerIndex <= writerIndex <= capacity * </pre>顺序訪问索引。ByteBuf提供两个指针标量来支持读写操作。嗯,这个有点像曾经有面试官问的问题。怎样用循环数组实现队列。
看图上第一段,是被丢弃的字节,这个和我们的电脑磁盘操作一样,删除文件时仅仅是将文件的描写叙述指针从当前位置挪开(好吧。事实上我也不知道叫啥指针甚至于是不是指针),将这块区域置为新空间标识,嗯。大概就是这么个意思。
第一段以下有个readerIndex,也就是在0到readIndex之间都是被丢弃的字节(已读完)。readerIndex 到writerIndex之间为可读内容,writeable到容量之间的区域为可写区域。
这里easy误解的地方在于。什么是write,read.这里的读写不是指我们写数据发出去,读数据进来两个动作同一时候发生。而是指单个业务行为比方读数据进来的时候实际发生的底层动作。有点绕。写的好像有点多余。
通过几个图应该就能够看出来了(这部分參考的《netty权威指南》):
初始化ByteBuf的时候:
* +---------------------------------------------------------+ * | writable bytes | * | | * +---------------------------------------------------------+ * | | * 0 =readerIndex =writerIndex capacity
写入N个字节后的ByteBuf:
* +--------------------------------------+------------------+ * | readable bytes | writable bytes | <pre name="code" class="java"> * | | |* +--------------------------------------+------------------+ * | | * 0 =readerIndex N=writerIndex capacity
读取M(M<N)个字节以后:
* +-------------------+------------------+------------------+ * | discardable bytes | readable bytes | writable bytes | * | | | | * +-------------------+------------------+------------------+ * | | | | * 0 M=readerIndex N=writerIndex capacity
调用discardReadBytes操作后的ByteBuf:
* +-------------------+-------------------------------------+ * | readable bytes | writable bytes | * | | | * +-------------------+-------------------------------------+ * | | | * 0=readerIndex N-M=writerIndex capacity
调用clear方法后:
* +---------------------------------------------------------+ * | writable bytes | * | | * +---------------------------------------------------------+ * | | * 0 =readerIndex =writerIndex capacity
认真看完以上几个图后,能够知道readerIndex和writerIndex的工作方式了。
继续回到凝视上,我们掠过代码中关于两个指针的描写叙述后,来到例如以下内容:
* <h3>Search operations</h3> * * For simple single-byte searches, use {@link #indexOf(int, int, byte)} and {@link #bytesBefore(int, int, byte)}. * {@link #bytesBefore(byte)} is especially useful when you deal with a {@code NUL}-terminated string. * For complicated searches, use {@link #forEachByte(int, int, ByteBufProcessor)} with a {@link ByteBufProcessor} * implementation.这个是关于检索/搜索的描写叙述,对于简单的单子接搜索,推荐使用 ByteBuf.indexOf(int fromIndex, int toIndex, byte value)、bytesBefore(int index,int length,byte value)以及bytesBefore(byte value)进行检索,粗粗看了下关于这几个方法的描写叙述。均是指在可读区域(即readerIndex与writerIndex之间部分)内的指定长度范围检索特定值。返回为找到的第一个值的index值,否则返回-1.
关于重置和标记:
* <h3>Mark and reset</h3> * * There are two marker indexes in every buffer. One is for storing * {@link #readerIndex() readerIndex} and the other is for storing * {@link #writerIndex() writerIndex}. You can always reposition one of the * two indexes by calling a reset method. It works in a similar fashion to * the mark and reset methods in {@link InputStream} except that there's no * {@code readlimit}.这里说的rederIndex和writerIndex能够觉得是两个marker,并且能够像使用InputStream一样进行reset,差别在于InputStream有readlimit參数。
补充下InputStream.mark(int readlimit)其中readlimit的作用:在inputStream中。mark的作用是在你mark完一个数据点时,当你继续往下读的时候能够通过调用reset方法回到mark的地点。
可是,这个mark须要有个限制。当你在读readlimit的数据长度内,都会保留mark。超出则不再保留mark
关于衍生缓冲区:
* <h3>Derived buffers</h3> * * You can create a view of an existing buffer by calling either * {@link #duplicate()}, {@link #slice()} or {@link #slice(int, int)}. * A derived buffer will have an independent {@link #readerIndex() readerIndex}, * {@link #writerIndex() writerIndex} and marker indexes, while it shares * other internal data representation, just like a NIO buffer does. * <p> * In case a completely fresh copy of an existing buffer is required, please * call {@link #copy()} method instead.
调用duplicate()、slice()、slice(int index, int length)会创建一个现有缓冲区的视图。衍生的缓冲区有独立的readerIndex、writerIndex和标注索引。共享内部数据。假设须要现有缓冲区的全新副本,能够使用copy()获得。
这里须要说明:duplicate。slice方法均持有独立的读写索引,可是共享内容指针引用。即改动副本的内容将影响原本中的内容。
最后一段。关于转换成JDK已存在的类
* <h3>Conversion to existing JDK types</h3> * * <h4>Byte array</h4> * * If a {@link ByteBuf} is backed by a byte array (i.e. {@code byte[]}), * you can access it directly via the {@link #array()} method. To determine * if a buffer is backed by a byte array, {@link #hasArray()} should be used. * * <h4>NIO Buffers</h4> * * If a {@link ByteBuf} can be converted into an NIO {@link ByteBuffer} which shares its * content (i.e. view buffer), you can get it via the {@link #nioBuffer()} method. To determine * if a buffer can be converted into an NIO buffer, use {@link #nioBufferCount()}. * * <h4>Strings</h4> * * Various {@link #toString(Charset)} methods convert a {@link ByteBuf} * into a {@link String}. Please note that {@link #toString()} is not a * conversion method. * * <h4>I/O Streams</h4> * * Please refer to {@link ByteBufInputStream} and * {@link ByteBufOutputStream}.数组:假设一个bytebuf是一个数组类型。能够使用array()方法。推断是否为数组的方法为:hasArray()
NIO Buffer:能够通过使用nioBuffer()方法使ByteBuf转换为java.nio.ByteBuffer。
推断能否转换的方法为:nioBufferCount(). nioBufferCount()返回NIO buffer的个数。假设没有则返回-1.
转String:toString(Charset)将可读区域的bytes依照指定编码转换成String,转换不改动readerIndex和writerIndex. toString()打印内存地址,并不转换成String
IO stream:參看日后分析的ByteBufInputStream和ByteBufOutputStream
第一篇先写到这,接下来我们主要分析AbstractByteBuf、HeapByteBuf、DirectByteBuf等几个核心实现。