1.Netty之IO浅析

传统Socket 同步阻塞服务端

采用while(true)方式,每个连接分配单独的线程的模型,之所以使用多线程是因为该模型同步阻塞的,当一个连接在处理I/O的时候,后续的请求是阻塞的。如果是单线程的话必然就挂死在那里,后续的请求无法进入;于是优化采取每个请求新建一个线程去处理。

稍微好点的是直接用线程池去接收请求,来减小线程池生命周期的开销。

这个模型最本质的问题在于,严重依赖于线程。但线程是很"贵"的资源,主要表现在:

  1. 线程的创建和销毁成本很高,在Linux/Unix操作系统上,线程本质上就是一个进程。创建和销毁都是重量级的系统函数。
  2. 线程本身占用较大内存,像Java的线程栈,一般是接近1M的空间,如果系统中的线程数过万,恐怕整个JVM的内存都会被吃掉一半。
  3. 线程的切换成本是很高的。操作系统发生线程切换的时候,需要保留线程的上下文,然后执行系统调用。如果线程数过高,可能执行线程切换的时间甚至会大于线程执行的时间。
  4. 如果网络环境不是很稳定,就很容易造成大量请求的结果同时返回,激活大量阻塞线程从而使系统负载压力过大。

通过观察我们可以看到主要原因在于socket.accept()、socket.read()、socket.write()三个主要函数都是同步阻塞的,也就意味着该请求的接受和读/写都是阻塞的,那么会多次阻塞。

这种方式对应的是Unix网络编程里对应的阻塞I/O模型,即从发起请求数据开始,接收数据到完成读写都是阻塞的,整个资源都是处于占用状态。

---------------------------------------------------------------------------------------------------------------------

NIO 非阻塞IO服务器

NIO对应的Unix网络编程里的I/O多路复用模型,处理读写的操作对应的linux底层操作指令是epoll(Nginx底层调用的也是这个函数),先去检查系统是否有能力处理请求,如果有能力接受请求,则直接接受到一个队列中,然后有操作系统去从队列中取这一批数据去做处理;反之则直接返回0,客户端永远不会阻塞

介绍了I/O模型并且结合代码,我们可以发现NIO中的socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,阻塞操作只在CPU处理读写上了(这种更加的高效)。

NIO的线程模型采取了非常高效的Reactor模型:

标准/典型的Reactor:

  • 步骤1:等待事件到来(Reactor负责)。
  • 步骤2:将读就绪事件分发给用户定义的处理器(Reactor负责)。
  • 步骤3:读数据(用户处理器负责)。
  • 步骤4:处理数据(用户处理器负责)。

NIO的模型图为

可以看到Reactor起到了接收请求非阻塞的关键,这个组件在NIO模型中扮演该角色的是Selector。

       NIO一个新的特点就是不依赖输入输出流他依赖一个新的组件Channel,它用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输,此外通道之间还有聚合和分散的操作。

 

区别通过Channel
支持异步不支持支持
是否可双向传输数据不能,只能单向支持
是否结合 Buffer 使用必须
性能较低较高

然后介绍下NIO还用了比IO更先进的数据缓冲区Buffer,它的好处非常明显:

  1. 使用数组的方式不够灵活且性能差。
  2. 缓冲区的很多操作(clear、flip、rewind)都是操作limit和position的值来实现重复读写。

也就是说这个Buffer是一个可以重复利用的数据结构,它相对于之前的数组非常的高效。

 

C:\Windows\system32\cmd.e:x + zation(InitDestroyAnnotationBeanPostProcessor.java:136) ...26 common frames omittedCaused by: java.net.BindException: Address already in use: bindsun.nio.ch.Net.bind(Native Method)atatsun.nio.ch.Net.bind(Net.java:433)at sun.nio.ch.Net.bind(Net.java:425)sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)atat io .grpc.netty.shaded. io.netty.channel.socket.nio.NioServerSocketchannel.doBind(NioServerSocketchannel.java:13at io . grpc.netty.shaded.io.netty.channel.Abstractchannel$AbstractUnsafe.bind(Abstractchannel.java:551)at io .grpc.netty.shaded. io.netty.channel.DefaultchannelPipeline$HeadContext.bind(DefaultchannelPipeline.java:134at io .grpc.netty.shaded.io.netty.channel.AbstractchannelHandlerContext.invokeBind(AbstractChannelHandlerContextat io.grpc.netty.shaded.io.netty.channel.AbstractchannelHandlerContext.bind(AbstractchannelHandlerContext.java:4at io.grpc .netty.shaded.io.netty.channel.Defaultchannelpipeline.bind(DefaultchannelPipeline.java:985)atio.grpc.nettyshaded.io.netty.channel.AbstractChannel.bind(Abstractchannel.java:247)atio.grpc.netty.shaded.io.netty..bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:344)at io.grpc.netty.shaded, io.netty.util,concurrent.AbstractEventExecutor,safeExecute(AbstractEventExecutor.java:16 2) 0 java:503) 88) at io .grpc.netty,shaded. io.netty.util.concurrent .singleThreadEventExecutor.runAllTasks(SingleThreadEventExecutorio.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:518)io.grpc.netty..shaded.at io.grpc.netty.shaded. io.netty.util.concurrent ,singleThreadEventExecutor$6.run(SingleThreadEventExecutor .javaat io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)at io.grpc.netty.shaded. io.netty.util.concurrent .FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)at java.lang.Thread.run(Thread.iava:748) at java:510) 1044)
最新发布
03-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值