Netty in action—Netty简介

来源:互联网 发布:淘宝能买到色情光碟吗 编辑:程序博客网 时间:2024/06/11 18:54

引言

这是《Netty实战》的读书笔记

java的网络编程

int portNumber = 8000;try {    serverSocket = new ServerSocket(portNumber);    Socket clientSocket = serverSocket.accept();//block until a connnection is established    BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));    //得到clientSocket的输出流    PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);    String request,response;    while( (request = in.readLine())!=null){        if("Done".equals(request)){            break;        }        response = processRequest(request);//处理request,生成response        out.println(response);    }} catch (IOException e) {    e.printStackTrace();}

NIO

Java通过NIO支持非阻塞IO。
Selectors

Selector是Java非阻塞IO实现的关键。它使用时间通知API(和一系列非阻塞sockets)来告知IO准备好了。
通过一个线程就能处理并发的连接。

Netty简介

Netty特性总结

分类 Netty特性 设计 为多种网络传输类型提供统一的API,包括阻塞式和非阻塞式;简单但是强大的线程模型;支持真正无连接datagram socket;链接逻辑组件以支持复用 易用性 大量的Javadoc和丰富的demo;JDK1.7就够了 性能 更好的吞吐量和更低的延迟;通过线程池复用线程来减少资源消耗;减少内存复制的消耗 健壮性 不会因过慢过快或者超载的连接而导致的OutOfMemoryError;消除了在高速网络中NIO应用常见的不公平读写比率 安全性 完全支持SSL/TLS和StartTLS;可用于受限环境下,如Applet或OSGI 社区驱动 发布频繁且快速

异步和事件驱动
异步和可扩展性的联系:

  • 非阻塞网络调用使得我们可以不必等待一个操作的完成。Netty的完全异步的I/O正是基于这个特性构建的,并且更进一步:异步方法会立即返回,并且在它完成时,会直接或者在稍后的某个时间点通知用户
  • 选择器使得我们能够通过较少的线程便可监视许多连接上的事件

Netty的核心组件

核心组件有:

  • Channels
  • 回调(Callback)
  • Futures
  • 事件(Event)和处理器(handler)

Channels
Channel是Java NIO的一个基本组成元素。它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作(读或者写)的程序组件)的开放连接(open connection)。

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");    }}

这里是简单的继承了ChannelInboundHandlerAdapter并且重载了channelActive()方法。
ChannelInboundHandlerAdapter是一个实现了ChannelInboundHandler的类,它继承了实现了ChannelHandler接口的抽象类ChannelHandlerAdapter

public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler

这样我们可以专注于我们关心的方法,这里是channelActive()方法,而不需要实现我们不关心的其他方法。这种思想在Java集合类中很常见。

Futures
Future提供了另一种在操作完成时通知应用程序的方式。JDK预置了接口java.util.concurrent.Future,但是其所提供的实现,只允许手动检查对应的操作是否已经完成,或者一直阻塞直到它完成,这是非常麻烦的,因此,Netty提供了它自己的实现——ChannelFuture,用于执行异步操作。

ChannelFuture提供了额外的方法允许我们注册一个或多个ChannelFutureListener实例,它的回调函数是operationComplete(),将会在操作完成时被调用(不管有没有成功都会调用)。如果失败了会携带一个error(一个Throwable异常)。总之,ChannelFutureListener提供的通知机制(通过回调的方式,就像好莱坞法则:Don’t call me; I’ll call you)不需要我们手动检查操作是否完成。

每一个Netty的输出数据(outbound)IO操作都会返回一个ChannelFuture,也就是说,它们都不会阻塞。Netty是彻底的异步和事件驱动的。

下面的代码展示了一个ChannelFuture作为一个I/O操作的一部分返回的例子。这里,connect()方法将会直接返回,而不会阻塞,该调用将会在后台完成。这究竟什么时候会发生则取决于若干的因素,但这个关注点已经从代码中抽象出来了。因为线程不用阻塞以等待对应的操作完成,所以它可以同时做其他的工作,从而更加有效地利用资源。

Channel channel = ...;//异步地连接到远程节点ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25));

下面的代码显示了如何利用ChannelFutureListener。首先,要连接到远程节点上。然后,要注册一个新的ChannelFutureListener到对connect()方法的调用所返回的ChannelFuture上。当该监听器被通知连接已经建立的时候,要检查对应的状态。如果该操作是成功的,那么将数据写到该Channel。否则,要从ChannelFuture中检索对应的Throwable。

Channel channel = ...;//不会在这里阻塞ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25));//注册一个ChannelFutureListener以便当操作完成时得到通知future.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) {    //操作完成后,需要检测这个操作的状态(成功还是抛出异常)    if (future.isSuccess()){        //将要发送的数据放到ByteBuf中        ByteBuf buffer = Unpooled.copiedBuffer("Hello",Charset.defaultCharset());        //异步的发送数据给远程节点,writeAndFlush会返回一个ChannelFuture        ChannelFuture wf = future.channel().writeAndFlush(buffer);        ....    } else {        Throwable cause = future.cause();//得到错误原因        cause.printStackTrace();    }}});

需要注意的是,对错误的处理完全取决于你。例如,如果连接失败,你可以尝试重新连接或者建立一个到另一个远程节点的连接。

如果你把ChannelFutureListener看作是回调的一个更加精细的版本,那么你是对的。事实上,回调和Future是相互补充的机制;它们相互结合,构成了Netty本身的关键构件之一。

事件和处理器

Netty使用不同的事件来通知我们状态的改变或者是操作的状态。这使得我们能够基于已经发生的事件来触发适当的动作。这些动作可能是:

  • 记录日志
  • 数据转换
  • 流控制
  • 应用程序逻辑
    Netty是一个网络编程框架,因此事件是根据是输入数据还是输出数据来分类的。可能由输入数据或者相关的状态变更而触发的事件包括:

  • 连接激活或者失活

  • 数据读取
  • 用户事件
  • 错误事件

一个输出数据事件是一个操作的结果,这个操作会在将来触发一个动作,可能是:

  • 打开或关闭连接到远程节点的连接
  • write或者flush数据到socket

inbound:输入(数据);outbound(输出)数据
每一个事件都能被分发到一个用户实现的具有handler方法的类。

Netty的异步编程模型是建立在Future和回调的概念之上的, 而将事件派发到ChannelHandler的方法则发生在更深的层次上。结合在一起,这些元素就提供了一个处理环境,使你的应用程序逻辑可以独立于任何网络操作相关的顾虑而独立地演变。这也是Netty的设计方式的一个关键目标。

拦截操作以及高速地转换输入数据和输出数据,都只需要你提供回调或者利用操作所返回的Future。这使得链接操作(见上图)变得既简单又高效,并且促进了可重用的通用代码的编写。

Netty通过触发事件将Selector从应用程序中抽象出来,消除了所有本来将需要手动编写的派发代码。在内部,将会为每个Channel分配一个EventLoop,用以处理所有事件,包括:

  • 注册感兴趣的事件;
  • 将事件派发给ChannelHandler;
  • 安排进一步的动作。

EventLoop本身只由一个线程驱动,其处理了一个Channel的所有I/O事件,并且在该EventLoop的整个生命周期内都不会改变。这个简单而强大的设计消除了你可能有的在你的ChannelHandler中需要进行同步的任何顾虑,因此,你可以专注于提供正确的逻辑,用来在有感兴趣的数据要处理的时候执行。如同我们在详细探讨Netty的线程模型时将会看到的,该API是简单而紧凑的。

原创粉丝点击