异步网络编程框架-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有个简单的认识。
Netty is an synchronous event-driven network application framework for rapid development of maintainable high performance protocol servers and clients
这句话有几个要点:异步、事件驱动、网络开发、服务器端、客户端、高性能协议、框架。
这句话里面要点挺多,理解了这句话,至少不会把Netty和Jetty相互比较。这两个有区别吗?没有区别吗?没有区别吧!到底有没有区别?JJ和NN的区别吧!这个也叫区别?……
好吧,又调皮了吧。我来阐述下这几个要点:异步和同步的区别大家都知道,另外关于异步和同步的优缺点也要了解下;事件驱动,这个不错,怎么做到的呢?可以用来进行网络应用的开发;哟,客户端加服务器端,不错,通吃;高性能协议嘛,其实这个协议需要自己定义,内置PCP/IP协议;Netty也就是个个异步框架,能够用来进行Server端和Client端的网络应用开发。框架,一定要记住框架。
Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.
简化TCP和UDP socket server开发。
'Quick and easy' doesn't mean that a resulting application will suffer from a maintainability or a performance issue. Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols. As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise.
借鉴FTP、SMTP、HTTP协议实现,在易开发、高性能、稳定、灵活上没有多少妥协,也即是说Netty具有这些好用功能。
这张图是从官网上面接下来的,需要仔细看下。这张图展示了Netty的构架,下面逐一分析:
最下面是Netty的核心core:具有ZeroCopy特性的ByteBuffer,其实就是ChannelBuffer里面的操作,所有Netty的Server和Client端的传输,最终都是byte传输,也就是说必须得经过ChannelBuffer的操作,转化成二进制传输;Universal Communication API,这个是Netty的核心底层内容;还有Extensible Event Model。这些都是底层的东西,已经封装好,仅供拿来使用或者只需要修改自己感兴趣的内容即可。
右上角是跟跟传输相关的协议:最下面是Legacy-Text传统文本协议,也就是二进制协议,这个实际上是传输的协议;上面的各种协议都是基于文本协议的操作,如HTTP、WebSocket、zlib/gzip压缩、SSL(https)、Google-Protobuf、Large-File、RTSP(Real Time Streaming Protocol)传输多媒体数据。这些协议是内置的,如果自己有需要,也可以定制自己的传输方式。
左上角是传输层支持的内容:Pipeline、Tunnel、Datagram&Socket,这些都是传输层支持的内容,如果开发遇到的话,可以仔细阅读下源码
Netty还有哪些优点,还有哪些是作者想告诉我们的呢?
Netty was designed and written from scratch to provide the best experience in network application development:
Design
Unified API for various transport types - blocking and non-blocking socket
Based on a flexible and extensible event model which allows clear separation of concerns
Highly customizable thread model - single thread, one or more thread pools such as SEDA
True connectionless datagram socket support (since 3.1)
Ease of use
Well-documented Javadoc, user guide and examples,这句话深表赞同
No additional dependencies but JDK 1.5 (or above)
PerformanceBetter throughput, lower latency
Less resource consumption
Minimized unnecessary memory copy,这个分析完源码也会表示赞同的
Robustness
No more OutOfMemoryError due to fast, slow or overloaded connection.我写的程序从来不说No More
No more unfair read / write ratio often found in a NIO application under high speed network
Security
Complete SSL/TLS and StartTLS support
Runs OK in a restricted environment such as Applet
Community
Release early, release often对作者的勤奋表示感谢
The author has been writing similar frameworks since 2003 and he still finds your feed back precious!
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; } }}