<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>kafka0102的边城客栈 &#187; netty</title>
	<atom:link href="http://www.kafka0102.com/tag/netty/feed" rel="self" type="application/rss+xml" />
	<link>http://www.kafka0102.com</link>
	<description>要有最朴素的生活与最遥远的梦想，即使明日天寒地冻、路远马亡。</description>
	<lastBuildDate>Sat, 18 Jun 2011 04:20:42 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Netty实现原理浅析</title>
		<link>http://www.kafka0102.com/2010/06/167.html</link>
		<comments>http://www.kafka0102.com/2010/06/167.html#comments</comments>
		<pubDate>Sat, 19 Jun 2010 20:21:20 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[framework]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[netty]]></category>
		<category><![CDATA[nio framework]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=167</guid>
		<description><![CDATA[Netty是JBoss出品的高效的Java NIO开发框架，关于其使用，可参考我的另一篇文章 netty使用初步。本文将主要分析Netty实现方面的东西，由于精力有限，本人并没有对其源码做了极细致的研 究。如果下面的内容有错误或不严谨的地方，也请指正和谅解。对于Netty使用者来说，Netty提供了几个典型的example，并有详尽的API doc和guide doc，本文的一些内容及图示也来自于Netty的文档，特此致谢。]]></description>
			<content:encoded><![CDATA[<p>Netty是JBoss出品的高效的Java  NIO开发框架，关于其使用，可参考我的另一篇文章<a href="http://www.kafka0102.com/2010/06/netty%E4%BD%BF%E7%94%A8%E5%88%9D%E6%AD%A5/" target="_blank"> netty使用初步</a>。本文将主要分析Netty实现方面的东西，由于精力有限，本人并没有对其源码做了极细致的研 究。如果下面的内容有错误或不严谨的地方，也请指正和谅解。对于Netty使用者来说，Netty提供了几个典型的example，并有详尽的API  doc和guide doc，本文的一些内容及图示也来自于Netty的文档，特此致谢。</p>
<h2>1、总体结构</h2>
<p style="text-align: left;"><a href="http://www.kafka0102.com/wp-content/uploads/2010/06/architecture.png"><img class="aligncenter size-full wp-image-168" title="architecture" src="http://www.kafka0102.com/wp-content/uploads/2010/06/architecture.png" alt="" width="605" height="287" /></a></p>
<p style="text-align: left;">先放上一张漂亮的Netty总体结构图，下面的内容也主要围绕该图上的一些核心功能做分析，但对如Container  Integration及Security Support等高级可选功能，本文不予分析。</p>
<h2>2、网络模型</h2>
<p>Netty是典型的Reactor模型结构，关于Reactor的详尽阐释，可参考POSA2,这里不做概念性的解释。而应用Java  NIO构建Reactor模式，Doug Lea（就是那位让人无限景仰的大爷）在“<a href="http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf" target="_blank">Scalable IO in  Java</a>”中给了很好的阐述。这里截取其PPT中经典的图例说明 Reactor模式的典型实现：</p>
<p>1、这是最简单的单Reactor单线程模型。Reactor线程是个多面手，负责多路分离套接字，Accept新连接，并分派请求到处理器链中。该模型 适用于处理器链中业务处理组件能快速完成的场景。不过，这种单线程模型不能充分利用多核资源，所以实际使用的不多。</p>
<p style="text-align: center;"><a href="http://www.kafka0102.com/wp-content/uploads/2010/06/reactor1.png"><img class="aligncenter size-full wp-image-170" title="reactor1" src="http://www.kafka0102.com/wp-content/uploads/2010/06/reactor1.png" alt="" width="470" height="256" /></a></p>
<p>2、相比上一种模型，该模型在处理器链部分采用了多线程（线程池），也是后端程序常用的模型。</p>
<p style="text-align: center;"><a href="http://www.kafka0102.com/wp-content/uploads/2010/06/reactor2.png"><img class="aligncenter size-full wp-image-172" title="reactor2" src="http://www.kafka0102.com/wp-content/uploads/2010/06/reactor2.png" alt="" width="582" height="395" /></a></p>
<p style="text-align: left;">3、 第三种模型比起第二种模型，是将Reactor分成两部分，mainReactor负责监听server  socket，accept新连接，并将建立的socket分派给subReactor。subReactor负责多路分离已连接的socket，读写网 络数据，对业务处理功能，其扔给worker线程池完成。通常，subReactor个数上可与CPU个数等同。<br />
<a href="http://www.kafka0102.com/wp-content/uploads/2010/06/reactor3.png"><img class="aligncenter size-full wp-image-173" title="reactor3" src="http://www.kafka0102.com/wp-content/uploads/2010/06/reactor3.png" alt="" width="573" height="389" /></a></p>
<p>说完Reacotr模型的三种形式，那么Netty是哪种呢？其实，我还有一种Reactor模型的变种没说，那就是去掉线程池的第三种形式的变种，这也 是Netty  NIO的默认模式。在实现上，Netty中的Boss类充当mainReactor，NioWorker类充当subReactor（默认 NioWorker的个数是Runtime.getRuntime().availableProcessors()）。在处理新来的请求 时，NioWorker读完已收到的数据到ChannelBuffer中，之后触发ChannelPipeline中的ChannelHandler流。</p>
<p>Netty是事件驱动的，可以通过ChannelHandler链来控制执行流向。因为ChannelHandler链的执行过程是在 subReactor中同步的，所以如果业务处理handler耗时长，将严重影响可支持的并发数。这种模型适合于像Memcache这样的应用场景，但 对需要操作数据库或者和其他模块阻塞交互的系统就不是很合适。Netty的可扩展性非常好，而像ChannelHandler线程池化的需要，可以通过在 ChannelPipeline中添加Netty内置的ChannelHandler实现类&#8211;ExecutionHandler实现，对使用者来说只是 添加一行代码而已。对于ExecutionHandler需要的线程池模型，Netty提供了两种可 选：1） MemoryAwareThreadPoolExecutor 可控制Executor中待处理任务的上限（超过上限时，后续进来的任务将被阻 塞），并可控制单个Channel待处理任务的上限；2） OrderedMemoryAwareThreadPoolExecutor 是  MemoryAwareThreadPoolExecutor 的子类，它还可以保证同一Channel中处理的事件流的顺序性，这主要是控制事件在异步处 理模式下可能出现的错误的事件顺序，但它并不保证同一Channel中的事件都在一个线程中执行（通常也没必要）。一般来 说，OrderedMemoryAwareThreadPoolExecutor 是个很不错的选择，当然，如果有需要，也可以DIY一个。</p>
<h2>3、 buffer</h2>
<p>org.jboss.netty.buffer包的接口及类的结构图如下：</p>
<p style="text-align: center;"><a href="http://www.kafka0102.com/wp-content/uploads/2010/06/channelbuffer.png"><img class="aligncenter size-full wp-image-174" title="channelbuffer" src="http://www.kafka0102.com/wp-content/uploads/2010/06/channelbuffer.png" alt="" width="801" height="384" /></a></p>
<p>该包核心的接口是ChannelBuffer和ChannelBufferFactory,下面予以简要的介绍。</p>
<p>Netty使用ChannelBuffer来存储并操作读写的网络数据。ChannelBuffer除了提供和ByteBuffer类似的方法，还提供了 一些实用方法，具体可参考其API文档。ChannelBuffer的实现类有多个，这里列举其中主要的几个：</p>
<p>1）HeapChannelBuffer：这是Netty读网络数据时默认使用的ChannelBuffer，这里的Heap就是Java堆的意思，因为 读SocketChannel的数据是要经过ByteBuffer的，而ByteBuffer实际操作的就是个byte数组，所以 ChannelBuffer的内部就包含了一个byte数组，使得ByteBuffer和ChannelBuffer之间的转换是零拷贝方式。根据网络字 节续的不同，HeapChannelBuffer又分为BigEndianHeapChannelBuffer和 LittleEndianHeapChannelBuffer，默认使用的是BigEndianHeapChannelBuffer。Netty在读网络 数据时使用的就是HeapChannelBuffer，HeapChannelBuffer是个大小固定的buffer，为了不至于分配的Buffer的 大小不太合适，Netty在分配Buffer时会参考上次请求需要的大小。</p>
<p>2）DynamicChannelBuffer：相比于HeapChannelBuffer，DynamicChannelBuffer可动态自适应大 小。对于在DecodeHandler中的写数据操作，在数据大小未知的情况下，通常使用DynamicChannelBuffer。</p>
<p>3）ByteBufferBackedChannelBuffer：这是directBuffer，直接封装了ByteBuffer的 directBuffer。</p>
<p>对于读写网络数据的buffer，分配策略有两种：1）通常出于简单考虑，直接分配固定大小的buffer，缺点是，对一些应用来说这个大小限制有时是不 合理的，并且如果buffer的上限很大也会有内存上的浪费。2）针对固定大小的buffer缺点，就引入动态buffer，动态buffer之于固定 buffer相当于List之于Array。</p>
<p>buffer的寄存策略常见的也有两种（其实是我知道的就限于此）：1）在多线程（线程池） 模型下，每个线程维护自己的读写buffer，每次处理新的请求前清空buffer（或者在处理结束后清空），该请求的读写操作都需要在该线程中完成。 2）buffer和socket绑定而与线程无关。两种方法的目的都是为了重用buffer。</p>
<p>Netty对buffer的处理策略是：读 请求数据时，Netty首先读数据到新创建的固定大小的HeapChannelBuffer中，当HeapChannelBuffer满或者没有数据可读 时，调用handler来处理数据，这通常首先触发的是用户自定义的DecodeHandler，因为handler对象是和ChannelSocket 绑定的，所以在DecodeHandler里可以设置ChannelBuffer成员，当解析数据包发现数据不完整时就终止此次处理流程，等下次读事件触 发时接着上次的数据继续解析。就这个过程来说，和ChannelSocket绑定的DecodeHandler中的Buffer通常是动态的可重用 Buffer（DynamicChannelBuffer），而在NioWorker中读ChannelSocket中的数据的buffer是临时分配的 固定大小的HeapChannelBuffer，这个转换过程是有个字节拷贝行为的。</p>
<p>对ChannelBuffer的创建，Netty内部使用的是ChannelBufferFactory接口，具体的实现有 DirectChannelBufferFactory和HeapChannelBufferFactory。对于开发者创建 ChannelBuffer，可使用实用类ChannelBuffers中的工厂方法。</p>
<h2>4、Channel</h2>
<p>和Channel相关的接口及类结构图如下：</p>
<p><a href="http://www.kafka0102.com/wp-content/uploads/2010/06/Channel.png"><img class="aligncenter size-full wp-image-175" title="Channel" src="http://www.kafka0102.com/wp-content/uploads/2010/06/Channel.png" alt="" width="539" height="253" /></a></p>
<p>从该结构图也可以看到，Channel主要提供的功能如下：</p>
<p>1）当前Channel的状态信息，比如是打开还是关闭等。<br />
2）通过ChannelConfig可以得到的Channel配置信息。<br />
3）Channel所支持的如read、write、bind、connect等IO操作。<br />
4）得到处理该Channel的ChannelPipeline，既而可以调用其做和请求相关的IO操作。</p>
<p>在Channel实现方面，以通常使用的nio  socket来说，Netty中的NioServerSocketChannel和NioSocketChannel分别封装了java.nio中包含的 ServerSocketChannel和SocketChannel的功能。</p>
<h2>5、ChannelEvent</h2>
<p>如前所述，Netty是事件驱动的，其通过ChannelEvent来确定事件流的方向。一个ChannelEvent是依附于Channel的 ChannelPipeline来处理，并由ChannelPipeline调用ChannelHandler来做具体的处理。下面是和 ChannelEvent相关的接口及类图：</p>
<p><a href="http://www.kafka0102.com/wp-content/uploads/2010/06/ChannelEvent.png"><img class="aligncenter size-full wp-image-176" title="ChannelEvent" src="http://www.kafka0102.com/wp-content/uploads/2010/06/ChannelEvent.png" alt="" width="416" height="437" /></a></p>
<p>对于使用者来说，在ChannelHandler实现类中会使用继承于ChannelEvent的MessageEvent，调用其 getMessage()方法来获得读到的ChannelBuffer或被转化的对象。</p>
<h2>6、ChannelPipeline</h2>
<p>Netty 在事件处理上，是通过ChannelPipeline来控制事件流，通过调用注册其上的一系列ChannelHandler来处理事件，这也是典型的拦截 器模式。下面是和ChannelPipeline相关的接口及类图：</p>
<p><a href="http://www.kafka0102.com/wp-content/uploads/2010/06/ChannelPipeline.png"><img class="aligncenter size-full wp-image-177" title="ChannelPipeline" src="http://www.kafka0102.com/wp-content/uploads/2010/06/ChannelPipeline.png" alt="" width="559" height="237" /></a></p>
<p>事件流有两种，upstream事件和downstream事件。在ChannelPipeline中，其可被注册的ChannelHandler既可以 是 ChannelUpstreamHandler 也可以是ChannelDownstreamHandler  ，但事件在ChannelPipeline传递过程中只会调用匹配流的ChannelHandler。在事件流的过滤器链 中，ChannelUpstreamHandler或ChannelDownstreamHandler既可以终止流程，也可以通过调用 ChannelHandlerContext.sendUpstream(ChannelEvent)或 ChannelHandlerContext.sendDownstream(ChannelEvent)将事件传递下去。下面是事件流处理的图示：</p>
<p><a href="http://www.kafka0102.com/wp-content/uploads/2010/06/ChannelPipeline.jpg"><img class="aligncenter size-full wp-image-178" title="ChannelPipeline" src="http://www.kafka0102.com/wp-content/uploads/2010/06/ChannelPipeline.jpg" alt="" width="522" height="622" /></a></p>
<p>从上图可见，upstream event是被Upstream Handler们自底向上逐个处理，downstream  event是被Downstream  Handler们自顶向下逐个处理，这里的上下关系就是向ChannelPipeline里添加Handler的先后顺序关系。简单的理 解，upstream event是处理来自外部的请求的过程，而downstream event是处理向外发送请求的过程。</p>
<p>服务端处 理请求的过程通常就是解码请求、业务逻辑处理、编码响应，构建的ChannelPipeline也就类似下面的代码片断：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">ChannelPipeline pipeline <span style="color: #339933;">=</span> Channels.<span style="color: #006633;">pipeline</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
pipeline.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;decoder&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> MyProtocolDecoder<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
pipeline.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;encoder&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> MyProtocolEncoder<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
pipeline.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;handler&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> MyBusinessLogicHandler<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>其中，MyProtocolDecoder是ChannelUpstreamHandler类型，MyProtocolEncoder是 ChannelDownstreamHandler类型，MyBusinessLogicHandler既可以是 ChannelUpstreamHandler类型，也可兼ChannelDownstreamHandler类型，视其是服务端程序还是客户端程序以及 应用需要而定。</p>
<p>补充一点，Netty对抽象和实现做了很好的解耦。像org.jboss.netty.channel.socket包， 定义了一些和socket处理相关的接口，而org.jboss.netty.channel.socket.nio、 org.jboss.netty.channel.socket.oio等包，则是和协议相关的实现。</p>
<h2>7、codec  framework</h2>
<p>对于请求协议的编码解码，当然是可以按照协议格式自己操作ChannelBuffer中的字节数据。另一方面，Netty也做了几个很实用的codec  helper，这里给出简单的介绍。</p>
<p>1）FrameDecoder：FrameDecoder内部维护了一个 DynamicChannelBuffer成员来存储接收到的数据，它就像个抽象模板，把整个解码过程模板写好了，其子类只需实现decode函数即可。 FrameDecoder的直接实现类有两个：（1）DelimiterBasedFrameDecoder是基于分割符  （比如\r\n）的解码器，可在构造函数中指定分割符。（2）LengthFieldBasedFrameDecoder是基于长度字段的解码器。如果协 议 格式类似“内容长度”+内容、“固定头”+“内容长度”+动态内容这样的格式，就可以使用该解码器，其使用方法在API DOC上详尽的解释。<br />
2）ReplayingDecoder： 它是FrameDecoder的一个变种子类，它相对于FrameDecoder是非阻塞解码。也就是说，使用  FrameDecoder时需要考虑到读到的数据有可能是不完整的，而使用ReplayingDecoder就可以假定读到了全部的数据。<br />
3）ObjectEncoder 和ObjectDecoder：编码解码序列化的Java对象。<br />
4）HttpRequestEncoder和 HttpRequestDecoder：http协议处理。</p>
<p>下面来看使用FrameDecoder和ReplayingDecoder的两个例子：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> IntegerHeaderFrameDecoder <span style="color: #000000; font-weight: bold;">extends</span> FrameDecoder <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #003399;">Object</span> decode<span style="color: #009900;">&#40;</span>ChannelHandlerContext ctx, Channel channel,
				ChannelBuffer buf<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>buf.<span style="color: #006633;">readableBytes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;</span> <span style="color: #cc66cc;">4</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
			buf.<span style="color: #006633;">markReaderIndex</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #000066; font-weight: bold;">int</span> length <span style="color: #339933;">=</span> buf.<span style="color: #006633;">readInt</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>buf.<span style="color: #006633;">readableBytes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;</span> length<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				buf.<span style="color: #006633;">resetReaderIndex</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
			<span style="color: #000000; font-weight: bold;">return</span> buf.<span style="color: #006633;">readBytes</span><span style="color: #009900;">&#40;</span>length<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>而使用ReplayingDecoder的解码片断类似下面的，相对来说会简化很多。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> IntegerHeaderFrameDecoder2 <span style="color: #000000; font-weight: bold;">extends</span> ReplayingDecoder <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #003399;">Object</span> decode<span style="color: #009900;">&#40;</span>ChannelHandlerContext ctx, Channel channel,
				ChannelBuffer buf, VoidEnum state<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">return</span> buf.<span style="color: #006633;">readBytes</span><span style="color: #009900;">&#40;</span>buf.<span style="color: #006633;">readInt</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>就实现来说，当在ReplayingDecoder子类的decode函数中调用ChannelBuffer读数据时，如果读失败，那么 ReplayingDecoder就会catch住其抛出的Error，然后ReplayingDecoder接手控制权，等待下一次读到后续的数据后继 续decode。</p>
<h2>8、小结</h2>
<p>尽管该文行至此处将止，但该文显然没有将Netty实现原理深入浅出的说全说透。当我打算写这篇文章时，也是一边看Netty的代码，一边总结些可写的东 西，但前后断断续续，到最后都没了多少兴致。我还是爱做一些源码分析的事情，但精力终究有限，并且倘不能把源码分析的结果有条理的托出来，不能产生有意义 的心得，这分析也没什么价值和趣味。而就分析Netty代码的感受来说，Netty的代码很漂亮，结构上层次上很清晰，不过这种面向接口及抽象层次对代码 跟踪很是个问题，因为跟踪代码经常遇到接口和抽象类，只能借助于工厂类和API  DOC，反复对照接口和实现类的对应关系。就像几乎任何优秀的Java开源项目都会用上一系列优秀的设计模式，也完全可以从模式这一点单独拿出一篇分析文 章来，尽管我目前没有这样的想法。而在此文完成之后，我也没什么兴趣再看Netty的代码了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/06/167.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Netty使用初步</title>
		<link>http://www.kafka0102.com/2010/06/161.html</link>
		<comments>http://www.kafka0102.com/2010/06/161.html#comments</comments>
		<pubDate>Sat, 19 Jun 2010 16:32:15 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[framework]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[netty]]></category>
		<category><![CDATA[nio framework]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=161</guid>
		<description><![CDATA[    Java1.4提供了NIO使开发者可以使用Java编写高性能的服务端程序，但使用原生的NIO API就像Linux C中网络编程一样，还是需要做IO处理、协议处理等低层次工作。所以，就像C服务端程序大量使用libevent作为网络应用框架一样，Java社区也不断涌现出基于NIO的网络应用框架。在这其中，Jboss出品的Netty就是个中翘楚。Netty是个异步的事件驱动网络应用框架，具有高性能、高扩展性等特性。Netty提供了统一的底层协议接口，使得开发者从底层的网络协议（比如TCP/IP、UDP）中解脱出来。就使用来说，开发者只要参考 Netty提供的若干例子和它的指南文档，就可以放手开发基于Netty的服务端程序了。
    在Java社区，最知名的开源Java NIO框架要属Mina和Netty，而且两者渊源颇多，对两者的比较自然不少。实际上，Netty的作者原来就是Mina作者之一，所以可以想到，Netty和Mina在设计理念上会有很多共同点。我对Mina没什么研究，但其作者介绍，Netty的设计对开发者有更友好的扩展性，并且性能方面要优于Mina，而Netty完善的文档也很吸引人。所以，如果你在寻找Java NIO框架，Netty是个很不错的选择。本文的内容就是围绕一个demo介绍使用Netty的点点滴滴。]]></description>
			<content:encoded><![CDATA[<h2>1、简介</h2>
<p>    Java1.4提供了NIO使开发者可以使用Java编写高性能的服务端程序，但使用原生的NIO API就像Linux C中网络编程一样，还是需要做IO处理、协议处理等低层次工作。所以，就像C服务端程序大量使用libevent作为网络应用框架一样，Java社区也不断涌现出基于NIO的网络应用框架。在这其中，Jboss出品的Netty就是个中翘楚。Netty是个异步的事件驱动网络应用框架，具有高性能、高扩展性等特性。Netty提供了统一的底层协议接口，使得开发者从底层的网络协议（比如TCP/IP、UDP）中解脱出来。就使用来说，开发者只要参考 Netty提供的若干例子和它的指南文档，就可以放手开发基于Netty的服务端程序了。</p>
<p>    在Java社区，最知名的开源Java NIO框架要属Mina和Netty，而且两者渊源颇多，对两者的比较自然不少。实际上，Netty的作者原来就是Mina作者之一，所以可以想到，Netty和Mina在设计理念上会有很多共同点。我对Mina没什么研究，但其作者介绍，Netty的设计对开发者有更友好的扩展性，并且性能方面要优于Mina，而Netty完善的文档也很吸引人。所以，如果你在寻找Java NIO框架，Netty是个很不错的选择。本文的内容就是围绕一个demo介绍使用Netty的点点滴滴。</p>
<h2>2、服务端程序</h2>
<h3>2.1、ChannelHandler</h3>
<p>     服务端程序通常的处理过程是：解码请求数据、业务逻辑处理、编码响应。从框架角度来说，可以提供3个接口来控制并调度该处理过程；从更通用的角度来说，并不特化处理其中的每一步，而把每一步当做过滤器链中的一环，这也是Netty的做法。Netty对请求处理过程实现了过滤器链模式（ChannelPipeline），每个过滤器实现了ChannelHandler接口。Netty中有两种请求事件流类型也做了细分：</p>
<p>    1）downstream event：其对应的ChannelHandler子接口是ChannelDownstreamHandler。downstream event是说从头到尾执行ChannelPipeline中的ChannelDownstreamHandler，这一过程相当于向外发送数据的过程。 downstream event有：&#8221;write&#8221;、&#8221;bind&#8221;、&#8221;unbind&#8221;、 &#8220;connect&#8221;、 &#8220;disconnect&#8221;、&#8221;close&#8221;。</p>
<p>    2）upstream event：其对应的ChannelHandler子接口是ChannelUpstreamHandler。upstream event处理的事件方向和downstream event相反，这一过程相当于接收处理外来请求的过程。upstream event有：&#8221;messageReceived&#8221;、 &#8220;exceptionCaught&#8221;、&#8221;channelOpen&#8221;、&#8221;channelClosed&#8221;、 &#8220;channelBound&#8221;、&#8221;channelUnbound&#8221;、 &#8220;channelConnected&#8221;、&#8221;writeComplete&#8221;、&#8221;channelDisconnected&#8221;、&#8221;channelInterestChanged&#8221;。</p>
<p>     Netty中有个注释@interface ChannelPipelineCoverage，它表示被注释的ChannelHandler是否能添加到多个ChannelPipeline中，其可选的值是&#8221;all&#8221;和&#8221;one&#8221;。&#8221;all&#8221;表示ChannelHandler是无状态的，可被多个ChannelPipeline共享，而&#8221;one&#8221;表示ChannelHandler只作用于单个ChannelPipeline中。但ChannelPipelineCoverage只是个注释而已，并没有实际的检查作用。对于ChannelHandler是&#8221;all&#8221;还是&#8221;one&#8221;，还是根据逻辑需要而定。比如，像解码请求handler，因为可能解码的数据不完整，需要等待下一次读事件来了之后再继续解析，所以解码请求handler就需要是&#8221;one&#8221;的（否则多个Channel共享数据就乱了）。而像业务逻辑处理hanlder通常是&#8221;all&#8221;的。</p>
<p>     下面以一个简单的例子说明如何编写“解码请求数据、业务逻辑处理、编码响应”这一过程中涉及的ChannelHandler。该例子实现的协议格式很简单，请求和响应流中头4个字节表示后面跟的内容长度，根据该长度可得到内容体。</p>
<p>    首先看下解码器的实现：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MessageDecoder <span style="color: #000000; font-weight: bold;">extends</span> FrameDecoder <span style="color: #009900;">&#123;</span>
&nbsp;
    @Override
    <span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #003399;">Object</span> decode<span style="color: #009900;">&#40;</span>
            ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>buffer.<span style="color: #006633;">readableBytes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&lt;</span> <span style="color: #cc66cc;">4</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//(1)</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #000066; font-weight: bold;">int</span> dataLength <span style="color: #339933;">=</span> buffer.<span style="color: #006633;">getInt</span><span style="color: #009900;">&#40;</span>buffer.<span style="color: #006633;">readerIndex</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>buffer.<span style="color: #006633;">readableBytes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&lt;</span> dataLength <span style="color: #339933;">+</span> <span style="color: #cc66cc;">4</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//(2)</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        buffer.<span style="color: #006633;">skipBytes</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">4</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//(3)</span>
        <span style="color: #000066; font-weight: bold;">byte</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> decoded <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #000066; font-weight: bold;">byte</span><span style="color: #009900;">&#91;</span>dataLength<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        buffer.<span style="color: #006633;">readBytes</span><span style="color: #009900;">&#40;</span>decoded<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #003399;">String</span> msg <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">String</span><span style="color: #009900;">&#40;</span>decoded<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//(4)</span>
        <span style="color: #000000; font-weight: bold;">return</span> msg<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>    MessageDecoder继承自FrameDecoder，FrameDecoder是Netty codec包中的辅助类，它是个ChannelUpstreamHandler，decode方法是FrameDecoder子类需要实现的。在上面的代码中，有：</p>
<p>    (1)检查ChannelBuffer中的字节数，如果ChannelBuffer可读的字节数少于4,则返回null等待下次读事件。<br />
    (2)继续检查ChannelBuffer中的字节数，如果ChannelBuffer可读的字节数少于dataLength + 4，则返回null等待下次读事件。<br />
    (3)越过dataLength的字节。<br />
    (4)构造解码的字符串返回。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">@ChannelPipelineCoverage<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;all&quot;</span><span style="color: #009900;">&#41;</span>
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MessageServerHandler <span style="color: #000000; font-weight: bold;">extends</span> SimpleChannelUpstreamHandler <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> Logger logger <span style="color: #339933;">=</span> Logger.<span style="color: #006633;">getLogger</span><span style="color: #009900;">&#40;</span>
            MessageServerHandler.<span style="color: #000000; font-weight: bold;">class</span>.<span style="color: #006633;">getName</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    @Override
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> messageReceived<span style="color: #009900;">&#40;</span>
            ChannelHandlerContext ctx, MessageEvent e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>e.<span style="color: #006633;">getMessage</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">instanceof</span> <span style="color: #003399;">String</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000000; font-weight: bold;">return</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//(1)</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #003399;">String</span> msg <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#41;</span> e.<span style="color: #006633;">getMessage</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #003399;">System</span>.<span style="color: #006633;">err</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;got msg:&quot;</span><span style="color: #339933;">+</span>msg<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        e.<span style="color: #006633;">getChannel</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">write</span><span style="color: #009900;">&#40;</span>msg<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//(2)</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    @Override
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> exceptionCaught<span style="color: #009900;">&#40;</span>
            ChannelHandlerContext ctx, ExceptionEvent e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        logger.<span style="color: #006633;">log</span><span style="color: #009900;">&#40;</span>
                Level.<span style="color: #006633;">WARNING</span>,
                <span style="color: #0000ff;">&quot;Unexpected exception from downstream.&quot;</span>,
                e.<span style="color: #006633;">getCause</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        e.<span style="color: #006633;">getChannel</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">close</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>    MessageServerHandler是服务端业务处理handler，其继承自SimpleChannelUpstreamHandler，并主要实现messageReceived事件。关于该类，有如下注解：</p>
<p>    (1)该upstream事件流中，首先经过MessageDecoder，其会将decode返回的解码后的数据构造成 MessageEvent.getMessage()，所以在handler上下文关系中，MessageEvent.getMessage()并不一定都返回ChannelBuffer类型的数据。<br />
    (2)MessageServerHandler只是简单的将得到的msg再写回给客户端。e.getChannel().write(msg);操作将触发DownstreamMessageEvent事件，也就是调用下面的MessageEncoder将编码的数据返回给客户端。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">@ChannelPipelineCoverage<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;all&quot;</span><span style="color: #009900;">&#41;</span>
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MessageEncoder <span style="color: #000000; font-weight: bold;">extends</span> OneToOneEncoder <span style="color: #009900;">&#123;</span>
&nbsp;
    @Override
    <span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #003399;">Object</span> encode<span style="color: #009900;">&#40;</span>
            ChannelHandlerContext ctx, Channel channel, <span style="color: #003399;">Object</span> msg<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>msg <span style="color: #000000; font-weight: bold;">instanceof</span> <span style="color: #003399;">String</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000000; font-weight: bold;">return</span> msg<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//(1)</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #003399;">String</span> res <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#41;</span>msg<span style="color: #339933;">;</span>
        <span style="color: #000066; font-weight: bold;">byte</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> data <span style="color: #339933;">=</span> res.<span style="color: #006633;">getBytes</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000066; font-weight: bold;">int</span> dataLength <span style="color: #339933;">=</span> data.<span style="color: #006633;">length</span><span style="color: #339933;">;</span>
        ChannelBuffer buf <span style="color: #339933;">=</span> ChannelBuffers.<span style="color: #006633;">dynamicBuffer</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//(2)</span>
        buf.<span style="color: #006633;">writeInt</span><span style="color: #009900;">&#40;</span>dataLength<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        buf.<span style="color: #006633;">writeBytes</span><span style="color: #009900;">&#40;</span>data<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">return</span> buf<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//(3)</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>    MessageEncoder是个ChannelDownstreamHandler。对该类的注解如下：</p>
<p>    (1)如果编码的msg不是合法类型，就直接返回该msg，之后OneToOneEncoder会调用 ctx.sendDownstream(evt);来调用下一个ChannelDownstreamHandler。对于该例子来说，这种情况是不应该出现的。<br />
    (2)开发者创建ChannelBuffer的用武之地就是这儿了，通常使用dynamicBuffer即可，表示得到的ChannelBuffer可动态增加大小。<br />
    (3)返回编码后的ChannelBuffer之后，OneToOneEncoder会调用Channels.write将数据写回客户端。</p>
<h3>2.2、MessageServerPipelineFactory</h3>
<p>    创建了3个ChannelHandler，需要将他们注册到ChannelPipeline，而ChannelPipeline又是和Channel对应的（是全局单例还是每个Channel对应一个ChannelPipeline实例依赖于实现）。可以实现ChannelPipeline的工厂接口 ChannelPipelineFactory实现该目的。MessageServerPipelineFactory的代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MessageServerPipelineFactory <span style="color: #000000; font-weight: bold;">implements</span>
        ChannelPipelineFactory <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> ChannelPipeline getPipeline<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
        ChannelPipeline pipeline <span style="color: #339933;">=</span> pipeline<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        pipeline.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;decoder&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> MessageDecoder<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        pipeline.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;encoder&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> MessageEncoder<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        pipeline.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;handler&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> MessageServerHandler<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #000000; font-weight: bold;">return</span> pipeline<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<h3>2.3、MessageServer</h3>
<p>    服务端程序就剩下启动代码了，使用Netty的ServerBootstrap三下五除二完成之。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MessageServer <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">void</span> main<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> args<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// Configure the server.</span>
        ServerBootstrap bootstrap <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> ServerBootstrap<span style="color: #009900;">&#40;</span>
                <span style="color: #000000; font-weight: bold;">new</span> NioServerSocketChannelFactory<span style="color: #009900;">&#40;</span>
                        Executors.<span style="color: #006633;">newCachedThreadPool</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>,
                        Executors.<span style="color: #006633;">newCachedThreadPool</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #666666; font-style: italic;">// Set up the default event pipeline.</span>
        bootstrap.<span style="color: #006633;">setPipelineFactory</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> MessageServerPipelineFactory<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #666666; font-style: italic;">// Bind and start to accept incoming connections.</span>
        bootstrap.<span style="color: #006633;">bind</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> InetSocketAddress<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">8080</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>    稍加补充的是，该Server程序并不完整，它没有处理关闭时的资源释放，尽管暴力的来看并不一定需要做这样的善后工作。</p>
<h2>3、客户端程序</h2>
<p>    客户端程序和服务端程序处理模型上是很相似的，这里还是付上代码并作简要说明。</p>
<h3>3.1、 ChannelHandler</h3>
<p>    客户端是先发送数据到服务端（downstream事件流），然后是处理从服务端接收的数据（upstream事件流）。这里有个问题是，怎么把需要发送的数据送到downstream事件流里呢？这就用到了ChannelUpstreamHandler的channelConnected事件了。实现的 MessageClientHandler代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">@ChannelPipelineCoverage<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;all&quot;</span><span style="color: #009900;">&#41;</span>
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MessageClientHandler <span style="color: #000000; font-weight: bold;">extends</span> SimpleChannelUpstreamHandler <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> Logger logger <span style="color: #339933;">=</span> Logger.<span style="color: #006633;">getLogger</span><span style="color: #009900;">&#40;</span>
            MessageClientHandler.<span style="color: #000000; font-weight: bold;">class</span>.<span style="color: #006633;">getName</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
&nbsp;
    @Override
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> channelConnected<span style="color: #009900;">&#40;</span>
            ChannelHandlerContext ctx, ChannelStateEvent e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #003399;">String</span> message <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;hello kafka0102&quot;</span><span style="color: #339933;">;</span>
        e.<span style="color: #006633;">getChannel</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">write</span><span style="color: #009900;">&#40;</span>message<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    @Override
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> messageReceived<span style="color: #009900;">&#40;</span>
            ChannelHandlerContext ctx, MessageEvent e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// Send back the received message to the remote peer.</span>
        <span style="color: #003399;">System</span>.<span style="color: #006633;">err</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;messageReceived send message &quot;</span><span style="color: #339933;">+</span>e.<span style="color: #006633;">getMessage</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">try</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #003399;">Thread</span>.<span style="color: #006633;">sleep</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1000</span><span style="color: #339933;">*</span><span style="color: #cc66cc;">3</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">Exception</span> ex<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            ex.<span style="color: #006633;">printStackTrace</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        e.<span style="color: #006633;">getChannel</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">write</span><span style="color: #009900;">&#40;</span>e.<span style="color: #006633;">getMessage</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    @Override
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> exceptionCaught<span style="color: #009900;">&#40;</span>
            ChannelHandlerContext ctx, ExceptionEvent e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// Close the connection when an exception is raised.</span>
        logger.<span style="color: #006633;">log</span><span style="color: #009900;">&#40;</span>
                Level.<span style="color: #006633;">WARNING</span>,
                <span style="color: #0000ff;">&quot;Unexpected exception from downstream.&quot;</span>,
                e.<span style="color: #006633;">getCause</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        e.<span style="color: #006633;">getChannel</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">close</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>    对于编码和解码Handler，复用MessageEncoder和MessageDecoder即可。</p>
<h3>3.2、 MessageClientPipelineFactory</h3>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MessageClientPipelineFactory <span style="color: #000000; font-weight: bold;">implements</span>
        ChannelPipelineFactory <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> ChannelPipeline getPipeline<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
        ChannelPipeline pipeline <span style="color: #339933;">=</span> pipeline<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        pipeline.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;decoder&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> MessageDecoder<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        pipeline.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;encoder&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> MessageEncoder<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        pipeline.<span style="color: #006633;">addLast</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;handler&quot;</span>, <span style="color: #000000; font-weight: bold;">new</span> MessageClientHandler<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #000000; font-weight: bold;">return</span> pipeline<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<h3>3.3、MessageClient</h3>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> MessageClient <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">void</span> main<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> args<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// Parse options.</span>
        <span style="color: #003399;">String</span> host <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;127.0.0.1&quot;</span><span style="color: #339933;">;</span>
        <span style="color: #000066; font-weight: bold;">int</span> port <span style="color: #339933;">=</span> <span style="color: #cc66cc;">8080</span><span style="color: #339933;">;</span>
        <span style="color: #666666; font-style: italic;">// Configure the client.</span>
        ClientBootstrap bootstrap <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> ClientBootstrap<span style="color: #009900;">&#40;</span>
                <span style="color: #000000; font-weight: bold;">new</span> NioClientSocketChannelFactory<span style="color: #009900;">&#40;</span>
                        Executors.<span style="color: #006633;">newCachedThreadPool</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>,
                        Executors.<span style="color: #006633;">newCachedThreadPool</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #666666; font-style: italic;">// Set up the event pipeline factory.</span>
        bootstrap.<span style="color: #006633;">setPipelineFactory</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> MessageClientPipelineFactory<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #666666; font-style: italic;">// Start the connection attempt.</span>
        ChannelFuture future <span style="color: #339933;">=</span> bootstrap.<span style="color: #006633;">connect</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> InetSocketAddress<span style="color: #009900;">&#40;</span>host, port<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #666666; font-style: italic;">// Wait until the connection is closed or the connection attempt fails.</span>
        future.<span style="color: #006633;">getChannel</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">getCloseFuture</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">awaitUninterruptibly</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #666666; font-style: italic;">// Shut down thread pools to exit.</span>
        bootstrap.<span style="color: #006633;">releaseExternalResources</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>    在写客户端例子时，我想像的代码并不是这样的，对客户端的代码我也没做过多的研究，所以也可能没有找到更好的解决方案。在上面的例子中，bootstrap.connect方法中会触发实际的连接操作，接着触发 MessageClientHandler.channelConnected，使整个过程运转起来。但是，我想要的是一个连接池，并且如何写数据也不应该在channelConnected中，这样对于动态的数据，只能在构造函数中传递需要写的数据了。但到现在，我还不清楚如何将连接池和 ChannelPipeline有效的结合起来。或许，这样的需求可以跨过Netty来实现。</p>
<h2>4、总结</h2>
<p>    关于Netty的初步使用，尚且总结到这里。关于这篇文章，写得断断续续，以至于到后来我都没兴趣把内容都整理出来。当然，这多少也是因为我是先整理 Netty原理方面的东西所致。我也只能卑微的期望，该文对Netty入门者会有些许帮助。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/06/161.html/feed</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
	</channel>
</rss>

