ICE的AMI和AMD:

AMI:指的是客户端的调用.一般有一个代理类,一个回调类。
从服务端返回的数据在回调类中的ice_response函数中.

AMD:指的是服务端对客户端的调用分派.一般进行业务处理需要自己写一个类继承于_Disp类。重载method_async(AMD_CALLBACK __cb,arg1,arg2,...)函数.在这个函数中调用__cb的ice_response(result)往回调类中写result.这样客户端就能够接收到回写的结果

     还有一点很有特色的是,AMI和AMD是完全互相独立的,也就是说对于同一个interface,客户端不会知道服务器是否用AMD方式相应请求,服务器端也不会知道客户端是否用AMI发起调用。 而且,他们也无需知道,这是实现的细节,不是接口的契约。

异步方法调用(Asynchronous Method Invocation,简称AMI)

下面这种情况就是AMI调用:“斧头帮”大哥(客户端)叫小弟(服务器端)去干收租的活(远程调用),并且给小弟一把烟花炮竹(回调类)。嘱咐说: “我还有其它事情要打扫打扫,如果你的事情办完了,就放'OK'烟花;如果遇到反抗,就放'斧头'烟花!”(服务器答复)。说完,这位大哥就可以放心的做 其它事去了,直到看到天上烟花盛开,根据"OK"或"斧头"状再作处理。

AMI是针对客户端而言的,当客户端使用AMI发出远程调用时,客户端需要提供一个实现了回调接口的类用于接收通知。然后不等待调用完成立即返回,这时可以继续其它活动,当得到服务器端的答复时,客户端的回调类中的方法就会被执行。

例:修改原Helloworld 客户端,使用异步方法远程调用printString。

首先,要修改原来的Printer.ice定义文件,在printString方法前加上["ami"]元标识符。

  1. module Demo{
  2. interface Printer
  3. {
  4. ["ami" ] void printString(string s);
  5. };
  6. };

同样,再用slice2cpp Printer.ice生成Printer.h和Printer.cpp文件,并把这两个文件加入原项目(如果是直接修改之前的代码的话,因为原先已经加入了这两个文件,这步可以跳过)。

观察生成的Printer.h文件,可以找到这个定义:

  1. namespace Demo
  2. {
  3. class AMI_Printer_printString : public ::IceInternal::OutgoingAsync
  4. {
  5. public :
  6. virtual void ice_response() = 0;
  7. virtual void ice_exception( const ::Ice::Exception&) = 0;
  8. ...
  9. };
  10. }

这里的AMI_Printer_printString 就是printString方法的AMI回调接口,可以发现它AMI回调接口类名的规律是AMI_类名_方法名。

这个接口提供了两个方法:

void ice_response(<params>); 

    表明操作已成功完成。各个参数代表的是操作的返回值及out 参数。如果操作的有一个非 void返回类型,ice_response 方法的第一个参数就是操作的返回值。操作的所有out 参数都按照声明时的次序,跟在返回值的后面。

void ice_exception(const Ice::Exception &); 

    表明抛出了本地或用户异常。

同时,slice2cpp还为Printer代理类生成了异步版本的printString方法:

  1. namespace IceProxy //是代理类
  2. {
  3. namespace Demo
  4. {
  5. class Printer : virtual public ::IceProxy::Ice::Object
  6. {
  7. ...
  8. public :
  9. bool printString_async( const ::Demo::AMI_Printer_printStringPtr&,
  10. const ::std::string&);
  11. bool printString_async( const ::Demo::AMI_Printer_printStringPtr&,
  12. const ::std::string&, const ::Ice::Context&);
  13. ...
  14. };
  15. }
  16. }

结合这两样东西(AMI_Printer_printString 接口和printString_async 方法),我们的客户端AMI调用方法为:

  1. 实现AMI_Printer_printString接口的ice_response和ice_exception方法,以响应远程调用完成后的工作。
  2. 把上面实现的回调对象作为printString_async的参数启动远程调用,然后可以做其它事了。
  3. 当得到服务端答复后,AMI_Printer_printString接口的ice_response的方法被调用。

