编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

hprose for java源码分析-13 NIO(javaformac10.7的.dmg安装包下载)

wxchong 2024-10-30 04:30:34 开源技术 33 ℃ 0 评论

13.1 NIO

NIO,即非阻塞IO。关键点包括Channel, Selector。

Channel向Selector注册事件,如READ,WRITE,CONNECT。当Selector检测到channel上的相关事件发生时,会通知到应用程序。所谓非阻塞,也并非一点都不阻塞,只是阻塞点发生了变化。

13.2 Reactor

hprose用到一种称为Reactor模式的框架。

Reactor继承自Thread,运行在一个单独的线程里。

Reactor之上有个ReactorGroup,称为组。一个组可包括多个Reactor,即可以有多个独立运行的线程。

这样做的好处是,可以利用多核性质,提高系统性能。一个线程处理若干channel上的事件,各线程又独立运转,因此效率较高,但有时需要注意线程同步问题。

13.3 hprose客户端连接过程

hprose客户端连接服务器从Connector类开始

Connector构造时,生成一个Selector实例,生成一个ReactorGroup组实例,组中可容纳的线程数量由外部传入(reactorThreads变量)。

当外部调用Connector的start()方法时,线程就开始运转了。

run()方法会一直运行,直到线程被打断。在run方法的开始,调了ReactorGroup的start()方法,此时组的线程也开始运行。

当客户端试图连接服务器时,会调用哪个方法呢?会调下面的create方法,

这个方法内,通过SocketChannel.open()生成一个SocketChannel实例channel,并且将channel,handler(外部设置的连接回调),address封装到了一个新Connection实例中。同时配置channel为非阻塞。方法中有个register(conn)调用,

register方法只是简单的将conn置入队列queue中,然后唤醒selector, 为何要唤醒?

到这里,还没有发现是如何连接的。因为还没有见到channel.connect()类似这样的调用,这是连接的关键代码。

看来,连接要在run()循环中去找。

在run()方法中,有调用 process()方法

这个方法中,从queue中取出一个Conection实例,然后调conn.connect()方法,继续探究:

此处,channel向selector注册了事件 OP_CONNECT, 并且调用关键代码 channel.connect(address),到此终于看到客户端是如何连接的。

但调下channel.connect是不是完了呢?还没有。

还需要在Connector.dispatch()方法中,作处理

87行代码selector.select()其实是个阻塞方法,当没有检测到任何事件发生时,会一直阻塞。这就是之前调register()方法,将selector唤醒的原因。若不唤醒,线程可能会一直阻塞。看到这里,就知道NIO非阻塞,其实并不是一点也不阻塞,只是阻塞点发生了变化。但这样的一个变化,节约了程序的执行时间,程序不需要一遍遍轮询数据有没有准备好。

接下来的90-98行代码,是selector处理的一般写法。即获取一个迭代器,遍历,依次检测key的状态,调

it.remove()。

key.isConnectable()为true,说明已经检测到连接好了的事件。

连接好以后,调connect()方法

本方法中,通过调channel.finishConnect()完成连接过程。并且将这个conn注册到reactor中。

为什么要将conn注册到reactor中呢?因为conn虽然连接好了,但接下来需要通过conn发送数据,接收服务器传来的数据,这些操作事件的监听放到哪里去处理呢?可以放到Conector实例内,但这样做一个Connector需要完成多件事,会使效率变慢。所以放到了reactor这样专门处理读写的线程内,reactor其实是一个组实例,在组内,又可以将conn分到不同的线程内,进一步提高执行效率。

到此,完成了连接过程。但是,应用程序又怎么知道连接完成了,因为应用程序知道连接完成以后,就要开始发送数据了。这就是连接回调设置的目的。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表