「javanetty阻塞」java io阻塞

博主:adminadmin 2022-11-26 22:32:07 56

今天给各位分享javanetty阻塞的知识,其中也会对java io阻塞进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

有人能说清楚JAVA7 NIO NETTY IOCP之间的联系吗

早期网络访问的做法是,连接以后,就等待对方应答,对方没有应答,代码就会死在那里。这称为阻塞式通讯。

后来,java 1.4 开始引入了nio, nio是非阻塞式网络通讯. 其实可以理解为系统去扫描端口,如果有返回就处理,没有的话,代码就继续做别的事。

不过nio的编码比较麻烦,虽然性能好,可是很多代码还在用阻塞,因为代码简单,很多例子上也是socket的。

然后高丽人开发了apache的mina,可以快速开发nio。

后来高丽人离开apache,开发了netty,提供了另一种调用nio的framework。

现在mina和netty是最常用的nio框架。

至于iocp,这个是个系统级的实现,其实类似nio,不过是通过操作系统来实现的。而且只支持windows。java 7以后把iocp引入,提供了快速开发的接口。不过不是很推荐使用。系统实现就表示iocp不再是跨平台的。那么实用性就差很多了。

java 应用服务框架mina 和 netty应用案例都有那些,两者该怎么选择

哪个熟悉用哪个.Netty是基于线程的完全非阻塞异步模型, 强依赖于LinkedBlockingQueue. 说不定未来会有一款基于完全无锁队列(例如disruptor)的IO框架. Netty比较烦心的是需要定义Inbound,Outbound的handler, 当信道active的时候, 会先找到inbound,如果这个inbound handler里面有调用写操作或者刷新操作,则会在这个inbound处,再次查找outbound,这就要求在添加pipleline的时候要按照一定的顺序, 个人感觉这像是被强奸了一样.

为什么要用Netty开发

Netty是由JBOSS提供的基于Java NIO的开源框架,Netty提供异步非阻塞、事件驱动、高性能、高可靠、高可定制性的网络应用程序和工具,可用于开发服务端和客户端。

JAVA原先是采用的是传统的BIO,为什么后来又研发出了NIO呢?

首先看看传统的基于同步阻塞IO(BIO)的线程模型图

BIO主要存在以下缺点:

1.从线程模型图中可以看到,一连接一线程,由于线程数是有限的,所以这样的模型是非常消耗资源的,

最终也导致它不能承受高并发连接的需求

2.性能低,因为频繁的进行上下文切换,导致CUP利用率低

3.可靠性差,由于所有的IO操作都是同步的,即使是业务线程也如此,所以业务线程的IO操作也有可能被阻塞,

这将导致系统过分依赖网络的实时情况和外部组件的处理能力,可靠性大大降低

上面的原因就是导致早期的高性能服务器为什么不选用JAVA开发,而是选用C/C++的重要原因。

为了解决上面的问题,NIO横空出世,下面是NIO的线程模型图

1.NIO采用了Reactor线程模型,一个Reactor聚合了一个多路复用器Selector,它可以同时注册、监听和轮询

成百上千个Channel,这样一个IO线程可以同时处理很多个客户端连接,线程模型优化为1:N(N最大句柄、数),

或M:N(M通常为CUP核数+1)

2.避免了IO线程频繁的上下文切换,提升了CUP的效率

3.所有的IO操作都是异步的,所以业务线程的IO操作就不用担心阻塞,系统降低了对网络的实时情况和外部组件

的处理能力的依赖

为什么不直接用JDK原生的NIO而选用Netty框架?

先看看JDK的NIO中服务端和客户端的时序图

服务端:

客户端:

从图中我们可以看到,使用JDK原生NIO的不足之处

1.NIO的类库和API相当复杂,使用它来开发,需要非常熟练地掌握Selector、ByteBuffer、ServerSocketChannel、SocketChannel等

2.需要很多额外的编程技能来辅助使用NIO,例如,因为NIO涉及了Reactor线程模型,所以必须必须对多线程和网络编程非常熟悉才能写出高质量的NIO程序

