Netty是一个NIO client-server框架。Netty提供了高层次的抽象来简化TCP和UDP服务器的编程。
Netty框架由三部分组成:Transport Services, Protocol Support以及Core。
Transport Services由Socket Datagram,HTTP Tunnel以及In-VM Pipe组成。
Protocol Support由Http & WebSocket, SSL-StartTLS,Google Protobuf,zlib/gzip Compression,Large File Transfer,RTSP以及Legacy Text-Binary Protocols with Unit Testability组成。
Core由Extensible Event Model,Universal Communication API和Zero-Copy-Capable Rich Byte Buffer组成。
整个Netty的API都是异步的。
Callbacks(回调)
一个回调是被传递到并且执行完该方法。回调有个问题是当使用链式调用很多不同的方法会导致线性代码。
public class Worker{ public void doWork(){ Fecher fetcher = new MyFetcher(new Data(1, 0)); fetcher.fetchData(new FetcherCallback(){ public void onError(Throwable cause){ System.out.println("An error accour: " + cause.getMessage()); } public void onData(Data data){ System.out.println("Data received : " + data); } }); } } public interface Fetcher{ void fetchData(FetcherCallback callback); } public class MyFetch implements Fetch{ final Data data; public MyFetch(Data data){ this.data = data; } public void fetchData(FetcherCallback callback){ try{ callback.onData(data); }catch(Exception e){ callback.onError(e); } } } public interface FetcherCallback{ void onData(Data data) throw Exception; void onError(Throwable cause); } public class Data{ private int n; private int m; public Data(int n, int m){ this.n = n; this.m = m; } }
Futures
Futures是一个抽象的概念,它表示一个值,该值可能在某一点变得可用。一个Future可以获得计算完的结果,要么获得计算失败后的异常。Java在java.util.concurrent包中附带了Future接口,它使用Executor异步执行。
public class FutureExample{ public static void main(String args[]) throws Exception{ ExecutorService executor = Executors.newCachedThreadPool(); Runnable task = new Runnable(){ public void run(){ System.out.println("task 1"); } }; Callable<Integer> task2 = new Callable<Integer>(){ public Integer call() throws Exception{ return new Integer(100); } }; Future<?> f1 = executor.sumbit(task1); Future<?> f2 = executor.sumbit(task2); System.out.println("task1 is completed? " + f1.isDone()); System.out.println("task2 is completed? " + f2.isDone()); while(fi.isDone){ System.out.println("task1 completed"); break; } while(f2.isDone()){ System.out.println("return value by task2 :" + f2.get()); break; } } }
NIO是一个比较底层的APIs,他依赖于操作系统的IO APIS。Java实现了统一的接口来操作IO,其在所有操作系统中的工作行为是一样的。
ByteBuffer是一个数据容器。ByteBuffer允许包装一个byte[]来获得一个实例。Netty通过一些APIs对ByteBuffer进行构造,使用和操作,以此来解决NIO中的一些限制。
很多Channel的实现支持Gather和Scatter。这个功能允许从多个ByteBuffer中读入或写入到多个ByteBuffer中,以提升性能。操作系统底层知道如何处理这些被写入/读出。若要分割的数据在多个不同的ByteBuffer中,使用Gather/Scatter是比较好的方式。Gather/Scatter功能会导致内存泄漏,需在JDK1.7版本以上使用。
一个简单的demo
Netty服务器
一个Netty服务器主要有两部分组成:配置服务器功能(如线程,端口)以及实现服务器处理程序,它包含业务逻辑,决定当有一个请求连接或接收数据时该做什么。
启动服务器应先创建一个ServerBootstrap对象,指定NioEventLoopGroup来接受和处理新连接,指定通道类型为NioServerSocketChannel,设置InetSocketAddress让服务器监听某个端口已等待客户端连接。调用childHandler指定连接后调用的ChannelHandler,该方法接受一个ChannelInitializer类型的参数,实现其initChannel方法,该方法用来设置ChannelHandler。直到绑定完成调用sync()方法会阻塞直到服务器完成绑定,然后等待通道关闭。
Netty客户端
一个Netty的客户端由连接服务器,写数据到服务器,等待接受服务器返回相同的数据和关闭连接四个步骤组成。
创建一个客户端包含:创建Bootstrap对象用来引导启动客户端;创建EventLoopGroup对象并设置到Bootstrap中,EventLoopGroup可以理解为一个线程池,这个线程池用来处理连接,接收数据,发送数据;创建InetSocketAddress并设置到Boostrap中,InetSocketAddress是指定连接的服务器地址;添加一个ChannelHandler,客户端成功连接服务器后就会被执行;调用Bootstrap.connect()来连接服务器;最后关闭EventLoopGroup来释放资源。
使用SimpleChannelInboundHandler的ChannelHandler来处理业务,通过重写父类的三个方法来处理感兴趣的事件:channelActive():客户端连接服务器后被调用;channelRead():从服务器接收到数据后调用;exceptionCaught():发生异常时被调用