ICE的異步方法調用

轉自:http://blog.sina.com.cn/s/blog_45497dfa0100nwbr.htmlhtml

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"或"斧頭"狀再做處理。app

AMI是針對客戶端而言的,當客戶端使用AMI發出遠程調用時,客戶端須要提供一個實現了回調接口的類用於接收通知。而後不等待調用完成當即返回,這時能夠繼續其它活動,當獲得服務器端的答覆時,客戶端的回調類中的方法就會被執行。異步

例:修改原Helloworld 客戶端,使用異步方法遠程調用printString。async

首先,要修改原來的Printer.ice定義文件,在printString方法前加上["ami"]元標識符。函數

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

一樣,再用slice2cpp Printer.ice生成Printer.h和Printer.cpp文件,並把這兩個文件加入原項目(若是是直接修改以前的代碼的話,由於原先已經加入了這兩個文件,這步能夠跳過)。spa

觀察生成的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提升的是服務器的負荷能力)

相關文章
相關標籤/搜索