3.想要有高可靠性,工作量和难度都非常的大,因为服务端需要面临客户端频繁的接入和断开、网络闪断、半包读写、失败缓存、网络阻塞的问题,这些将严重影响我们的可靠性,而使用原生NIO解决它们的难度相当大。

4.JDK NIO中著名的BUG--epoll空轮询,当select返回0时,会导致Selector空轮询而导致CUP100%,官方表示JDK1.6之后修复了这个问题,其实只是发生的概率降低了,没有根本上解决。

那么为什么要用Netty呢?

1.API使用简单,更容易上手,开发门槛低

2.功能强大,预置了多种编解码功能,支持多种主流协议

3.定制能力高,可以通过ChannelHandler对通信框架进行灵活地拓展

4.高性能,与目前多种NIO主流框架相比,Netty综合性能最高

5.高稳定性,解决了JDK NIO的BUG

6.经历了大规模的商业应用考验,质量和可靠性都有很好的验证。

Netty能提供什么服务?

1.开发异步非阻塞的TCP网络应用程序

2.开发异步非阻塞的UDP网络应用程序

3.开发异步文件传输程序

4.开发异步HTTP程序的服务端和客户端

5.提供多种编解码的集成框架,包括谷歌Protobuf、JBossMarshalling、Java序列化、压缩编解码、XML解码、

字符串编解码等都可以由用户直接使用

6.提供形式多样的编解码基础类库,可以方便地进行私有协议栈编解码框架的二次开发

7.基于职责链的Pipeline-Handler机制,可以方便地对网络事件进行拦截和定制

8.所有的IO操作都是异步的,用户可以通过Future-Listeren机制主动get结果或者等IO线程完成操作之后主动Notify来通知,

用户业务线程不需要同步等待

9.基于链路空闲事件监测的心跳机制

10.流量控制和整形

......

netty系列之:NIO和netty详解

netty为什么快呢?这是因为netty底层使用了JAVA的NIO技术,并在其基础上进行了性能的优化,虽然netty不是单纯的JAVA nio,但是netty的底层还是基于的是nio技术。

nio是JDK1.4中引入的,用于区别于传统的IO,所以nio也可以称之为new io。

nio的三大核心是Selector,channel和Buffer,本文我们将会深入探究NIO和netty之间的关系。

在讲解netty中的NIO实现之前,我们先来回顾一下JDK中NIO的selector,channel是怎么工作的。对于NIO来说selector主要用来接受客户端的连接,所以一般用在server端。我们以一个NIO的服务器端和客户端聊天室为例来讲解NIO在JDK中是怎么使用的。

因为是一个简单的聊天室,我们选择Socket协议为基础的ServerSocketChannel,首先就是open这个Server channel:

然后向server channel中注册selector:

虽然是NIO,但是对于Selector来说,它的select方法是阻塞方法,只有找到匹配的channel之后才会返回,为了多次进行select操作,我们需要在一个while循环里面进行selector的select操作:

selector中会有一些SelectionKey,SelectionKey中有一些表示操作状态的OP Status,根据这个OP Status的不同,selectionKey可以有四种状态,分别是isReadable,isWritable,isConnectable和isAcceptable。

当SelectionKey处于isAcceptable状态的时候,表示ServerSocketChannel可以接受连接了,我们需要调用register方法将serverSocketChannel accept生成的socketChannel注册到selector中,以监听它的OP READ状态,后续可以从中读取数据:

当selectionKey处于isReadable状态的时候,表示可以从socketChannel中读取数据然后进行处理:

上面的serverResponse方法中,从selectionKey中拿到对应的SocketChannel,然后调用SocketChannel的read方法,将channel中的数据读取到byteBuffer中,要想回复消息到channel中,还是使用同一个socketChannel,然后调用write方法回写消息给client端,到这里一个简单的回写客户端消息的server端就完成了。

接下来就是对应的NIO客户端,在NIO客户端需要使用SocketChannel,首先建立和服务器的连接:

