Netty是一个业界使用范围特别广的高并发线程框架,关于线程模型框架,可以说直接影响着网络程序的性能
为什么使用Netty,Netty带了的性能提升有哪些,主要就这两个方面进行解答
1.网络编程的性能瓶颈,在之前的网络编程中,客户端和服务端建立了一个连接,这个连接是传统的BIO,也就是阻塞式的IO,会为每一个Socket分配一个独立的线程
虽然保证了网络编程的安全性,但是会导致一个网络请求对应一个线程,如果响应速度或发送速度过慢,都会导致线程的阻塞,而且需要考虑一个问题,就是在于连接建立了之后,如果请求迟迟不到,也会导致阻塞问题的出现
在现实生活中,往往在十万乃至百万并发问题时候,如果采用这种阻塞式的IO操作,需要服务器端提供相对应数量的线程,这几乎等于是不可能的
故,Java提供了非阻塞式的NIO的API.使用非阻塞式的API来做到一个线程处理多个请求了,而且外加上 Reactor设计模式,实现了Netty
接下来先说明下Reactor模式
上面为其的类图
其中Handle值得就是IO句柄,就是一个网络连接的抽象画,Event Handler作为事件处理器,可以调用其的handle_event()方法处理IO事件,
Synchronous Event Demutiplexer是系统提供的多路复用API
Reactor模式的核心是Reactor这个类,其中register_handler是用于阻塞处理器,handle_events()用于返回所有的处理器,然后遍历来选择处理器处理
所以通常都是while(true){}来调用handle_events()方法
Netty中的线程模型
在Reactor的基础之上,发展出了自己的套路,其核心的概念就是事件循环 EventLoop, 负责监听事件并调用事件处理器
在Netty中,事件处理器和网络连接是一对多的关系,而EventLoop和Java线程是一对一的关系
这里的稳定是关系的一旦确定就不再变化,也就是一个网络连接对应一个EventLoop,保证了单线程,确保了处理安全
其还有一个核心概念,EventLoopGroup,一个EventLoopGroup由一组EventLoop组成
实际使用中,往往有两组EventLoopGroup,称为BossGroup WorkGroup
这两个Group的分工明确,因为在TCP网络请求中,会建立两个Socket,一个用于连接请求,一个用于处理请求,建立两个请求,所以会创建两个Socket,对应着创建两个Group,在Netty中,Boss用于分配连接请求,Work用于真正的处理请求
两者用轮询算法连接
在真正的使用中,需要注意的是,如果Boss只监听一个端口,那么只需要一个EventLoop
在默认情况下,Netty会创建2*CPU核数的EventLoop,但是仍然需要避免长时间的阻塞问题
如果想要对Netty深入的了解,可以去学习一些TCP/IP的网络知识,参考<<TCP/IP网络编程>>
public static void main(String[] args) {
//事件处理器
final EchoServerHandler serverHandler = new EchoServerHandler();
//boss线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//worker线程组
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch){
ch.pipeline().addLast(serverHandler);
}
});
//bind服务端端口
ChannelFuture f = b.bind(9090).sync();
f.channel().closeFuture().sync();
} finally {
//终止工作线程组
workerGroup.shutdownGracefully();
//终止boss线程组
bossGroup.shutdownGracefully();
}
}
//socket连接处理器
class EchoServerHandler extends ChannelInboundHandlerAdapter {
//处理读事件
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg){
ctx.write(msg);
}
//处理读完成事件
@Override
public void channelReadComplete(ChannelHandlerContext ctx){
ctx.flush();
}
//处理异常事件
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}