感谢网友【黄亿华】投递本稿。
<code></code>大概用netty的,无论新手还是老手,都知道它是一个“网络通讯框架”。所谓框架,基本上都是一个作用:基于底层api,提供更便捷的编程模型。那么”通讯框架”到底做了什么事情呢?回答这个问题并不太容易,我们不妨反过来看看,不使用netty,直接基于nio编写网络程序,你需要做什么(以server端tcp连接为例,这里我们使用reactor模型):
监听端口,建立socket连接
建立线程,处理内容
读取socket内容,并对协议进行解析
进行逻辑处理
回写响应内容
如果是多次交互的应用(smtp、ftp),则需要保持连接多进行几次交互
关闭连接
建立线程是一个比较耗时的操作,同时维护线程本身也有一些开销,所以我们会需要多线程机制,幸好jdk已经有很方便的多线程框架了,这里我们不需要花很多心思。 此外,因为tcp连接的特性,我们还要使用连接池来进行管理:
建立tcp连接是比较耗时的操作,对于频繁的通讯,保持连接效果更好
对于并发请求,可能需要建立多个连接
维护多个连接后,每次通讯,需要选择某一可用连接
连接超时和关闭机制
想想就觉得很复杂了!实际上,基于nio直接实现这部分东西,即使是老手也容易出现错误,而使用netty之后,你只需要关注逻辑处理部分就可以了。
这里我们引用netty的example包里的一个例子,一个简单的echoserver,它接受客户端输入,并将输入原样返回。其主要代码如下:
<code>01</code>
<code>public</code> <code>void</code> <code>run() {</code>
<code>02</code>
<code>// configure the server.</code>
<code>03</code>
<code>serverbootstrap bootstrap =</code><code>new</code> <code>serverbootstrap(</code>
<code>04</code>
<code>new</code> <code>nioserversocketchannelfactory(</code>
<code>05</code>
<code>executors.newcachedthreadpool(),</code>
<code>06</code>
<code>executors.newcachedthreadpool()));</code>
<code>07</code>
<code>08</code>
<code>// set up the pipeline factory.</code>
<code>09</code>
<code>bootstrap.setpipelinefactory(</code><code>new</code> <code>channelpipelinefactory() {</code>
<code>10</code>
<code>public</code> <code>channelpipeline getpipeline()</code><code>throws</code> <code>exception {</code>
<code>11</code>
<code>return</code> <code>channels.pipeline(</code><code>new</code> <code>echoserverhandler());</code>
<code>12</code>
<code>}</code>
<code>13</code>
<code>});</code>
<code>14</code>
<code>15</code>
<code>// bind and start to accept incoming connections.</code>
<code>16</code>
<code>bootstrap.bind(</code><code>new</code> <code>inetsocketaddress(port));</code>
<code>17</code>
这里<code>echoserverhandler</code>是其业务逻辑的实现者,大致代码如下:
<code>1</code>
<code>public</code> <code>class</code> <code>echoserverhandler</code><code>extends</code> <code>simplechannelupstreamhandler {</code>
<code>2</code>
<code>3</code>
<code> </code><code>@override</code>
<code>4</code>
<code> </code><code>public</code> <code>void</code> <code>messagereceived(</code>
<code>5</code>
<code> </code><code>channelhandlercontext ctx, messageevent e) {</code>
<code>6</code>
<code> </code><code>// send back the received message to the remote peer.</code>
<code>7</code>
<code> </code><code>e.getchannel().write(e.getmessage());</code>
<code>8</code>
<code> </code><code>}</code>
<code>9</code>
还是挺简单的,不是吗?
完成了以上一段代码,我们算是与netty进行了第一次亲密接触。如果想深入学习呢?
理解netty的关键点在哪呢?我觉得,除了nio的相关知识,另一个就是事件驱动的设计思想。什么叫事件驱动?我们回头看看<code>echoserverhandler</code>的代码,其中的参数:<code>public void messagereceived(channelhandlercontext ctx, messageevent e)</code>,messageevent就是一个事件。这个事件携带了一些信息,例如这里<code>e.getmessage()</code>就是消息的内容,而<code>echoserverhandler</code>则描述了处理这种事件的方式。一旦某个事件触发,相应的handler则会被调用,并进行处理。这种事件机制在ui编程里广泛应用,而netty则将其应用到了网络编程领域。
在netty里,所有事件都来自<code>channelevent</code>接口,这些事件涵盖监听端口、建立连接、读写数据等网络通讯的各个阶段。而事件的处理者就是<code>channelhandler</code>,这样,不但是业务逻辑,连网络通讯流程中底层的处理,都可以通过实现<code>channelhandler</code>来完成了。事实上,netty内部的连接处理、协议编解码、超时等机制,都是通过handler完成的。当博主弄明白其中的奥妙时,不得不佩服这种设计! 下图描述了netty进行事件处理的流程。<code>channel</code>是连接的通道,是channelevent的产生者,而<code>channelpipeline</code>可以理解为channelhandler的集合。

理解了netty的事件驱动机制,我们现在可以来研究netty的各个模块了。netty的包结构如下:
<code>org</code>
<code> </code><code>└── jboss</code>
<code> </code><code>└── netty</code>
<code> </code><code>├── bootstrap 配置并启动服务的类</code>
<code> </code><code>├── buffer 缓冲相关类,对nio buffer做了一些封装</code>
<code> </code><code>├── channel 核心部分,处理连接</code>
<code> </code><code>├── container 连接其他容器的代码</code>
<code> </code><code>├── example 使用示例</code>
<code> </code><code>├── handler 基于handler的扩展部分,实现协议编解码等附加功能</code>
<code> </code><code>├── logging 日志</code>
<code> </code><code>└── util 工具类</code>
在这里面,<code>channel</code>和<code>handler</code>两部分比较复杂。我们不妨与netty官方的结构图对照一下,来了解其功能。
zero-copy-capable rich byte buffer 零拷贝的buffer。为什么叫零拷贝?因为在数据传输时,最终处理的数据会需要对单个传输层的报文,进行组合或者拆分。nio原生的bytebuffer要做到这件事,需要对bytebuffer内容进行拷贝,产生新的bytebuffer,而netty通过提供composite(组合)和slice(切分)两种buffer来实现零拷贝。这部分代码在<code>org.jboss.netty.buffer</code>包中。
universal communication api 统一的通讯api。因为java的old i/o和new i/o,使用了互不兼容的api,而netty则提供了统一的api(<code>org.jboss.netty.channel.channel</code>)来封装这两种i/o模型。这部分代码在<code>org.jboss.netty.channel</code>包中。