Netty 是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器和客户端java
早期的 Java API 只支持由本地系统套接字库提供的所谓的阻塞函数,下面的代码展现了一个使用传统 Java API 的服务器代码的普通示例编程
// 建立一个 ServerSocket 用以监听指定端口上的链接请求 ServerSocket serverSocket = new ServerSocket(5000); // 对 accept 方法的调用将被阻塞,直到一个链接创建 Socket clientSocket = serverSocket.accept(); // 这些流对象都派生于该套接字的流对象 BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); String request, response; // 客户端发送了 "Done" 则退出循环 while ((request = in.readLine()) != null) { if ("Done".equals(request)) { break; } // 请求被传递给服务器的处理方法 response = proce***equest(request); // 服务器的响应被发送给客户端 out.println(response); }
这段代码只能同时处理一个链接,要管理多个客户端,就要为每一个新的客户端 Socket 建立一个新的 Thread,让咱们来考虑一下这种方案的影响:安全
这种并发方案对于中小数量的客户端还算理想,但不能很好地支持更大的并发,幸运的是,还有另外一种解决方案服务器
NIO(Non-blocking I/O,也称 New I/O),是一种同步非阻塞的 I/O 模型,也是 I/O 多路复用的基础。传统的 IO 流是阻塞的,这意味着,当一个线程调用读或写操做时,线程将被阻塞,直至数据被彻底读取或写入。NIO 的非阻塞模式,使一个线程进行读或写操做时,若是目前无数据可用时,就不作操做,而不是保持线程阻塞,因此直至数据就绪之前,线程能够继续作其余事情网络
class java.nio.channels.Selector 是 Java 非阻塞 IO 实现的关键。它使用事件通知 API 以肯定在一组非阻塞套接字中有哪些已经就绪并能进行 IO 相关操做。由于能够在任什么时候间点任意检查读操做或写操做的完成状况,因此单一线程能够处理多个并发的链接并发
与阻塞 IO 模型相比,这种模型提供了更好的资源管理:框架
尽管 Java NIO 如此高效,但要作到正确和安全并不容易,在高负载下可靠和高效地处理和调度 IO 操做是一项烦琐且容易出错的任务,所幸,有 Netty 能帮助咱们解决问题异步
Netty 是一个普遍使用的 Java 网络编程框架,它隐藏了 Java 高级 API 背后的复杂性,提供一个易于使用的 API 的客户端/服务器框架。在这里咱们将要讨论 Netty 的主要构件:ide
Channel 是 Java NIO 的一个基本构造,能够把 Channel 简单看做是传入(入站)或传出(出站)数据的载体。所以,它能够被打开或关闭,链接或断开链接异步编程
一个回调其实就是一个方法,Netty 使用回调来处理事件,当一个回调被触发时,相关的事件能够被 ChannelHandler 的实现处理。下面的代码展现了这样一个例子:当一个新的链接已经创建,ChannelHandler 的 channelActive() 的回调方法将会被调用,并打印出一条信息
public class ConnectHandler extends ChannelInboundHandlerAdapter { @override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("Client " + ctx.channel().remoteAddress() + " connected"); } }
Future 提供了另外一种在操做完成时通知应用程序的方式,它将在将来的某个时刻完成,并提供对其结果的访问。虽然 Java 提供了 Future 的一种实现,但须要手动检查对应操做是否已经完成,或一直阻塞直到它完成,十分烦琐。Netty 提供了本身的实现 ChannelFuture,用于执行异步操做的时候使用
ChannelFuture 提供了几种额外的方法,这些方法使得咱们可以注册一个或多个 ChannelFutureListener 实例。监听器的回调方法 operationComplete() 将会在对应操做完成时被调用。每一个 Netty 的 IO 操做都会返回一个 ChannelFuture,它们都不会被阻塞,能够同时作其余工做,更加有效的利用资源
Channel channel = ...; // 异步地链接到远程结点 ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25)); // 注册一个 ChannelFutureListener future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { // 若是操做成功 if(future.isSuccess()) { ... } else { // 发生异常 ... } } });
Netty 使用不一样的事件来触发合适的动做,事件是按照与入站或出站数据流的相关性进行分类的,可能由入站数据或相关状态更改而触发的事件包括:
出站事件是将来将会触发的某个动做的操做结果,包括:
每一个事件均可分发给 ChannelHandler 类中的某个用户实现的方法,下图展现了一个事件如何被一个 ChannelHandler 链处理
Netty 提供了大量预约义的 ChannelHandler 实现,供开发者使用
Netty 的异步编程模型是创建在 Future 和回调的概念之上的,将事件派发到 ChannelHandler 拦截并高速地转换入站数据和出站数据,开发者只须要提供回调或者利用返回的 Future 便可。Netty 经过触发事件将 Selector 从应用程序中抽象出来,消除了本需手写的派发代码。在内部,将会为每一个 Channel 分配一个 EventLoop,用于处理全部事件,包括:
EventLoop 自己只有一个线程驱动,处理了一个 Channel 的全部 IO 事件,这个简单而强大的设计消除了可能在 ChannelHandler 实现中须要进行同步的任何顾虑,所以咱们只需专一于提供正确的逻辑便可