轉自:http://blog.sina.com.cn/s/blog_45497dfa0100nwbr.htmlhtml
http://www.cnblogs.com/mawanglin2008/articles/3411735.html服務器
下面這種狀況就是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調用方法爲:
#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執行得慢一點:
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 ,在這個方法裏,咱們要兩種方案:
#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提升的是服務器的負荷能力)