您的位置 首页 java

hprose for java源码分析-5

5.1 何方神圣

书接上回。上回遗留2个问题没有解决。本篇先解决 .then问题。

上回中的#11方法返回的是 Promise<ByteBuffer> 实例,而then其实是Promise类的一个方法,因此先看看Promise是什么。打开Promise. java 源码,发现它是一个泛型类。假设Promise泛型参数V为ByteBuffer,其实例为pobj,即 pobj = new Promise<ByteBuffer>();

1. pobj有3个状态,分别为

PENDING 初始状态,new 一个Promise时,会处于此状态

FULFILLED 满足状态,当调用方法resolve(Object value) 时,会处于此状态

REJECTED 拒绝状态,当调用方法reject(Throwable e)时,会处于此状态

pobj只可能从初始状态转向 FULFILLED或REJECTED状态,一旦处于FULFILLED或REJECTED状态时,该实例的状态就不再发生变化。

2. 方法 then(Action<V> onfulfill) 作用,向pobj增加一个 回调 接口,当pobj处于FULFILLED状态时,将会调用该回调接口,同时返回一个新的Promise实例,其最终会调到的代码如下:

可以看到892行,返回了新生成的Promise实例next。

假设有调用 pobj.then( new Action<ByteBuffer>{ … } ),由于初始时pobj处于PENDING状态,那么会执行888行的default, 从而将 onfulfill, onreject,next封装到了一个SubScriber实例中,并且将这个实例,存储于pobj中的subscribers列表中, 然后返回一个新的Promise实例,假定为 pe1。

外部拿到pe1后,可以继续通过pe1调用then方法,即 pe1. then( new Action<ByteBuffer>{ … } ),此时跟上面的步骤一样,生成一个新Promise实例(假定为pe2),并且将pe2, onfulfill, onreject存储于pe1的subscribers列表内, 返回pe2。

外部拿到pe2后,可再次通过pe2调用then方法,即 pe2. then( new Action<ByteBuffer>{ … } ),与上面一样,生成一个pe3,并且将pe3, onfulfill, onreject存储于pe2的subscribers列表内, 返回pe3。

上面的 .then 调用可一直延续下去。最终在内存会生成一个 链表 结构,如图g1所示。

(下图假设,只有3个Promise实例,若有更多实例,可从pe3处依次类推。为了看的清晰,每个Promise实例subscribers列表中,只取了一个由onfulfill, onreject,next组成的元素)

—————————————-( g1 )

3. 有了上面的链表,调 pe1.resolve(Object value)时,首先查看pe1上有没有挂载onfulfill回调,有的话,先调用onfulfill,然后再调用 pe2. resolve(Object value)。再次查看pe2上有没有挂载onfulfill回调,有的话,先调用onfulfill,然后再调pe3. resolve(Object value) … 依次类推。

方法resolve(Object value)最终会调到如下代码段。

>>>

771行,改变了Promise实例的状态;772行,保存了传入的值value,然后遍历subscribers列表,对列表中每个元素,均调用751行的resolve方法。

752行可以看到,若Promise上onfulfill不为空,则会处理回调,否则直接调 next.resolve()。

对照图g1中的链表结构来看这段代码,就一目了然了。

5.2 神奇的链

了解了Promise的then() 方法形成的链表结构后,就可以针对上回中漫长的调用链作一个剖析,并且画出其内存图。第4回调用链 上的then调用最终会构成如下的一个链表( 图g2 )

下图表示,第4回中调用链层层返回时(逆着调用链顺序),构成的promise实例的情形。

左半部分是Promise实例,右半部分是onfulfill或onreject挂载的代码, null表示没有挂载代码。

————————————( g2 )

到p5后,还会返回到 HproseClient.java中

此段牵涉客户端等待问题,后续介绍。

p1-p5构成一个Promise链,这个链中挂载了当结果数据从服务器返回时要执行的一些操作。看到这个链表,会有如下疑问。

1. 链表头p1 存到了哪里?

———– ( g3 ) (HproseTcpClient.java文件内)

141行生成了一个Request实例,Request构造时,如下

———-( g4 )

47行中生成了Promise<ByteBuffer>实例。

g3中142行根据request调 fetch方法获取连接实例,第一次调用fetch由于没有找到一个可用连接,会将request保存到一个列表内, 如下所示,requests列表中存储了Request,

(HproseTcpClient.java文件内)

而这个Request又保存了Promise<ByteBuffer>实例,即p1。

2. 保存的Request什么时候取出?

(HproseTcpClient.java文件内)

fetch方法第一次无法获取到连接实例,会新生成一个连接实例,当这个连接实例已连接到服务器时,框架回调到265行onConnected(Connection conn)方法。

此方法内,267行将之前保存的request取出,并且调 269行send(conn, request),将这个消息包发送到服务器端,在send方法内,又做了一点手脚,如下

(HproseTcpClient.java文件内)

249行,将request中的 result,即Promise<ByteBuffer>实例,存储到了 responses Map内。将request请求从列表内取出后,如果不保存它内部成员result,result就会丢失,这样图g2中的p1就会丢掉,整个链表就会丢掉,而挂载到每个Promise上的代码就无法执行。

3. 保存到 responses中的Promise<ByteBuffer>又什么时候取出呢?

(HproseTcpClient.java文件内)

303行方法,在收到服务器返回的数据后,会调用。此时,data中保存了服务器返回的 rpc 调用结果, id是调用id, conn是对应的连接。

304行,通过conn, id取出之前保存的Promise<ByteBuffer>实例

309行,这是重要一行调用,正是这一行代码,引发了图 g2 链表上的每个节点都会调用各自的 resolve()方法,从而挂载到各个节点上的onfulfill方法全部得到了执行。服务器返回的rpc结果,正是通过p1-p5构成的链表层层返回到了客户端调用者跟前。

到此,Promise中的 then调用疑问基本解决,但客户端等待问题呢, 客户端又是如何拿到rpc结果的呢?

请继续关注下集真相大白。

文章来源:智云一二三科技

文章标题:hprose for java源码分析-5

文章地址:https://www.zhihuclub.com/173078.shtml

关于作者: 智云科技

热门文章

网站地图