gary00 2012-11-02
项目一直在用Mina,了解到Netty也是异步编程框架,就了解了一下,记录学习过程。另外Mina项目和Netty项目的创始人也有关系,大家可以自行度之。
目前Netty的稳定Final版本是3.5.9Final,官网上放出的最新代码时4.0.0.Alpha6,目前看作者正在用力的开发,什么时候能把Alpha去掉才算大功告成。大家如果准备升级,可以参考官网上的指导。
Netty版本从3.5.x到4.x版本应该是个巨大的进步,3.x版本将源码都放到一个包里面,通过package来区分,而4.x版本则根据功能分jar包,细分为codec、transport、handler、buffer、common等包,用户使用时可以引用单个jar包,也可以只引用自己需要的jar包,这个是功能内聚。另外这样实际上是提供了Netty的接口层,用户扩展可以针对某个包进行扩展,添加自己的功能。最有可能被用户单独访问的是那个buffer包,关于buffer的操作有很大的进步,详情会在后面的系列中分析。
在这里吐槽下Netty的版本升级,升级之后包名的前缀都变了,由org.jboss.netty变为io.netty,这个变化对于升级应用特别坑啊。或者作者有别的考虑,想区分出来以前的功能?反正想升级,第一步先改包引用吧。不过升级后的编程方式也有变化,应该是重构了吧,改进不小。顺便说下,这种切分jar包升级在好多开源项目上都有体现,Spring、HttpClient、Hadoop等
学习一个新的框架,肯定是先看官网上怎么介绍,打开netty.io,进入首页,可以看到简述,能够对Netty有个简单的认识。
Nettyisansynchronousevent-drivennetworkapplicationframeworkforrapiddevelopmentofmaintainablehighperformanceprotocolserversandclients
这句话有几个要点:异步、事件驱动、网络开发、服务器端、客户端、高性能协议、框架。
这句话里面要点挺多,理解了这句话,至少不会把Netty和Jetty相互比较。这两个有区别吗?没有区别吗?没有区别吧!到底有没有区别?JJ和NN的区别吧!这个也叫区别?……
好吧,又调皮了吧。我来阐述下这几个要点:异步和同步的区别大家都知道,另外关于异步和同步的优缺点也要了解下;事件驱动,这个不错,怎么做到的呢?可以用来进行网络应用的开发;哟,客户端加服务器端,不错,通吃;高性能协议嘛,其实这个协议需要自己定义,内置PCP/IP协议;Netty也就是个个异步框架,能够用来进行Server端和Client端的网络应用开发。框架,一定要记住框架。
NettyisaNIOclientserverframeworkwhichenablesquickandeasydevelopmentofnetworkapplicationssuchasprotocolserversandclients.ItgreatlysimplifiesandstreamlinesnetworkprogrammingsuchasTCPandUDPsocketserver.
简化TCP和UDPsocketserver开发。
'Quickandeasy'doesn'tmeanthataresultingapplicationwillsufferfromamaintainabilityoraperformanceissue.NettyhasbeendesignedcarefullywiththeexperiencesearnedfromtheimplementationofalotofprotocolssuchasFTP,SMTP,HTTP,andvariousbinaryandtext-basedlegacyprotocols.Asaresult,Nettyhassucceededtofindawaytoachieveeaseofdevelopment,performance,stability,andflexibilitywithoutacompromise.
借鉴FTP、SMTP、HTTP协议实现,在易开发、高性能、稳定、灵活上没有多少妥协,也即是说Netty具有这些好用功能。
这张图是从官网上面接下来的,需要仔细看下。这张图展示了Netty的构架,下面逐一分析:
最下面是Netty的核心core:具有ZeroCopy特性的ByteBuffer,其实就是ChannelBuffer里面的操作,所有Netty的Server和Client端的传输,最终都是byte传输,也就是说必须得经过ChannelBuffer的操作,转化成二进制传输;UniversalCommunicationAPI,这个是Netty的核心底层内容;还有ExtensibleEventModel。这些都是底层的东西,已经封装好,仅供拿来使用或者只需要修改自己感兴趣的内容即可。
右上角是跟跟传输相关的协议:最下面是Legacy-Text传统文本协议,也就是二进制协议,这个实际上是传输的协议;上面的各种协议都是基于文本协议的操作,如HTTP、WebSocket、zlib/gzip压缩、SSL(https)、Google-Protobuf、Large-File、RTSP(RealTimeStreamingProtocol)传输多媒体数据。这些协议是内置的,如果自己有需要,也可以定制自己的传输方式。
左上角是传输层支持的内容:Pipeline、Tunnel、Datagram&Socket,这些都是传输层支持的内容,如果开发遇到的话,可以仔细阅读下源码
Netty还有哪些优点,还有哪些是作者想告诉我们的呢?
Nettywasdesignedandwrittenfromscratchtoprovidethebestexperienceinnetworkapplicationdevelopment:
Design
UnifiedAPIforvarioustransporttypes-blockingandnon-blockingsocket
Basedonaflexibleandextensibleeventmodelwhichallowsclearseparationofconcerns
Highlycustomizablethreadmodel-singlethread,oneormorethreadpoolssuchasSEDA
Trueconnectionlessdatagramsocketsupport(since3.1)
Easeofuse
Well-documentedJavadoc,userguideandexamples,这句话深表赞同
NoadditionaldependenciesbutJDK1.5(orabove)
PerformanceBetterthroughput,lowerlatency
Lessresourceconsumption
Minimizedunnecessarymemorycopy,这个分析完源码也会表示赞同的
Robustness
NomoreOutOfMemoryErrorduetofast,sloworoverloadedconnection.我写的程序从来不说NoMore
Nomoreunfairread/writeratiooftenfoundinaNIOapplicationunderhighspeednetwork
Security
CompleteSSL/TLSandStartTLSsupport
RunsOKinarestrictedenvironmentsuchasApplet
Community
Releaseearly,releaseoften对作者的勤奋表示感谢
Theauthorhasbeenwritingsimilarframeworkssince2003andhestillfindsyourfeedbackprecious!
Netty的用户手册还是很全的,不过有各种应用的例子,就是没有helloworld,这个不符合作为一个程序员的入门教程,我来写个helloworld,作为Netty入门的手册。
Netty是区分Server和Client的。Server启动服务后,提供端口供Client连接,两者通过网络流进行通信。实际底层过程就是Socket的连接过程,包括open—>bindconnected,断开连接是相反的流程disconnect—>unbindclosed
好吧,看完上面的内容,你应该已经了解了Netty的地位,我们先看下helloworld的Server端代码:public class Server { public static void main ( String[] args ) { Executor bossExecutor = Executors.newCachedThreadPool(); Executor workerExecutor = Executors.newCachedThreadPool(); NioServerSocketChannelFactory serverSocketChannelFactory = new NioServerSocketChannelFactory(bossExecutor, workerExecutor); ServerBootstrap serverBootstrap = new ServerBootstrap(serverSocketChannelFactory); ServerPipelineFactory pipelineFactory = new ServerPipelineFactory(); serverBootstrap.setPipelineFactory(pipelineFactory); serverBootstrap.bind(new InetSocketAddress(8080)); } private static class ServerHandler extends SimpleChannelUpstreamHandler { public void messageReceived ( ChannelHandlerContext ctx, MessageEvent e ) throws Exception { System.out.println("client says:" + e.getMessage()); e.getChannel().write(new String("Hello client, you say " + e.getMessage() + "?\r\n")); } } private static class ServerPipelineFactory implements ChannelPipelineFactory { public ChannelPipeline getPipeline () throws Exception { ChannelPipeline pipeline = pipeline(); pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast("handler", new ServerHandler()); return pipeline; } } }Client代码:
public class Client { public static void main ( String[] args ) throws IOException, InterruptedException { Executor bossExecutor = Executors.newCachedThreadPool(); Executor workerExecutor = Executors.newCachedThreadPool(); NioClientSocketChannelFactory clientSocketChannelFactory = new NioClientSocketChannelFactory(bossExecutor, workerExecutor); ClientBootstrap clientBootstrap = new ClientBootstrap(clientSocketChannelFactory); ClientPipelineFactory pipelineFactory = new ClientPipelineFactory(); clientBootstrap.setPipelineFactory(pipelineFactory); ChannelFuture future = clientBootstrap.connect(new InetSocketAddress(8080)); Channel channel = future.awaitUninterruptibly().getChannel(); if (!future.isSuccess()) { future.getCause().printStackTrace(); clientBootstrap.releaseExternalResources(); return; } if(channel.isWritable()){ ChannelFuture lastWriteFuture = channel.write("wtf-gfw\r\n"); Thread.sleep(1000); if(lastWriteFuture.isSuccess()){ lastWriteFuture.addListener(ChannelFutureListener.CLOSE); } } channel.close().awaitUninterruptibly(); clientBootstrap.releaseExternalResources(); } private static class ClientHandler extends SimpleChannelUpstreamHandler { public void messageReceived ( ChannelHandlerContext ctx, MessageEvent e ) throws Exception { System.out.println(e.getMessage()); } } private static class ClientPipelineFactory implements ChannelPipelineFactory { public ChannelPipeline getPipeline () throws Exception { ChannelPipeline pipeline = pipeline(); pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast("handler", new ClientHandler()); return pipeline; } } }
先启动server端,再启动client端,即可看到结果。
这个c/s实例作为helloworld入门其实是不太合适的,不过从结果来看,能够输出c/s的交互信息,有作为Server和client启动运行的各个要素,也算是一个不错的入门吧。
一直说Netty是基于时间驱动的,但是到底是怎么实现的呢?其实Server启动后,Client的连接过程就是一系列事件,包括接收client的连接过程都是事件,open、bound、connected。再准确是说句,Server和Client的自身启动也是一系列事件,handler根据不同的事件进行不同的处理,这样理解事件驱动会更深入点。
Ok,启动没有问题,有人会抱怨,NettyDemo里面的Server启动只要三行就行,你这个也太多了吧。其实我这个是将Demo里面出现的概念给抽出来,Demo里面也有这些东西,就是没有引用而已。Server里面的BootStrap、ChannelFactory、Handler、Channel、ChannelFuture都是什么概念呢?