然后就可以使用这个channel来发送和接受消息了:

向channel中写入消息可以使用write方法,从channel中读取消息可以使用read方法。

这样一个NIO的客户端就完成了。

虽然以上是NIO的server和client的基本使用,但是基本上涵盖了NIO的所有要点。接下来我们来详细了解一下netty中NIO到底是怎么使用的。

以netty的ServerBootstrap为例,启动的时候需要指定它的group,先来看一下ServerBootstrap的group方法:

ServerBootstrap可以接受一个EventLoopGroup或者两个EventLoopGroup,EventLoopGroup被用来处理所有的event和IO,对于ServerBootstrap来说,可以有两个EventLoopGroup,对于Bootstrap来说只有一个EventLoopGroup。两个EventLoopGroup表示acceptor group和worker group。

EventLoopGroup只是一个接口,我们常用的一个实现就是NioEventLoopGroup,如下所示是一个常用的netty服务器端代码:

这里和NIO相关的有两个类,分别是NioEventLoopGroup和NioServerSocketChannel,事实上在他们的底层还有两个类似的类分别叫做NioEventLoop和NioSocketChannel,接下来我们分别讲解一些他们的底层实现和逻辑关系。

NioEventLoopGroup和DefaultEventLoopGroup一样都是继承自MultithreadEventLoopGroup:

他们的不同之处在于newChild方法的不同,newChild用来构建Group中的实际对象,NioEventLoopGroup来说,newChild返回的是一个NioEventLoop对象,先来看下NioEventLoopGroup的newChild方法:

这个newChild方法除了固定的executor参数之外,还可以根据NioEventLoopGroup的构造函数传入的参数来实现更多的功能。

这里参数中传入了SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler、taskQueueFactory和tailTaskQueueFactory这几个参数,其中后面的两个EventLoopTaskQueueFactory并不是必须的。

最后所有的参数都会传递给NioEventLoop的构造函数用来构造出一个新的NioEventLoop。

在详细讲解NioEventLoop之前,我们来研读一下传入的这几个参数类型的实际作用。

SelectorProvider是JDK中的类,它提供了一个静态的provider()方法可以从Property或者ServiceLoader中加载对应的SelectorProvider类并实例化。

另外还提供了openDatagramChannel、openPipe、openSelector、openServerSocketChannel和openSocketChannel等实用的NIO操作方法。

SelectStrategyFactory是一个接口,里面只定义了一个方法,用来返回SelectStrategy:

什么是SelectStrategy呢?

先看下SelectStrategy中定义了哪些Strategy:

SelectStrategy中定义了3个strategy,分别是SELECT、CONTINUE和BUSY_WAIT。

我们知道一般情况下,在NIO中select操作本身是一个阻塞操作,也就是block操作,这个操作对应的strategy是SELECT,也就是select block状态。

如果我们想跳过这个block,重新进入下一个event loop,那么对应的strategy就是CONTINUE。

BUSY_WAIT是一个特殊的strategy,是指IO 循环轮询新事件而不阻塞,这个strategy只有在epoll模式下才支持,NIO和Kqueue模式并不支持这个strategy。

RejectedExecutionHandler是netty自己的类,和 java.util.concurrent.RejectedExecutionHandler类似,但是是特别针对SingleThreadEventExecutor来说的。这个接口定义了一个rejected方法,用来表示因为SingleThreadEventExecutor容量限制导致的任务添加失败而被拒绝的情况:

EventLoopTaskQueueFactory是一个接口,用来创建存储提交给EventLoop的taskQueue:

这个Queue必须是线程安全的,并且继承自java.util.concurrent.BlockingQueue.

讲解完这几个参数,接下来我们就可以详细查看NioEventLoop的具体NIO实现了。

首先NioEventLoop和DefaultEventLoop一样,都是继承自SingleThreadEventLoop:

表示的是使用单一线程来执行任务的EventLoop。

首先作为一个NIO的实现,必须要有selector,在NioEventLoop中定义了两个selector,分别是selector和unwrappedSelector:

