Ice框架提供了不少回调设施,其中一些是使用Ice远程调用进行ami模式或amd模式的支撑。本篇来看一下用于代理端的回调设施。

Ice代码中有好几个Callback相关命名的基类,并且slice还会为你描述的接口,每个接口方法生成好几个回调类,让人眼花缭乱。其实认真整理归纳一下,就会发现,为我们提供的代理调用回调接口只有两个,一个是较为底层一点的,你需要解释Future,另一个是较为高级一点的,框架为你解释好Future,并以输出参数回调。而回调的挂接点是在网络层的io读写完成事件。较为底层一点的回调接口有两个函数,分别是sent(const AsyncResultPtr&)和completed(const AsyncResultPtr&),这两个回调函数分别挂接在网络层的io写完成事件以及io读完成事件上。较为高级一点的回调接口,有sent,response和exception三个函数入口,底层的回调处理completed将读出的响应结果分类为exception或正常输出参数,分别递交给exception或response回调入口。它们的关系如下图:

OutgoingAsync是所有具体Future类的直接基类(,继承树最后一层基类),本身就是一个Callback类,挂接回调在网络层。当ConnectionI对网络io事件进行dispatch调度时,就会满足条件下相应回调OutgoingAsync。OutgoingAsync自身有一套回调框架,可以使回调同步在io执行dispatch调度的线程上,或异步执行在其它线程上。不论那种情况,Outgoing最后将回调委托在基类CallbackCompletion。CallbackCompletion再委托给上层提供的回调。通过回调离开Future层进入的是由Slice为我们生成的框架代码,这时使用的回调接口,才是我们上面说的较为底层的回调接口sent(const AsyncResultPtr&)和completed(const AsyncResultPtr&)。Future以参数方式委托给上层代码进行事件回调处理。这个委托我们是可以在创建一个Future进行指定的。我们有两种选择,一是提供较为底层的回调方式的委托,二是提供较为高级方式的版本。Slice为我们生成的5个begin_Method函数中,有两个是指定底层次版本回调,另两个是指定高级版本回调的,最一个是不使用回调的。我们看一下slice生成的begin_Method函数模板:

::Ice::AsyncResultPtr begin_Method(const ::Ice::Context& context = ::Ice::noExplicitContext);
::Ice::AsyncResultPtr begin_Method(const ::Ice::CallbackPtr& del, const ::Ice::LocalObjectPtr& cookie = );
::Ice::AsyncResultPtr begin_Method(const ::Ice::Context& context, const ::Ice::CallbackPtr& del, const ::Ice::LocalObjectPtr& cookie = );
::Ice::AsyncResultPtr begin_Method(const ::Test::Callback_MyClass_opOnewayPtr& del, const ::Ice::LocalObjectPtr& cookie = );
::Ice::AsyncResultPtr begin_Method(const ::Ice::Context& context, const ::Test::Callback_MyClass_opOnewayPtr& del, const ::Ice::LocalObjectPtr& cookie = );
::Ice::AsyncResultPtr _iceI_begin_Method(const ::Ice::Context&, const ::IceInternal::CallbackBasePtr& del, const ::Ice::LocalObjectPtr& cookie = , bool sync = false);

在每个函数的都有一个参数,类型为Callback相关,命名为del,其实就是delegate,我们提供的Callback是回调处理中的委托目标,而这个委托又会委托给我们自定义的Callback中去。

我们要如何使用代理端的远程调用的回调呢,请看下图:

红色的类,就是我们要提供给Ice的回调类,这里要注意的是,我们的自定义的回调类并不继承于任何一个Callback基类。因为我们自定义的回调类是蓝色的框架提供的回调类委托的目标,而蓝色的由框架提供的回调类,却是OutgoingAsync(基类为AsyncResult的Future)的回调委托目标。其实我们提供的回调类,到最后回调到达已经是第二层委托了。我们提供的回调类唯一要继承的就是Shared。蓝色的类就像一个Binder绑定我们的提供的回调类以及我们选择为这个回调的函数。蓝色类继承树上的子类实例是我们调用Slice为我们生成begin_Method需要传入的回调委托,它绑定我们自定义的回调类和我们选用作本次远程调用回调的函数。蓝色类的继承自CallbackBase,CallbackBase也定义了回调虚接口sent和completed,为什么我们自定义的回调不是直接继承这个有回调虚接口呢?原因是那样的话,我们的自定义的回调类就必然会很多,不利于我们维护。尽管Slice为我们生成了许多我们压根都不会使用到的类,但是我们是不用去维护这些类的。使用绑定(binder,绑定target和function selector)以及委托的方式,我们就可以方便开发出处理多种回调函数的回调类。我们的回调委托并不是固定绑定到某一个对象调用的上,而是只对应某一次对象调用,并且我们可以为每次对象调用选用自定义回调类的不同成员函数进行绑定回调处理。