演示代码(客户端):

  1. #include <ice/ice.h>
  2. #include <printer.h>
  3. using namespace std;
  4. using namespace Demo;
  5. //实现AMI_Printer_printString接口
  6. struct APP : AMI_Printer_printString
  7. {
  8. virtual void ice_response()
  9. {
  10. cout << "printString完成" << endl;
  11. }
  12. virtual void ice_exception( const ::Ice::Exception& e)
  13. {
  14. cout << "出错啦:" << e << endl;
  15. }
  16. };
  17. class MyApp: public Ice::Application{
  18. public :
  19. virtual int run( int argc, char *argv[])
  20. {
  21. Ice::CommunicatorPtr ic = communicator();
  22. Ice::ObjectPrx base =
  23. ic->stringToProxy("SimplePrinter:default -p 10000" );
  24. Demo::PrinterPrx printer = PrinterPrx::checkedCast(base);
  25. if (!printer) throw "Invalid Proxy!" ;
  26. // 使用AMI异步调用
  27. printer->printString_async(new APP, "Hello World!" );
  28. cout << "做点其它的事情..." << endl;
  29. system("pause" );
  30. return 0;
  31. }
  32. };
  33. int main( int argc, char * argv[])
  34. {
  35. MyApp app;
  36. return app.main(argc,argv);
  37. }

服务端代码不变,编译运行,效果应该是调用printer->printString_async之后还能"做点其它的事情...",当服务端完成后客户端收到通知,显示"printString完成"。

另外,为了突出异步效果,可以修改服务器端代码,故意把printString执行得慢一点:

  1. struct PrinterImp : Printer{
  2. virtual void printString( const ::std::string& s,
  3. const ::Ice::Current&)
  4. {
  5. Sleep(1000);
  6. cout << s << endl;
  7. }
  8. };

异步方法分派(Asynchronous Method Dispatch,简称AMD)

AMD是针对服务器端而言的,在同步的情况下,服务器端收到一个调用请求后,在线程池中拿出一个空闲线程用于执行这个调用。这样,服务器在同一时刻所能支持的同步请求数受到线程池大小的限制。

如果线程池内的线程都在忙于执行长时间的操作,那么新的请求到来时就会处于长时间得不到答复的状态,这可能会造成客户端长时间等待(如果客户端没使用AMI的话)。

ICE的解决方法是:服务器收到请求时并不马上执行具体工作,而是把执行这项工作所需的参数以及回调类保存到一个地方(比如队列)后就返回。而另外的线程(或线程池)负责取出保存的参数并执行之,执行结束后使用回调类通知客户端工作已完成(或异常)。

还是用上面“斧头帮”来举例:“斧头帮”大哥(客户端)叫小弟(服务器端)去干收租的活(远程调用),这位小弟并不是马上就去收租去了,而是把这件 工作记录到他的日程表里(同时还有好几个老板叫他干活呢,可怜的人啊~~)。然后等有空的时候再按日程表一项项的做(或者叫其它有空的弟兄帮忙做),做完 工作后该放烟花的就放烟花(回调智能客户端),该砍人的就放信号弹啥的。

例:修改原Helloworld 服务器端,使用异步方法分派处理printString方法。

首先,要修改原来的Printer.ice定义文件,在printString方法前加上["amd"]元标识符。

  1. module Demo{
  2. interface Printer
  3. {
  4. ["amd" ] void printString(string s);
  5. };
  6. };

同样,再用slice2cpp Printer.ice生成Printer.h和Printer.cpp文件,并把这两个文件加入原项目(如果是直接修改之前的代码的话,因为原先已经加入了这两个文件,这步可以跳过)。

观察生成的Printer.h文件,可以发现和AMI类似的一个回调接口AMD_Printer_printString :

  1. namespace Demo
  2. {
  3. class AMD_Printer_printString : virtual public ::IceUtil::Shared
  4. {
  5. public :
  6. virtual void ice_response() = 0;
  7. virtual void ice_exception( const ::std::exception&) = 0;
  8. virtual void ice_exception() = 0;
  9. };
  10. ...
  11. }

这个回调接口由ICE自己实现,我们只要拿来用就可以了。在哪里用呢?马上就会发现:我们要实现的Printer接口的printString 方法不见了,取而代之的是printString_async 方法:

  1. namespace Demo
  2. {
  3. class Printer : virtual public ::Ice::Object
  4. {
  5. ...
  6. virtual void printString_async(
  7. const ::Demo::AMD_Printer_printStringPtr&,
  8. const ::std::string&, const ::Ice::Current& = ::Ice::Current()) = 0;
  9. ...
  10. };
  11. }