在NioEventLoop的构造函数中,他们是这样定义的:

首先调用openSelector方法,然后通过返回的SelectorTuple来获取对应的selector和unwrappedSelector。

这两个selector有什么区别呢?

在openSelector方法中,首先通过调用provider的openSelector方法返回一个Selector,这个Selector就是unwrappedSelector:

然后检查DISABLE_KEY_SET_OPTIMIZATION是否设置,如果没有设置那么unwrappedSelector和selector实际上是同一个Selector:

DISABLE_KEY_SET_OPTIMIZATION表示的是是否对select key set进行优化:

如果DISABLE_KEY_SET_OPTIMIZATION被设置为false,那么意味着我们需要对select key set进行优化,具体是怎么进行优化的呢?

先来看下最后的返回:

最后返回的SelectorTuple第二个参数就是selector,这里的selector是一个SelectedSelectionKeySetSelector对象。

SelectedSelectionKeySetSelector继承自selector,构造函数传入的第一个参数是一个delegate,所有的Selector中定义的方法都是通过调用

delegate来实现的,不同的是对于select方法来说,会首先调用selectedKeySet的reset方法,下面是以isOpen和select方法为例观察一下代码的实现:

selectedKeySet是一个SelectedSelectionKeySet对象,是一个set集合,用来存储SelectionKey,在openSelector()方法中,使用new来实例化这个对象:

netty实际是想用这个SelectedSelectionKeySet类来管理Selector中的selectedKeys,所以接下来netty用了一个高技巧性的对象替换操作。

首先判断系统中有没有sun.nio.ch.SelectorImpl的实现:

SelectorImpl中有两个Set字段:

这两个字段就是我们需要替换的对象。如果有SelectorImpl的话,首先使用Unsafe类,调用PlatformDependent中的objectFieldOffset方法拿到这两个字段相对于对象示例的偏移量,然后调用putObject将这两个字段替换成为前面初始化的selectedKeySet对象:

如果系统设置不支持Unsafe,那么就用反射再做一次:

还记得前面我们提到的selectStrategy吗?run方法通过调用selectStrategy.calculateStrategy返回了select的strategy,然后通过判断

strategy的值来进行对应的处理。

如果strategy是CONTINUE,这跳过这次循环,进入到下一个loop中。

BUSY_WAIT在NIO中是不支持的,如果是SELECT状态,那么会在curDeadlineNanos之后再次进行select操作:

如果strategy 0,表示有拿到了SelectedKeys,那么需要调用processSelectedKeys方法对SelectedKeys进行处理:

上面提到了NioEventLoop中有两个selector,还有一个selectedKeys属性,这个selectedKeys存储的就是Optimized SelectedKeys,如果这个值不为空,就调用processSelectedKeysOptimized方法,否则就调用processSelectedKeysPlain方法。

processSelectedKeysOptimized和processSelectedKeysPlain这两个方法差别不大,只是传入的要处理的selectedKeys不同。

处理的逻辑是首先拿到selectedKeys的key,然后调用它的attachment方法拿到attach的对象:

如果channel还没有建立连接,那么这个对象可能是一个NioTask,用来处理channelReady和channelUnregistered的事件。

如果channel已经建立好连接了,那么这个对象可能是一个AbstractNioChannel。

针对两种不同的对象,会去分别调用不同的processSelectedKey方法。

对第一种情况,会调用task的channelReady方法:

对第二种情况,会根据SelectionKey的readyOps()的各种状态调用ch.unsafe()中的各种方法,去进行read或者close等操作。

NioEventLoop虽然也是一个SingleThreadEventLoop,但是通过使用NIO技术,可以更好的利用现有资源实现更好的效率,这也就是为什么我们在项目中使用NioEventLoopGroup而不是DefaultEventLoopGroup的原因。

关于javanetty阻塞和java io阻塞的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

The End

发布于:2022-11-26,除非注明,否则均为首码项目网原创文章,转载请注明出处。