Netty学习
简介
高性能,事件驱动的异步非阻塞的IO框架,用于建立TCP等底层连接
基于netty可以构建高性能的http服务器,已经被很多项目作为socket底层基础
- 传统IO的读取
在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情
标准的IO基于字节流和字符流进行操作
InputStream is = new FileInputStream("input.bin");
int byte = is.read(); // 当前线程等待结果到达直至错误
- NIO的读取
Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。
NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中
while (true) {
selector.select(); // 从多个通道请求事件
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectorKey key = (SelectionKey) it.next();
handleKey(key);
it.remove();
}
}
阻塞与非阻塞原理
- 传统硬件的堵塞,从内存中取数据,写到磁盘,而CPU一直等到磁盘写完成,磁盘写操作是很慢的,这段时间CPU被堵塞不能发挥效率
- CPU只发出写操作这样的指令,做一些初始化工作,DMA具体执行,从内存中读数据,然后写到磁盘,当完成写后发出一个中断事件给CPU,这段时间CPU是空闲的,可以做别的事情,这个原理称为Zero.copy零拷贝
netty主要原理和适用
nio有一个主要的类selector,这个类似一个观察者,只要我们把需要探知的socketChannel告诉selector,我们继续做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些key,就会获得我们刚刚注册的socketChannel,然后我们从Channel中读取数据
netty服务端示例
ServerBootstrap是socket服务端的入口
// 初始化用于Acceptor的主"线程池"以及用于I/O工作的从"线程池"
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//初始化ServerBootstrap实例, 此实例是netty服务端应用开发的入口
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
//指定通道channel的类型,由于是服务端,故而是NioServerSocketChannel
.channel(NioServerSocketChannel.class)
//设置ServerSocketChannel的处理器
.handler(new LoggingHandler())
.childHandler(new ChannelInitializer<SocketChannel>() {
//设置子通道也就是SocketChannel的处理器, 其内部是实际业务开发的"主战场"
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
//配置ServerSocketChannel的选项
.option(ChannelOption.SO_BACKLOG, 128)
//配置子通道也就是SocketChannel的选项
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定并侦听某个端口
ChannelFuture f = b.bind(port).sync();
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
原理
channelPipeline是关键,类似Unix的管道
- 为每个channel保留channelHandlers,如EchoServerHandler
- 所有事件都要通过它
- 一个channel对应一个channelPipeline
- 包含协议编码解码,安全验证SSL和应用逻辑