这个printString_async 方法就是我们要实现的异步分派方法,它的第一个参数就是由ICE实现的回调类AMD_Printer_printString ,在这个方法里,我们要两种方案:

  1. 直接做具体工作,完成后在末尾调用回调类的ice_response方法告知客户端已完成。这种方案就和之前普通版的服务端一样,是同步执行的。
  2. 把 回调类和请求所需要的参数放入一个指定的位置,再由其它线程取出执行和通知客户端。这种方案就是异步分派方法,具体实现时还可以有多种方式,如使用命令模 式把参数和具体操作直接封装成一个对象放入队列,然后由另一线程(或线程池)取出执行。后面的示例代码为了简单起见直接使用了Windows API中的线程池功能,而且也没有使用队列。

示例代码

  1. #include <ice/ice.h>
  2. #include "printer.h"
  3. using namespace std;
  4. using namespace Demo;
  5. // 传递给线程函数的参数
  6. struct CallbackEntry{
  7. AMD_Printer_printStringPtr callback;
  8. string str;
  9. };
  10. // 线程函数
  11. DWORD WINAPI DoPrintString( LPVOID lpParameter)
  12. {
  13. // 取得参数
  14. CallbackEntry *pCE = (CallbackEntry *)lpParameter;
  15. // 工作:打印字符(延时1秒模拟长时间操作)
  16. Sleep(1000);
  17. cout << pCE->str << endl;
  18. // 回调,工作完成。如果工作异常,则调用ice_exception();
  19. pCE->callback->ice_response();
  20. // 删除参数(这里使用堆直接传递,其实更好的方法是使用队列)
  21. delete pCE;
  22. return TRUE;
  23. }
  24. struct PrinterImp : Printer{
  25. virtual void printString_async(
  26. const AMD_Printer_printStringPtr &callback,
  27. const string& s, const Ice::Current&)
  28. {
  29. // 参数打包(回调类和pringString方法的参数)
  30. CallbackEntry *pCE = new CallbackEntry;
  31. pCE->callback = callback;
  32. pCE->str = s;
  33. // 让Windows线程池来执行具体任务
  34. ::QueueUserWorkItem(DoPrintString,pCE,WT_EXECUTEDEFAULT);
  35. }
  36. };
  37. class MyApp : public Ice::Application{
  38. public :
  39. virtual int run( int n, char * v[]){
  40. Ice::CommunicatorPtr& ic = communicator();
  41. Ice::ObjectAdapterPtr adapter
  42. = ic->createObjectAdapterWithEndpoints("SP" , "default -p 10000" );
  43. Ice::ObjectPtr object = new PrinterImp;
  44. adapter->add(object, ic->stringToIdentity("SimplePrinter" ));
  45. adapter->activate();
  46. ic->waitForShutdown();
  47. return 0;
  48. }
  49. };
  50. int main( int argc, char * argv[])
  51. {
  52. MyApp app;
  53. return app.main(argc, argv);
  54. }

客户端不需要改变,编译运行服务器然后用客户端测试效果。(其实效果不是很明显,因为AMD提高的是服务器的负荷能力)

