ICE的异步方法调用

转自:http://blog.sina.com.cn/s/blog_45497dfa0100nwbr.html

http://www.cnblogs.com/mawanglin2008/articles/3411735.html

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"]元标识符。

module Demo
    {
    interface Printer
      {
        ["ami" ]  void  printString(string s);
      };
    };

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

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

namespace  Demo
   {
   class  AMI_Printer_printString :  public  ::IceInternal::OutgoingAsync
   {
   public :
       virtual   void  ice_response() = 0;
       virtual   void  ice_exception( const  ::Ice::Exception&) = 0;
   ...
   };
  }

这里的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方法:

namespace  IceProxy  //是代理类
 {
 namespace  Demo
 {
 class  Printer :  virtual   public  ::IceProxy::Ice::Object
 {
     ...
 public :
     bool  printString_async( const  ::Demo::AMI_Printer_printStringPtr&,
         const  ::std::string&);
     bool  printString_async( const  ::Demo::AMI_Printer_printStringPtr&,
         const  ::std::string&,  const  ::Ice::Context&);
     ...
 };
 }
 }

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

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

演示代码(客户端):

#include <ice/ice.h>
 #include <printer.h>
 
 using   namespace  std;
 using   namespace  Demo;
 
 //实现AMI_Printer_printString接口
 struct  APP : AMI_Printer_printString
 {
     virtual   void  ice_response()
     {
         cout << "printString完成"  << endl;
     }
     virtual   void  ice_exception( const  ::Ice::Exception& e)
     {
         cout << "出错啦:"  << e << endl;
     }
 };
 
 class  MyApp:  public  Ice::Application{
 public :
     virtual   int  run( int  argc,  char *argv[])
     {
         Ice::CommunicatorPtr ic = communicator();
 
         Ice::ObjectPrx base =
             ic->stringToProxy("SimplePrinter:default -p 10000" );
 
         Demo::PrinterPrx printer = PrinterPrx::checkedCast(base);
         if (!printer)  throw   "Invalid Proxy!" ;
       
         // 使用AMI异步调用
         printer->printString_async(new  APP, "Hello World!" );
         cout << "做点其它的事情..."  << endl;
         system("pause" );
         return  0;
     }
 };
 
 int  main( int  argc,  char * argv[])
 {
     MyApp app;
     return  app.main(argc,argv);
 }

服务端代码不变,编译运行,效果应该是调用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"]元标识符。

module Demo{
    interface Printer
    {
        ["amd" ]  void  printString(string s);
    };
    };

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

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

namespace  Demo
 {
 
 class  AMD_Printer_printString :  virtual   public  ::IceUtil::Shared
 {
 public :
 
     virtual   void  ice_response() = 0;
     virtual   void  ice_exception( const  ::std::exception&) = 0;
     virtual   void  ice_exception() = 0;
 };
 ...
 }

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

namespace  Demo
   {
   class  Printer :  virtual   public  ::Ice::Object
   {
       ...
       virtual   void  printString_async(
           const  ::Demo::AMD_Printer_printStringPtr&,
           const  ::std::string&,  const  ::Ice::Current& = ::Ice::Current()) = 0;
       ...
   };
   }

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

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

示例代码

#include <ice/ice.h>
 #include "printer.h"
 
 using   namespace  std;
 using   namespace  Demo;
 
 // 传递给线程函数的参数
 struct  CallbackEntry{
     AMD_Printer_printStringPtr callback;
     string str;
 };
 
 // 线程函数
 DWORD  WINAPI DoPrintString( LPVOID  lpParameter)
 {
     // 取得参数
     CallbackEntry *pCE = (CallbackEntry *)lpParameter;
     // 工作:打印字符(延时1秒模拟长时间操作)
     Sleep(1000);
     cout << pCE->str << endl;  
     // 回调,工作完成。如果工作异常,则调用ice_exception();
     pCE->callback->ice_response();
     // 删除参数(这里使用堆直接传递,其实更好的方法是使用队列)
     delete  pCE;
     return  TRUE;
 }
 
 struct  PrinterImp : Printer{
     virtual   void  printString_async(
         const  AMD_Printer_printStringPtr &callback,
         const  string& s,  const  Ice::Current&)
     {
         // 参数打包(回调类和pringString方法的参数)
         CallbackEntry *pCE = new  CallbackEntry;
         pCE->callback = callback;
         pCE->str = s;
         // 让Windows线程池来执行具体任务
         ::QueueUserWorkItem(DoPrintString,pCE,WT_EXECUTEDEFAULT);
     }
 };
 
 class  MyApp :  public  Ice::Application{
 public :
     virtual   int  run( int  n,  char * v[]){
         Ice::CommunicatorPtr& ic = communicator();
         Ice::ObjectAdapterPtr adapter
             = ic->createObjectAdapterWithEndpoints("SP" , "default -p 10000" );
         Ice::ObjectPtr object = new  PrinterImp;
         adapter->add(object, ic->stringToIdentity("SimplePrinter" ));
 
         adapter->activate();
         ic->waitForShutdown();
         return  0;
     }
 };
 
 int  main( int  argc,  char * argv[])
 {
     MyApp app;
     return  app.main(argc, argv);
 }

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

