Netty原理-从NIO开始
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty原理-从NIO开始相关的知识,希望对你有一定的参考价值。
参考技术A Netty是基于NIO的异步通信框架(曾经引入过AIO,后来放弃),故要说Netty原理我们要先从NIO开始。NIO 是JAVA在JDK4中引入的同步非阻塞通信模型,在NIO出现之前(JDK4之前)市场上只有一个BIO模型顾名思义BLOCKING IO (同步阻塞通信模型)
BIO(BLOCKING I/O):
BIO 为一个连接 一个线程的模式,当有连接时服务器会开启一个线程来处理请求
若此请求啥都不想干此时线程会怎么样?
此线程会进入阻塞模式(BLOCKING)!---啥也不干,干等着zzZZ~
这种一连接,一线程的模式会造成服务器资源不必要的开销并且在大量连接访问时 服务器会发生什么?车道(线程)不足,车太多--我堵车了
由此就出现了NIO
↓
NIO(new/NONBLOCKING I/O):
NIO为同步非阻塞通信模型,Select(多路复用器)为此模型的核心,实现了多个连接一个线程
当有客户端连接请求时 此连接请求会被注册至select上,当select检测到此连接有I/O请求时才会打开一个线程去对此I/O请求进行处理-----单线程模型
这个时候有人问了:这么多操作都在一个线程里,线程忙不过来怎么办?
此时 由于网络请求、I/O读写、业务操作都在一个线程下,会导致在高并发的情况下存在性能瓶颈 于是乎有人就提出来 将业务操作丢到另一个线程怎么样?
于是出现了第三种reactor模型-使用线程池进行操作网络请求、IO在一个线程,业务操作在另个一个线程 的业务分离----线程池模型
从此图中可以看出此时 模型中使用一个线程池来进行网络请求、IO读取
当读取完成后将业务操作绑定在线程池中另外的线程上-------网络IO与业务操作可以同步进行了!一切都完美了起来!
但是!事情还没完!!这个时候又有人提出问题:在高并发的时候咋办?会不会有性能瓶颈
因为网络IO是非常消耗CPU的,当网络请求与网络IO在同个线程中时,造C10K的情况下单个线程并不足以支撑起所有的IO操作!因此也形成了在高并发状态下的性能瓶颈
于是大佬们就想着,如果把IO拆出来让单个线程池去接收网络请求,用另一个线程池来进行IO与业务操作会不会更好
于是第四种Reactor模型应运而生--主从Reactor多线程模型
此模型中 mainReactor只用于接收网络请求,而subReactor中为一个线程池,线程池中每个线程上绑定一个select
当mainReactor接收到请求时(一个描述符) 系统会生成一个新的描述符代表此连接生效,此时mainReactor会将新的描述符通过一个算法在线程池中选定一个线程 将此描述符绑定至此线程池上的select上,由此线程来对请求进行I/O 与业务操作
从此百万连接高并发不是问题
写到这 我们是不是想起了Netty的启动过程
1、声明两个EventLoopGroup一个为boss(mainReactor)一个为worker(subReactor)
EventLoopGroup(线程池)初始化的时候会生成(懒加载)指定数量的EventLoop(线程)若无指定 则会生成CPU数X2的线程
2、声明一个启动辅助类Bootstrap并将EventLoopGroup注册到启动辅助类BootStrap上(bootStrap.group)
接着再给bootstrap指定channel模型等属性,再添加上业务流水线(channelpipeline)并且在pipeline中添加上业务操作handler,(通过channelpipeline可以对传入数据为所欲为)
3、绑定端口
Netty启动完成
这时候可能有人会问了:这和你上面说的reactor?NIO有啥关系?
这个时候我们要这么看
↓
若我们将boss与worker线程池设置为相同的一个线程池,那么会发生什么事?
此时关注一下第三个Reactor模型时就会发现 当BOSS=WORKER时候 netty实现的就是第三种Reactor模型 使用线程池模型
而当boss不等于worker的时候使用的就是第四种 主从多线程模型
Netty就是基于Reactor模型来对NIO进行了易用化封装,从Netty源码中就可以看出来其实底层还都是NIO的接口
此次处为自己读源码之后的理解 如有误请指正
感恩
反手拿下第一个赞
超详细解释从Java NIO到Netty的每一步
Netty 组件介绍
因为Netty被用于架构或者中间件底层通讯,所以平时我并没有直接基于Netty进行编程过,所以先大概读一下用户手册,发现用户手册Netty对基本概念两三句话概括了之,所以自己先研究了下Netty核心组件的基本概念,之后再去读源码。
从抛弃服务代码讲起
接下来先上用户手册中的抛弃服务(只接收数据不做出任何响应)代码,这两个类基本涵盖了netty的基本使用,我会在代码中注释,然后开始对其核心组件进行讲解,如果大家对哪个组件不理解需要我详细展开说明的,也可以告诉我,我可以在后续进行详细介绍,不过本次仅仅介绍组件的基本概念,后续再在源码中详细介绍。
package netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class DiscardServer
private int port;
public DiscardServer(int port)
this.port = port;
public void run() throws Exception
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();// (1)
try
// Netty的服务启用类
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup) // (1)
// 简单理解为Netty自己写了一个ServerSocketChannel接收连接请求
.channel(NioServerSocketChannel.class)
// 可以理解我们给SocketChannel添加handler对其进行处理
.childHandler(new ChannelInitializer<SocketChannel>()
@Override
public void initChannel(SocketChannel ch) throws Exception
ch.pipeline().addLast(new DiscardServerHandler()); //(2)
)
//对NioServerSocketChannel进行配置
.option(ChannelOption.SO_BACKLOG, 128)
//对SocketChannel进行配置
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口开始接收请求
ChannelFuture f = b.bind(port).sync();
// 简单理解为关闭服务
f.channel().closeFuture().sync();
finally
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
public static void main(String[] args) throws Exception
int port = 8080;
if (args.length > 0)
port = Integer.parseInt(args[0]);
new DiscardServer(port).run();
package netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* 自定义处理I/O请求
*/
public class DiscardServerHandler extends ChannelInboundHandlerAdapter //(3)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
// 丢弃接收到的数据.
((ByteBuf) msg).release();
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
// 当发生异常时关闭连接.
cause.printStackTrace();
ctx.close();
在接下来两章中详细讲解一下上述代码注释(1)、(2)的基本概念和理解。
关注我日常更新分享Java编程技术!希望大家都能早日走上人生巅峰!
以上是关于Netty原理-从NIO开始的主要内容,如果未能解决你的问题,请参考以下文章
4.socket框架netty的使用,以及nio的实现原理,为啥是异步非阻塞