我们的回调起源于网络服务层的io读写完成事件,经过AsyncResult(Future层)的回调框架最后委托到我们回调代码。那么AsyncResult(Future层)的回调框架又为我们做了些什么工作呢?首先总览AsyncResult的类图:

所有具体的Future类都多继承自两个基类CallbackCompletion和OutgoingAsync,可参考最下方同一水平层次的三个类。最顶层的基类有两员分别是,OutgoingAsyncCompletionCallback和AsyncResult,AsyncResult是一个提供给上层访问的接口,基本与回调框架无关。我们将无关的繁枝冗节去除,只留下回调框架相关的方法接口以及依赖关系,如下图:

白色的类是我们上面Future类图中的成员,并且借用OC对类(@interface)的分类声明,我们将一个类的方法按功能聚集成一个分类。

分类中的方法名与我们本篇开始提到的回调接口sent,response和exception相关。相关的方法有

sent,sentImpl,invokeSent,invokeSentAsync,handleSent, handleInvokeSent ;

response,responseImpl,invokeResponse,invokeResponseAsync,handleResponse,handleInvokeResponse ;

exception,exceptionImpl,invokeException,invokeExceptionAsync, handleException,handleInvokeException ;

这些函数的命名相当迷惑人,如果你不知道它们的真正作用又没有文档的话。sent,response和exception分别必须调用它们对应的(sent|response|exception)Impl版本,它们的Impl版本实质在调用它们各自对应的handle(Sent|Response|Exception)方法。但是这三组方法,根本不是回调处理,而只是三组查询方法,它们都返回一个bool值,表示是否支持回调。剩下的方法则都带invoke词根,但是它与AsyncResult的invoke并没有关系,而是真正要调用事件回调。invokeSent,invokeResponse和invokeException分别依赖调用handleInvokeSent,handleInvokeResponse和handleInvokeException,它们就是最后委托上层回调的地方。至于invokeSentAsync,invokeResponseAsync和invokeExceptionAsync则是将invokeSent,invokeResponse和invokeException方法委托给嵌套类,dispatch(和OC的GCD中语义一样)到线程池异步执行。invokeSent,invokeResponse和invokeException方法集是枢纽点,上面图用红字显示。当执行一次带回调需要的同步远程调用,就有一条最小的依赖路径,即上图中红色类和红字类,ConnectionI和OutgoingAsyncBase。网络服务层的Connection在调用io事件时,回调Future(OutgoingAsyncBase,which is a AsyncResult)的回调处理,再通过OutgoingAsyncCompletionCallback接口服务委托回调到上层。