时间: 2024-10-12 08:39:36

ICE的异步方法调用的相关文章

ZeroC ICE的远程调用框架 AMD

继上一篇<ZeroC ICE的远程调用框架>,本篇再来说其中的AMD. 当在ice文件中声明某个接口方法Method为["amd"]后,接口方法在stub类生成的远程调用框架代码不会变,但在skeleton类生成的就不是_iceD_Method和Method,而是_iceD_Method和Method_async.而amd模式和非amd模式的代码生成模板区别在于,_iceD_Method调用Method_async代替Method,并且在调用后不进行out方向参数的处理.另

C#多线程编程之:异步方法调用

异步方法 当一个线程调用方法后,直到方法执行完毕,线程才继续执行,这种方法被称为同步方法.然而,有些方法执行时间可能非常长,比如串口操作或访问网络,这样线程被阻塞,而无法响应用户的其他请求.这种情况通常是无法忍受的,所以这时我们应该使用异步方法. 异步方法的原理是,在方法调用前为异步方法指定一个回调函数,方法调用后被线程池中的一个线程接管,执行该方法.主线程立即返回,继续执行其他工作或响应用户请求.如果异步方法执行完毕,回调函数被自动执行,以处理异步方法的调用结果. 如何实现异步方法呢?C#通过

异步方法调用【其他模式】

public class AsyncMethodInvocation { /** * Async Method Invocation[异步方法调用] */ @Test public void all() throws InterruptedException, ExecutionException { final String result = "async method invocation"; // 1)可执行的异步任务呢 final Callable<String>

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

ASM与defaultServant,ServantLocator都是与调用调度(Dispatch)相关的. ASM是ServantManager中的一张二维表_servantMapMap,默认Servant则由_defaultServantMap和_locatorMap两张一维表维护.一个对 象可由这样的字符串指定"Category/Identity -f Facet".ASM是根据Identity和Facet对进行查找,而defaultServant和 ServantLocator

ZeroC ICE的远程调用框架 ThreadPool

ThreadPool提供Reactor/Proactor服务,并且强偶合了Reactor(反应器)/Proactor(前摄器).不同于Reactor/Proactor使用线程池 进行事件处理的设计.如ACE框架的ACE_TP_Reactor.同时ThreadPool提供一个共享的工作分派队列,可以用作Half-Async/Half-sync并发模式的线程池. ThreadPool为池中每个线程定制了一至的线程循环,运行在池中的线程者必须进行这个循环,接受线程池的统一控制.此外ThreadPool

c#异步方法调用

var t1 = new Task(() => GkeyTest()); DisplaylistboxMSG("初始化:" + t1.Status.ToString()); t1.Start(); DisplaylistboxMSG("等待任务调度器分配线程:" + t1.Status.ToString()); Task.WaitAll(t1); DisplaylistboxMSG("任务执行完毕:" + t1.Status.ToStrin

ICE中间件说明文档

1       ICE中间件简介 2       平台核心功能 2.1        接口描述语言(Slice) 2.2        ICE运行时 2.2.1         通信器 2.2.2         对象适配器 2.2.3         位置透明性 2.3        异步编程模型 2.3.1         异步方法调用 2.3.2         异步方法分派 2.4        订阅/发布编程模型 2.5        动态服务管理(IceBox) 2.6       

Zeroc Ice grid 研究学习

一.概念 slice: ice提供了自己的接口定义语言.用来定义rpc的接口和对象. ice.object: rpc调用的接口必须继承自ice.Object servant:ice.Object的实例化对象叫做servant,rpc调用的就是servant对象,因此servant需要线程安全 endpoints:客户端rpc调用servant的地址 icebox:servant的容器 icenode:icebox的容器 registry:注册中心,负责管理icenode的注册,负责和发布. 二.

Silverlight中异步调用WCF服务,传入回调函数

以前学的ASP.NET,调用的都是同步方法,同步方法的好处就是,一步一步走,完成这步才会走下一步.然而,WCF使用的都是异步方法,调用之后不管有没有获得结果就直接往下走,最可恶的是异步函数都是Void类型,得不到返回结果,虽然有Completed的事件处理,但是还是感觉比较束缚,无法与前端交互. 这里就跟大家分享一种传入回调函数的方法,把前台的方法写好,传到后台,让异步方法调用完成时执行.废话不多说了,开始写代码: 首先,要先建一个带网站的sliverlight项目,这里就不细说了,在网站中添加