0%

netty简介

netty简介

netty是一个异步的、基于事件驱动的网络应用框架,用来快速开发高性能、高可靠的网络IO程序,主要针对在TCP协议下,面向客户端的高并发应用,本质是一个NIO框架

三种I/O模型

目前java支持三种I/O模型,BIO、NIO、AIO

BIO

同步阻塞IO,一个连接请求对应一个线程,如果该连接没有做任何事情会造成不必要的开销,在java中就是服务端创建一个ServerSocket,然后客户端用一个Socket去连接服务端的ServerSocket,ServerSocket接收到一个连接请求就创建一个Socket和一个线程去跟客户端Socket进行通讯,客户端发送一个请求,服务端进行处理后返回响应,在响应返回前,客户端进行阻塞等待

缺点是每次一个客户端接入,都需要在服务端创建一个线程来对这个客户端进行服务

什么算作阻塞

系统调用read从socket里读取一段数据时,由于Linux无法知道网络上对方是否会发数据。如果没数据发过来,对于调用read的程序来说,就只能进行等待,直到收到数据,当等待时,整个线程会被挂起,无法执行,也无法做其他的工作

NIO

同步非阻塞IO,基于Reactor模型实现的,一个线程处理多个连接请求,客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有I/O请求进行处理

在调用read时,如果发现没有数据到达,BIO模式下会直接block,而NIO模式下会立刻返回-1,并且errno设为EAGAIN,且与IO多路复用结合,来提供一种通知机制,epoll是linux中的IO多路复用的实现

AIO

异步非阻塞IO,基于Proactor模型实现,引入了异步通道的概念,每个连接发送过来的请求,都会绑定一个Buffer,然后通知操作系统去完成异步的读,等到操作系统完成读之后,会调用服务端接口,来对数据进行处理,并进行数据回写,在进行数据回写时同样是给操作系统一个Buffer,让操作系统去完成写,写完了之后在通知客户端来读取数据,特点是先由操作系统完成后才通知服务端程序启动线程去处理,适用于连接数较多且连接时间较长的应用

为什么需要Netty

既然已经有了NIO,为什么还需要Netty对其进行再次封装呢?

  • NIO的类库和API过于繁琐
  • 开发工作量和难度太大,如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞、异常流处理等情况
  • 在JDK7中存在Epoll bug,会导致Selector空轮询,最终导致CPU 100%

注意:Netty5.x已经不在维护了,需要使用Netty4.x

netty构成

构成

核心组件

EventLoop和NioEventLoopGroup

EventLoop用于处理Channel的IO操作,一个单一的EventLoop通常会处理多个Channel事件,一个EventLoopGroup可以含有一组的EventLoop和提供了一种迭代用于检索清单中的下一个

1
2
3
4
5
6
7
8
public interface EventLoopGroup extends EventExecutorGroup {

@Override
EventLoop next();

ChannelFuture register(Channel channel);

}

NioEventLoopGroup是用来处理IO操作的多线程事件循环器,Netty提供了不同的EventLoopGroup的实现来处理不同的传输

Bootstrap和ServerBootstrap

Bootstrap和ServerBootstrap是一个启动NIO服务的辅助启动类,提供了一个用于应用程序网络层配置的容器

Bootstrap是用于客户端的,用于连接到远程主机和端口,只有一个EventLoopGroup

1
2
3
4
5
6
7
8
public B group(EventLoopGroup group) {
ObjectUtil.checkNotNull(group, "group");
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
return self();
}

ServerBootstrap是用于服务端的,用于绑定本地端口,有两个EventLoopGroup

1
2
3
4
5
6
7
8
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
return this;
}

且其内有两个channel集合,第一个集合包含一个单例ServerChannel,代表持有一个绑定了本地端口的socket

1
serverBootstrap.channel(NioServerSocketChannel.class)

第二个集合是包含了所有创建的channel,用来处理服务器所接收到的客户端进来的连接

1
2
3
4
5
6
7
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 管道中加入处理器
socketChannel.pipeline().addLast(new ServerHandler());
}
})
Channel

Channel定义了与socket丰富交互的操作集:bind、close、config、connect、isActive、isOpen、isWritable、read、write等

ChannelHandler

ChannelHandler支持很多协议,并且提供用于数据处理的容器,由特定事件触发,可用于几乎所有的动作,如将一个对象转为字节、执行过程中抛出的异常处理等。

常用接口如ChannelInboundHandler,该接口接收到入站事件可以处理应用程序逻辑

ChannelPipeline

ChannelPipeline提供了一个ChannelHandler链容器,并提供了一个API用于管理沿着链入站和出站事件的流动,每个Channel都有自己的ChannelPipeline,当Channel创建时自动创建的

其内部是一个ChannelHandlerContext的双向链表

1
2
volatile AbstractChannelHandlerContext next;
volatile AbstractChannelHandlerContext prev;

ChannelHandler会在程序的引导阶段被添加到ChannelPipline中,并且被添加的顺序将决定处理数据的顺序

ChannelFuture

Netty所有IO操作都是异步的,Netty提供了ChannelFuture接口来解决异步操作无法立即返回结果,通过addListener来注册一个ChannelFutureListener,当操作完成后,会进行通知

欢迎关注我的其它发布渠道