ZeroC ICE的远程调用框架 Callback(一)-AMI异步方法调用框架的更多相关文章

  1. ZeroC ICE的远程调用框架 Slice如何帮助我们进行Ice异步编程(AMI,AMD)

    Slice最大的用处就是为我们使用Ice进行编程,代劳绝大部分的重复性代码,并提供一些帮助性的框架代码,如用于AMI和AMD方式进行异步编程的回调框架. 当Slice不为我们生成代码时,我们仍然可以按 ...

  2. ZeroC ICE的远程调用框架 AMD

    继上一篇<ZeroC ICE的远程调用框架>,本篇再来说其中的AMD.(本篇需要重写) 当在ice文件中声明某个接口方法Method为["amd"]后,接口方法在stu ...

  3. ZeroC ICE的远程调用框架

    想搞清楚slice为我们生成了什么样的框架代码,就先搞明白Ice的远程调用框架暗中为我们做了些什么? Ice将Ice Object的方法调用分为三个阶段(或步骤),分别是begin,process和e ...

  4. ZeroC ICE的远程调用框架 AMI与AMD -Why?

    在Ice有两种异步使用的方式,AMI和AMD.AMI是异步方法调用,AMD是异步方法调度(分派).前者用在代理端,后者用在饲服实现端. AMI其实就是在代理端,使用Future机制进行异步调用,而不阻 ...

  5. ZeroC ICE的远程调用框架 class与interface

    我们在ice文件中定义的class或interface,slice都会为我们生成stub存根类和skeleton骨架类.在这里要注意slice并没有分别生成两份单独用在客户端或服务端的接口给开发分发. ...

  6. ZeroC ICE的远程调用框架 ServantLocator与Locator

    ServantLocator定位的目标是Servant,而Locator定位的目标是“Ice Object”,即一个可定位的“Ice Object”代理.Servant是::Ice::Object的继 ...

  7. ZeroC ICE的远程调用框架 ThreadPool

    ThreadPool提供Reactor/Proactor服务,并且强偶合了Reactor(反应器)/Proactor(前摄器).不同于Reactor/Proactor使用线程池 进行事件处理的设计.如 ...

  8. ZeroC ICE的远程调用框架 代理引用地址

    在官方文档中称为Binding,协议-地址对的绑定.在Proxy模式中,一般地有三个参与者,Proxy,Subject以及RealSubject.Subject定义了Proxy(代理)和RealSub ...

  9. ZeroC ICE的远程调用框架 ASM与defaultServant,ServantLocator

    ASM与defaultServant,ServantLocator都是与调用调度(Dispatch)相关的. ASM是ServantManager中的一张二维表_servantMapMap,默认Ser ...

随机推荐

  1. 《Java并发编程实战》读书笔记-第3章 对象的共享

    可见性 在没有同步的情况下,编译器.处理器以及运行时都可能做指令重排.执行结果可能会出现错误 volatile变量 编译器与运行时不会进行指令重排,不会进行缓存,使用volatile变量要满足以下条件 ...

  2. 查看java内存情况的几个常用命令

    java 命令简单查看jvm内存使用状况 jinfo:可以输出并修改运行时的java 进程的opts. jps:与unix上的ps类似,用来显示本地的java进程,可以查看本地运行着几个java程序, ...

  3. 清理git仓库

    参考 https://harttle.land/2016/03/22/purge-large-files-in-gitrepo.html https://git-scm.com/docs/git-re ...

  4. unity message

    再用unity进行开发过程中,不可避免的用到消息的传递问题,以下介绍几种消息传递的方法: (一)拖动赋值 此方法即为最普通的方法,即把需要引用的游戏物体或者需要引用的组件拖动到相关公有变量的槽上,然后 ...

  5. Android_Fragment栈操作 commit()问题分析

    栈操作时遇到一个问题 getFragmentManager().beginTransaction() .replace(R.id.fl_container,bFragment) .addToBackS ...

  6. 【原创】从零开始搭建Electron+Vue+Webpack项目框架,一套代码,同时构建客户端、web端(二)

    摘要:上篇文章说到了如何新建工程,并启动一个最简单的Electron应用.“跑起来”了Electron,那就接着把Vue“跑起来”吧.有一点需要说明的是,webpack是贯穿这个系列始终的,我也是本着 ...

  7. 前后端对称加密(AES)

    后端实现(JAVA) package com.vcgeek.hephaestus.demo; import org.apache.commons.codec.binary.Base64; import ...

  8. 运用ffmpeg实现压缩视频

    /// <param name="filePath">ffmpeg.exe的文件路径</param> /// <param name="Pa ...

  9. Rust 中的类型转换

    1. as 运算符 as 运算符有点像 C 中的强制类型转换,区别在于,它只能用于原始类型(i32 .i64 .f32 . f64 . u8 . u32 . char 等类型),并且它是安全的. 例 ...

  10. shell 队列实现线程并发控制

    需求:并发检测1000台web服务器状态(或者并发为1000台web服务器分发文件等)如何用shell实现? 方案一:(这应该是大多数人都第一时间想到的方法吧) 思路:一个for循环1000次,顺序执 ...