异步网络编程框架-Netty介绍

项目一直在用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—>bindconnected,断开连接是相反的流程disconnect—>unbindclosed

    好吧,看完上面的内容,你应该已经了解了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都是什么概念呢?

相关推荐