Moved the chapter 3 to the package description of org.jboss.netty.buffer. User guide will focus on bigger pictures
This commit is contained in:
parent
91c0f5ed56
commit
e8b186eb4a
@ -25,9 +25,6 @@
|
|||||||
<xi:include href="module/architecture.xml"
|
<xi:include href="module/architecture.xml"
|
||||||
xmlns:xi="http://www.w3.org/2001/XInclude" />
|
xmlns:xi="http://www.w3.org/2001/XInclude" />
|
||||||
|
|
||||||
<xi:include href="module/buffer.xml"
|
|
||||||
xmlns:xi="http://www.w3.org/2001/XInclude" />
|
|
||||||
|
|
||||||
<!-- The following chapters are not written yet. -->
|
<!-- The following chapters are not written yet. -->
|
||||||
<!--
|
<!--
|
||||||
<xi:include href="module/state-mgmt.xml"
|
<xi:include href="module/state-mgmt.xml"
|
||||||
|
@ -62,7 +62,8 @@
|
|||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
For more information, please refer to <xref linkend="buffer"/>.
|
For more information, please refer to the
|
||||||
|
<ulink url="&API;buffer/package-summary.html#package_description"><literal>org.jboss.netty.buffer</literal> package description</ulink>.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -1,153 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.docbook.org/xml/4.5/docbookx.dtd" [
|
|
||||||
<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
|
|
||||||
%CustomDTD;
|
|
||||||
]>
|
|
||||||
<chapter id="buffer">
|
|
||||||
<title>ChannelBuffer - Why and How</title>
|
|
||||||
<para>
|
|
||||||
As mentioned in <xref linkend="architecture"/>, Netty uses its own buffer
|
|
||||||
API instead of NIO <classname>ByteBuffer</classname> to represent a sequence
|
|
||||||
of bytes. This approach has significant advantage over using
|
|
||||||
<classname>ByteBuffer</classname>, which cannot be inherited to modify or
|
|
||||||
augment its behavior at all. Netty's new buffer type, &ChannelBuffer; has
|
|
||||||
been designed from ground up to address the problems of
|
|
||||||
<classname>ByteBuffer</classname> and to meet the daily needs of network
|
|
||||||
application developers. In this chapter, we will overview the features of
|
|
||||||
the new buffer API to explain what exactly it is good for.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Extensibility</title>
|
|
||||||
<para>
|
|
||||||
&ChannelBuffer; has rich set of operations optimized for rapid protocol
|
|
||||||
implementation. For example, &ChannelBuffer; provides various operations
|
|
||||||
for accessing unsigned values and strings and searching for certain byte
|
|
||||||
sequence in a buffer. You can also extend or wrap existing buffer type
|
|
||||||
to add convenient accessors. The custom buffer type still implements
|
|
||||||
&ChannelBuffer; interface rather than introducing an incompatible type.
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Transparent Zero Copy</title>
|
|
||||||
<para>
|
|
||||||
To lift up the performance of a network application to the extreme,
|
|
||||||
you need to reduce the number of memory copy operation. You might
|
|
||||||
have a set of buffers that could be sliced and combined to compose
|
|
||||||
a whole message. Netty provides a composite buffer which allows you
|
|
||||||
to create a new buffer from the arbitrary number of existing buffers
|
|
||||||
with no memory copy. For example, a message could be composed of two
|
|
||||||
parts; header and body. In a modularized application, the two parts
|
|
||||||
could be produced by different modules and assembled later when the
|
|
||||||
message is sent out.
|
|
||||||
</para>
|
|
||||||
<programlisting>+--------+----------+
|
|
||||||
| header | body |
|
|
||||||
+--------+----------+</programlisting>
|
|
||||||
<para>
|
|
||||||
If <classname>ByteBuffer</classname> were used, you would have to
|
|
||||||
create a new big buffer and copy the two parts into the new
|
|
||||||
buffer. Alternatively, you can perform a gathering write operation
|
|
||||||
in NIO, but it restricts you to represent the composite of buffers
|
|
||||||
as an array of <classname>ByteBuffer</classname>s rather than a
|
|
||||||
single <classname>ByteBuffer</classname>, breaking the abstraction and
|
|
||||||
introducing complicated state management. Moreover, it's of no use if
|
|
||||||
you are not going to read or write from an NIO channel.
|
|
||||||
</para>
|
|
||||||
<programlisting>ByteBuffer[]<co id="example.buffer1.co1"/> message = new ByteBuffer[] { header, body };</programlisting>
|
|
||||||
<calloutlist>
|
|
||||||
<callout arearefs="example.buffer1.co1">
|
|
||||||
<para>
|
|
||||||
The composite is not a <classname>ByteBuffer</classname> anymore.
|
|
||||||
</para>
|
|
||||||
</callout>
|
|
||||||
</calloutlist>
|
|
||||||
<para>
|
|
||||||
By contrast, &ChannelBuffer; does not have such caveats because it is
|
|
||||||
fully extensible and has a built-in composite buffer type.
|
|
||||||
</para>
|
|
||||||
<programlisting>&ChannelBuffer;<co id="example.buffer2.co1"/> message = &ChannelBuffers;.wrappedBuffer(header, body);
|
|
||||||
&ChannelBuffer;<co id="example.buffer2.co2"/> messageWithFooter = &ChannelBuffers;.wrappedBuffer(message, footer);
|
|
||||||
messageWithFooter.getUnsignedInt(
|
|
||||||
messageWithFooter.readableBytes() - footer.readableBytes() - 1<co id="example.buffer2.co3"/>);</programlisting>
|
|
||||||
<calloutlist>
|
|
||||||
<callout arearefs="example.buffer2.co1">
|
|
||||||
<para>
|
|
||||||
The composite is always a &ChannelBuffer;. It is completely
|
|
||||||
transparent.
|
|
||||||
</para>
|
|
||||||
</callout>
|
|
||||||
<callout arearefs="example.buffer2.co2">
|
|
||||||
<para>
|
|
||||||
You can even create a composite by mixing an existing composite and
|
|
||||||
an ordinary buffer.
|
|
||||||
</para>
|
|
||||||
</callout>
|
|
||||||
<callout arearefs="example.buffer2.co3">
|
|
||||||
<para>
|
|
||||||
Because the composite is still a &ChannelBuffer;, you can access
|
|
||||||
its content easily, and the accessor method will behave just like
|
|
||||||
it's a single buffer even if the region you want to access spans
|
|
||||||
over multiple components. The unsigned integer being read here is
|
|
||||||
located across body and footer.
|
|
||||||
</para>
|
|
||||||
</callout>
|
|
||||||
</calloutlist>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Automatic Capacity Expansion</title>
|
|
||||||
<para>
|
|
||||||
Many protocols define variable length messages, which means there's no
|
|
||||||
way to determine the length of a message until you construct the
|
|
||||||
message or it is difficult and inconvenient to calculate the length
|
|
||||||
precisely. It is just like when you build a <classname>String</classname>.
|
|
||||||
You often estimate the length of the resulting string and let
|
|
||||||
<classname>StringBuffer</classname> to expand the capacity of its
|
|
||||||
internal buffer on demand. Netty allows you to do the same via
|
|
||||||
a <firstterm>dynamic</firstterm> buffer which is created by the
|
|
||||||
&ChannelBuffers;<literal>.</literal><methodname>dynamicBuffer()</methodname>
|
|
||||||
method.
|
|
||||||
</para>
|
|
||||||
<programlisting>&ChannelBuffer;<co id="example.buffer3.co1"/> dynamicBuffer = &ChannelBuffers;.dynamicBuffer(4);
|
|
||||||
dynamicBuffer.writeByte('1');<co id="example.buffer3.co2"/>
|
|
||||||
dynamicBuffer.writeByte('2');
|
|
||||||
dynamicBuffer.writeByte('3');
|
|
||||||
dynamicBuffer.writeByte('4');
|
|
||||||
dynamicBuffer.writeByte('5');<co id="example.buffer3.co3"/>
|
|
||||||
dynamicBuffer.writeByte('6');
|
|
||||||
dynamicBuffer.writeByte('7');</programlisting>
|
|
||||||
<calloutlist>
|
|
||||||
<callout arearefs="example.buffer3.co1">
|
|
||||||
<para>
|
|
||||||
A new dynamic buffer is created. Internally, the actual buffer
|
|
||||||
is created lazily to avoid potentially wasted memory space.
|
|
||||||
</para>
|
|
||||||
</callout>
|
|
||||||
<callout arearefs="example.buffer3.co3">
|
|
||||||
<para>
|
|
||||||
When the first write attempt is made, the internal buffer is created
|
|
||||||
with the specified initial capacity (4).
|
|
||||||
</para>
|
|
||||||
</callout>
|
|
||||||
<callout arearefs="example.buffer3.co3">
|
|
||||||
<para>
|
|
||||||
When the number of written bytes exceeds the initial capacity (4),
|
|
||||||
the internal buffer is reallocated automatically with a larger
|
|
||||||
capacity.
|
|
||||||
</para>
|
|
||||||
</callout>
|
|
||||||
</calloutlist>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Documentation in progress</title>
|
|
||||||
<para>
|
|
||||||
This user guide is still under construction and waiting for your feed
|
|
||||||
back. Any idea to improve the documentation is more than appreciated.
|
|
||||||
Please join us in the <ulink url="&Community;">community</ulink> now to
|
|
||||||
share your idea!
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
</chapter>
|
|
@ -22,8 +22,117 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction of a byte-level buffer - the fundamental data structure
|
* Abstraction of a byte buffer - the fundamental data structure
|
||||||
* to represent low-level binary and text messages.
|
* to represent a low-level binary and text message.
|
||||||
|
*
|
||||||
|
* Netty uses its own buffer API instead of NIO {@link java.nio.ByteBuffer} to
|
||||||
|
* represent a sequence of bytes. This approach has significant advantage over
|
||||||
|
* using {@link java.nio.ByteBuffer}. Netty's new buffer type,
|
||||||
|
* {@link org.jboss.netty.buffer.ChannelBuffer}, has been designed from ground
|
||||||
|
* up to address the problems of {@link java.nio.ByteBuffer} and to meet the
|
||||||
|
* daily needs of network application developers. To list a few cool features:
|
||||||
|
* <ul>
|
||||||
|
* <li>You can define your buffer type if necessary.</li>
|
||||||
|
* <li>Transparent zero copy is achieved by built-in composite buffer type.</li>
|
||||||
|
* <li>A dynamic buffer type is provided out-of-the-box, whose capacity is
|
||||||
|
* expanded on demand, just like {@link java.lang.StringBuffer}.</li>
|
||||||
|
* <li>There's no need to call the {@code flip()} method anymore.</li>
|
||||||
|
* <li>It is often faster than {@link java.nio.ByteBuffer}.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <h3>Extensibility</h3>
|
||||||
|
*
|
||||||
|
* {@link org.jboss.netty.buffer.ChannelBuffer} has rich set of operations
|
||||||
|
* optimized for rapid protocol implementation. For example,
|
||||||
|
* {@link org.jboss.netty.buffer.ChannelBuffer} provides various operations
|
||||||
|
* for accessing unsigned values and strings and searching for certain byte
|
||||||
|
* sequence in a buffer. You can also extend or wrap existing buffer type
|
||||||
|
* to add convenient accessors. The custom buffer type still implements
|
||||||
|
* {@link org.jboss.netty.buffer.ChannelBuffer} interface rather than
|
||||||
|
* introducing an incompatible type.
|
||||||
|
*
|
||||||
|
* <h3>Transparent Zero Copy</h3>
|
||||||
|
*
|
||||||
|
* To lift up the performance of a network application to the extreme, you need
|
||||||
|
* to reduce the number of memory copy operation. You might have a set of
|
||||||
|
* buffers that could be sliced and combined to compose a whole message. Netty
|
||||||
|
* provides a composite buffer which allows you to create a new buffer from the
|
||||||
|
* arbitrary number of existing buffers with no memory copy. For example, a
|
||||||
|
* message could be composed of two parts; header and body. In a modularized
|
||||||
|
* application, the two parts could be produced by different modules and
|
||||||
|
* assembled later when the message is sent out.
|
||||||
|
* <pre>
|
||||||
|
* +--------+----------+
|
||||||
|
* | header | body |
|
||||||
|
* +--------+----------+
|
||||||
|
* </pre>
|
||||||
|
* If {@link java.nio.ByteBuffer} were used, you would have to create a new big
|
||||||
|
* buffer and copy the two parts into the new buffer. Alternatively, you can
|
||||||
|
* perform a gathering write operation in NIO, but it restricts you to represent
|
||||||
|
* the composite of buffers as an array of {@link java.nio.ByteBuffer}s rather
|
||||||
|
* than a single buffer, breaking the abstraction and introducing complicated
|
||||||
|
* state management. Moreover, it's of no use if you are not going to read or
|
||||||
|
* write from an NIO channel.
|
||||||
|
* <pre>
|
||||||
|
* // The composite type is incompatible with the component type.
|
||||||
|
* ByteBuffer[] message = new ByteBuffer[] { header, body };
|
||||||
|
* </pre>
|
||||||
|
* By contrast, {@link org.jboss.netty.buffer.ChannelBuffer} does not have such
|
||||||
|
* caveats because it is fully extensible and has a built-in composite buffer
|
||||||
|
* type.
|
||||||
|
* <pre>
|
||||||
|
* // The composite type is compatible with the component type.
|
||||||
|
* ChannelBuffer message = ChannelBuffers.wrappedBuffer(header, body);
|
||||||
|
*
|
||||||
|
* // Therefore, you can even create a composite by mixing a composite and an
|
||||||
|
* // ordinary buffer.
|
||||||
|
* ChannelBuffer messageWithFooter = ChannelBuffers.wrappedBuffer(message, footer);
|
||||||
|
*
|
||||||
|
* // Because the composite is still a ChannelBuffer, you can access its content
|
||||||
|
* // easily, and the accessor method will behave just like it's a single buffer
|
||||||
|
* // even if the region you want to access spans over multiple components. The
|
||||||
|
* // unsigned integer being read here is located across body and footer.
|
||||||
|
* messageWithFooter.getUnsignedInt(
|
||||||
|
* messageWithFooter.readableBytes() - footer.readableBytes() - 1);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h3>Automatic Capacity Extension</h3>
|
||||||
|
*
|
||||||
|
* Many protocols define variable length messages, which means there's no way to
|
||||||
|
* determine the length of a message until you construct the message or it is
|
||||||
|
* difficult and inconvenient to calculate the length precisely. It is just
|
||||||
|
* like when you build a {@link java.lang.String}. You often estimate the length
|
||||||
|
* of the resulting string and let {@link java.lang.StringBuffer} expand itself
|
||||||
|
* on demand. Netty allows you to do the same via a <em>dynamic</em> buffer
|
||||||
|
* which is created by the
|
||||||
|
* {@link org.jboss.netty.buffer.ChannelBuffers#dynamicBuffer()} method.
|
||||||
|
* <pre>
|
||||||
|
* // A new dynamic buffer is created. Internally, the actual buffer is created
|
||||||
|
* // lazily to avoid potentially wasted memory space.
|
||||||
|
* ChannelBuffer b = ChannelBuffers.dynamicBuffer(4);
|
||||||
|
*
|
||||||
|
* // When the first write attempt is made, the internal buffer is created with
|
||||||
|
* // the specified initial capacity (4).
|
||||||
|
* b.writeByte('1');
|
||||||
|
*
|
||||||
|
* b.writeByte('2');
|
||||||
|
* b.writeByte('3');
|
||||||
|
* b.writeByte('4');
|
||||||
|
*
|
||||||
|
* // When the number of written bytes exceeds the initial capacity (4), the
|
||||||
|
* // internal buffer is reallocated automatically with a larger capacity.
|
||||||
|
* b.writeByte('5');
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h3>Better Performance</h3>
|
||||||
|
*
|
||||||
|
* Most frequently used buffer implementation of
|
||||||
|
* {@link org.jboss.netty.buffer.ChannelBuffer} is a very thin wrapper of a
|
||||||
|
* byte array (i.e. {@code byte[]}). Unlike {@link java.nio.ByteBuffer}, it has
|
||||||
|
* no complicated boundary check and index compensation, and therefore it is
|
||||||
|
* easier for a JVM to optimize the buffer access. More complicated buffer
|
||||||
|
* implementation is used only for sliced or composite buffers, and it performs
|
||||||
|
* as well as {@link java.nio.ByteBuffer}.
|
||||||
*
|
*
|
||||||
* @apiviz.landmark
|
* @apiviz.landmark
|
||||||
* @apiviz.exclude ^java\.io\.[^\.]+Stream$
|
* @apiviz.exclude ^java\.io\.[^\.]+Stream$
|
||||||
|
Loading…
x
Reference in New Issue
Block a user