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. C语言每日一题之No.8

    正式面对自己第二天,突然一种强烈的要放弃的冲动,在害怕什么?害怕很难赶上步伐?害怕这样坚持到底是对还是错?估计是今天那个来了,所以身体激素有变化导致情绪起伏比较大比较神经质吧(☆_☆)~矮油,女人每个 ...

  2. c++中的peek函数

    c++中 cin.peek()函数 其返回值是一个char型的字符,返回值是指针指向的当前字符, 但是只是观测,指针任停留在当前位置,并不后移.如果要访问的字符是文件结束符,则函数值是EOF(-1); ...

  3. 黄聪:Mysql5.6缓存命中率

    MySQL缓存命中率,网上说法不一,下面我说下我的看法,大家轻拍: 总的select查询数等于com_select(没命中) + qcache_hits(命中) + 解析错误的查询. 再来看看Com_ ...

  4. C#笔记一 .Net Framwork

    参考Learning hard本人在博客园的主页: http://www.cnblogs.com/zhili/     以及本书中的一些知识点: http://www.cnblogs.com/zhil ...

  5. WebAPI 安全性 使用TOKEN+签名验证(上)

    首先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候,会面临着许多的安全性问题, ...

  6. IGS_学习笔记08_IREP通过soapUI测试客户化Web Service调用(案例)

    20150819 Created By BaoXinjian

  7. codeforces 258C Little Elephant and LCM 组合数学 枚举

    题意: input : n a1,a2,...,an 1 <= n <= 10^5 1 <= ai <= 10^5 求b数组的方案数,b数组满足: 1. 1 <= bi ...

  8. CF 369C . Valera and Elections tree dfs 好题

    C. Valera and Elections   The city Valera lives in is going to hold elections to the city Parliament ...

  9. CF 219D Choosing Capital for Treeland 树形DP 好题

    一个国家,有n座城市,编号为1~n,有n-1条有向边 如果不考虑边的有向性,这n个城市刚好构成一棵树 现在国王要在这n个城市中选择一个作为首都 要求:从首都可以到达这个国家的任何一个城市(边是有向的) ...

  10. uboot 的内存命令使用 mw (修改) md (显示)

    修改:mw [内存地址] [值] [长度] 例如:mw 0x02000000 0 128 表示修改地址为0x02000000~0x02000000+128的内存值为0. 显示:md [内存地址] [长 ...