ICE学习——异步1的更多相关文章

  1. 移动开发iOS&Android对比学习--异步处理

    在移动开发里很多时候需要用到异步处理.Android的主线程如果等待超过一定时间的时候直接出现ANR(对不熟悉Android的朋友这里需要解释一下什么叫ANR.ANR就是Application Not ...

  2. ICE学习笔记一----运行官方的java版demo程序

    建议新手和我一样,从官网下载英文文档,开个有道词典,慢慢啃. 官方文档下载: http://download.csdn.net/detail/xiong_mao_1/6300631 程序代码就不说了, ...

  3. JS三座大山再学习 ---- 异步和单线程

    本文已发布在西瓜君的个人博客,原文传送门 前言 写这一篇的时候,西瓜君查阅了很多资料和文章,但是相当多的文章写的都很简单,甚至互相之间有矛盾,这让我很困扰:同时也让我坚定了要写出一篇好的关于JS异步. ...

  4. ZeroC ICE java异步实现方式(ami/amd)

    首先说说ami 和amd 的区别(以下为个人见解,仅供参考.如有疑问欢迎提出来) ami (异步方法调用): 仅仅基于ice 的同步方式扩展了异步的扩展方式,其他理念改动不大,使用起来好理解,但是服务 ...

  5. ICE学习第四步-----客户端请求服务器返回数据

    这次我们来做一个例子,流程很简单:客户端向服务器发送一条指令,服务端接收到这条指令之后,向客户端发送数据库中查询到的数据,最终显示在DataGridView上. 根据上一篇文章介绍的Slice语法,我 ...

  6. ICE学习第三步-----Slice语言

    ICE:Slice语言(一)-编译 Introduce简介 Slice(Specification language for ice)是分离对象和对象的实现的基础的抽象机制.Slice在客户端和服务器 ...

  7. ICE学习第二步-----从第一个程序了解ICE(HelloWorld)

    ICE(Internet Communications Engine)是一种面向对象的中间件平台,主要用于网络通讯.它为面向对象的“客户端-服务器”模型的应用提供了一组很好的工具和API接口.目前在全 ...

  8. ICE学习第一步-----配置ICE环境变量

    安装 ICE: 1.下载ICE: http://www.zeroc.com/download.html 下载说明:ICE支持语言(C++, Java, C#, Visual Basic,Python, ...

  9. 6.26学习 异步委托回调函数 VS 多线程 VS 并行处理

    描述: 我现在是轮询着构建实例,然后这个实例去执行一个方法,但是执行方法需要大约10s时间,全部轮询下来需要很长时间.所以我现在要更改,头给了我两个方法,1多线程 2异步委托回调函数. 异步委托回调函 ...

随机推荐

  1. thinkphp禁止模版标签解析

    场景: 页面中某些样式或者js中含有tp定义的模版标签,如果被tp当成模版标签解析,就会解析异常. tp中提供了<literal></literal>标签用于禁止标签内部的代码 ...

  2. [tty与uart]理解线路规程的作用

    转自:http://biancheng.dnbcw.info/linux/336240.html Linux OS的设备驱动有相当经典的抽象思想以及分层思想.与通信世界里面的思想相一致. 一.在Lin ...

  3. 黄聪:Wordpress、PHP使用POST数据过大导致MySQL server has gone away报错原因分析

    错误原因: 当POST的数据超过 max_allowed_packet 就会报 MySQL server has gone away 的错误. 1.查看当前Mysql的 max_allowed_pac ...

  4. eclipse下编译hadoop源代码(转)

    hadoop是一个分布式存储和分布式计算的框架.在日常使用hadoop时,我们会发现hadoop不能完全满足我们的需要,我们可能需要修改hadoop的源代码并重新编译.打包. 下面将详细描述如何从sv ...

  5. 1. Netty解决Tcp粘包拆包

    一. TCP粘包问题 实际发送的消息, 可能会被TCP拆分成很多数据包发送, 也可能把很多消息组合成一个数据包发送 粘包拆包发生的原因 (1) 应用程序一次写的字节大小超过socket发送缓冲区大小 ...

  6. java.lang.ClassNotFoundException: org.eclipse.jetty.plus.webapp.EnvConfiguration

    最近刚接触jetty,在myeclipse8.6中加入了一个项目,运行时就出了这个java.lang.ClassNotFoundException: org.eclipse.jetty.plus.we ...

  7. NeHe OpenGL教程 第三十五课:播放AVI

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. python(19)编码问题

    转载链接:http://blog.csdn.net/lxdcyh/article/details/4018054 python的编码问题,一直都让人很头疼,今天就整理下python的编码知识. 首先: ...

  9. 图片_ _优化Bitmap加载图片1

    ===========  1   视图显示大量图片时的内存问题 setBackgroundResource 回去res 资源文件里面找适配手机当前屏幕的文件,所以消耗高,etBackgroundDra ...

  10. Python从2.6升级到2.7,使用pip安装module,报错:No Module named pip.log(转载)

    From:http://blog.csdn.net/iefreer/article/details/8086834 python升级后,使用pip安装module,错误: 错误原因:版本升级后,之前的 ...