当前位置:首页 >> 信息与通信 >>

ICE系列培训(二)


ICE系列培训(二)
黄亮 huangliang@fiberhome.com 统一网管平台专项

培训内容
ICE中的异步程序设计方法(AMD,AMI) 1.AMI:Asynchronous Method Invocation 异步方法调用 2.AMD:Asynchronous Method Dispatch 异步方法派发

r /> 1. 概述
1.1 同步模型与异步模型区别 1.2 AMI概述 1.3 AMD概述 1.4 元数据(metadata)与AMI,AMD

1.1 同步模型与异步模型区别
两者的区别:

同步模型中发出调用的线程会阻塞到操 作返回。异步模型则正好相反,发出调用的 线程不会阻塞的等待调用结果的返回。
Ice 在本质上是一个异步的中间件平台,出于对应用 (及其程序员)的考虑而模拟了同步的行为。当 Ice 应用通 过代理、向远地对象发出同步的双向调用时,发出调用的线 程会阻塞起来,以模拟同步的方法调用。在此期间, Ice run time在后台运行,处理消息,直到收到所需的答复为止,然 后发出调用的线程就可以解除阻塞并解编结果。

1.2 AMI概述
AMI: 异步方法调用这个术语描述的是客户端 的异步编程模型支持。 使用 AMI发出远地调用,在 Ice run time 等待答复的同时,发出调用的线程不会阻塞。 相反,发出调用的线程可以继续进行各种活 动,当答复最终到达时, Ice run time会通知 应用。通过回调发给应用对象通知

1.3 AMD概述
AMD:
使用 AMD 时,服务器可以接收一个请求,然后挂起 其处理,以尽快释放分派线程。当处理恢复、结果已得出时, 服务器要使用 Ice run time 提供的回调对象,显式地发送响 应。 AMD 操作通常会把请求数据 (也就是,回调对象和 操作参数)放入队列 ,供应用的某个线程 (或线程池)随 后处理用。这样,服务器就使分派线程的使用率降到了最低 限度,能够高效地支持数千并发客户。

1.3 AMD概述
为什么要使用AMD:
(1)默认情况下:一个服务器在同一时刻所能支持的同步 请求数受到 Ice run time的服务器线程池的尺寸限制 。如果 所有线程都在忙于分派长时间运行的操作,那么就没有线程 可用于处理新的请求,客户就会体验到不可接受的无响应状 态。 (2)Ice run time的服务器线程池尺寸受限于主机的CPU数。

1.4 元数据与AMI,AMD
1)ICE自3.4以后,提供了一套全新的AMI(异步调用方式)的API,新的API 已不需要在slice文件中标记[“ami”]这样的元数据。 2)ICE提供向下兼容性,支持[“ami”]的语言标记,但已标注为 deprecated. 3) 服务端使用AMD异步派发方式编程,仍然必须在slice文件中,对slice 定义标注[“amd”]元数据。 Tips: 给客户提供某个调用方法的同步和异步版本是有益的,但如果在服 务器中这样做,程序员就必须实现两种版本的分派方法,并不能带来切 实的好处,而且还会带来若干潜在的缺陷。

1.4 元数据与AMI,AMD
AMD元数据标记方式:
1)接口级(interface) ["amd"] interface I { bool isValid(); float computeRate(); }; 2) 操作级(operation) interface J { ["amd"] void startProcess(); int endProcess(); };

1.4 元数据与AMI,AMD
AMD元数据的标记方式 Tips:
在操作层面、而不是接口或类的层面指定元数据,不 仅能使所生成的代码的数量降到最低限度,而且,更重要的 是,它还能使复杂度降到最低限度。尽管异步模型更为灵活, 它的使用也更复杂。因此,最好只把它用于那些能从中获得 某种好处的操作;对其他操作则使用更简单的同步模型。

2. AMI编程
2.1 基本API 2.2 AsynResult类 2.3 轮询方式的完成通知(Polling for Completion) 2.4 通用的完成通知回调函数(Generic Completion Callbacks) 2.5 类型安全的回调(Type-Safe Completion Callbacks) 2.6 单路调用(Oneway Invocations) 2.7 流量控制(Flow Control) 2.8 批量请求(Batch Requests) 2.9 并发(Concurrency) 2.10 限制(Limitations) 2.11 示例

2.1 Slice介绍
(1)Slice是什么 Slice(Specification Language for Ice)是分离对象 接口和实现的重要抽象机制。Slice建立了客户和 服务器之间的契约,这些契约描述了应用所需的 类型及对象接口。这些描述是中立与语言的,所 以客户端和服务端可以使用不同的实现语言。 Slice提供编译工具将Slice定义编译为具体语言的 实现代码,即客户端的Proxy code和服务端的 Skeleton,这样用户只用把注意力放在业务逻辑的 处理上。

2.1 Basic Asynchronous API
1.异步方法通知(AMI)API
ICE从3.4开始采用新的AMI API: module Demo { interface Employees { string getName(int number); }; }; 1)不必再显式的注明[“ami”]元数据,slice工具会自动的生产同步调用与异步调用的 stub代码。 Ice::AsyncResultPtr begin_getName(Ice::Int number); Ice::AsyncResultPtr begin_getName(Ice::Int number, const Ice::Context& __ctx); std::string end_getName(const Ice::AsyncResultPtr&);

2.1 Basic Asynchronous API
(2)begin_xxxMethod负责向队列中插入一个调用的请求,当请求插入到队列后,便可立即返回, 不必阻塞的等待调用完成。 (3)end_xxxMethod负责收集异步调用的结果。当异步调用的结果还未处理完成时,调用 end_xxxMethod方法会阻塞到异步调用完成处理结果。换句话说,如果结果已完成,调用 end_xxxMethod方法会立即返回。 示例: EmployeesPrx e = ...; Ice::AsyncResultPtr r = e->begin_getName(99); // Continue to do other things here... string name = e->end_getName(r);

Tips:
AsyncResultPtr是AsynResult的智能指针,存储了ICE运行时对于异步调用的状态追踪的信息。在 调用begin_xxxMethod方法相关的end_xxxMethod方法时,必须传入相应的AsyncResultPtr对象

2.1 Basic Asynchronous API
(4)异步方法调用中的参数 Slice定义的方法参数在AMI的begin_,end_方法中有相应的变化: double op(int inp1, string inp2, out bool outp1, out long outp2); AMI: Ice::AsyncResultPtr begin_op(Ice::Int inp1, const ::std::string& inp2) Ice::Double end_op(bool& outp1, Ice::Long& outp2, const Ice::AsyncResultPtr&); 注意: 1)slice定义中的输入参数转移为begin_op中的唯一输入参数。 2)slice定义中的输出参数作为end_op的输出参数,end_op的返回值为 原slice定义的返回值。 3)Begin_op返回AsyncResultPtr作为end_op的输入参数。

2.1 Basic Asynchronous API
(5)异常处理(Exception Handling)
1)通常情况下,当异步调用过程中发生异常,在 end_xxxMethod中抛出,即使异常发生在begin_xxxMethod中。 2)但有2种例外的情况: ?当销毁了当前的Communicator后,再调用异步方法,会抛 出CommunicatorDestroyedException异常。 ?当调用方式出错时,抛出IceUtil::IllegalArgumentException异 常(begin_,end_配对错误,调用的方法参数错误)

2.2AsynResult类
(1)AsynResult类声明
class AsyncResult? : virtual public IceUtil::Shared,? private IceUtil::noncopyable {? public:? virtual bool operator==(const AsyncResult&) const;? virtual bool operator<(const AsyncResult&) const;? ? virtual Int getHash() const;? virtual CommunicatorPtr getCommunicator() const;? virtual ConnectionPtr getConnection() const;? virtual ObjectPrx getProxy() const;? const string& getOperation() const;? LocalObjectPtr getCookie() const;? ? bool isCompleted() const;? void waitForCompleted();? ? bool isSent() const;? void waitForSent();?? bool sentSynchronously() const;? };

AsyncResult类由begin_op方法返回, 封装了异步调用的有关信息

2.2AsynResult类
(2) 方法说明
bool operator==(const AsyncResult&) const bool operator<(const AsyncResult&) const Int getHash() const 比较函数与取hash值,用于AsynResult对象之间的比较,排序。特别是在外部有大量异 步调用请求,在begin_op与end_op之间需要传递AsynResult对象时需要存储AysnResult对象 时,这些方法可以将AsynResult对象作为主键存储在类似map这样的关联容器中,存储每个 异步调用的状态。 CommunicatorPtr getCommunicator() const 获取发送当前异步调用的通信器对象。 virtual ConnectionPtr getConnection() const 获取当前调用使用的连接对象

2.2AsynResult类
virtual ObjectPrx getProxy() const 获取发送异步调用的代理对象 const string& getOperation() const 获取当前异步调用方法的名称 LocalObjectPtr getCookie() const 获取begin_op发送的Cookie对象(后续介绍),如果begin_op没有发送Cookie,则返回Null bool isCompleted() const 判断当前异步调用是否完成,当异步调用的结果数据的状态为有效时,返回true;反之, 若结果数据状态为无效时,返回false void waitForCompleted() 阻塞调用者,直达异步调用的结果数据状态为有效。

2.2 AsynResult类
bool isSent() const 当begin_op操作被调用时,Ice运行时会将请求从客户侧的发送器发出,如果发送被客户侧 的发送器拒绝,则向客户端侧的传输队列插入相关的调用请求。当这个请求成功发送时, 返回true,当请求还在队列中时,返回false void waitForSent() 阻塞等待客户侧将调用请求发出
bool sentSynchronously() const 当调用请求在第一次发送的时候,如果没有入队直接发出,返回true;反之返回false

2.6 完成通知
1.轮询方式的完成通知 (Polling for Completion) 2.通用回调函数方式的完成通知 (Generic Completion Callbacks) 3.类型安全回调函数方式的完成通知 (Type-Safe Completion Callbacks)

2.6 完成通知
1.轮询方式的完成通知
interface FileTransfer { void send(int offset, ByteSeq bytes);? };
FileHandle file = open(...);? FileTransferPrx ft = ...;? const int chunkSize = ...;? Ice::Int offset = 0;? list<Ice::AsyncResultPtr> results;? const int numRequests = 5;? while (!file.eof()) {? ByteSeq bs;? bs = file.read(chunkSize);? // Send up to numRequests + 1 chunks asynchronously.? Ice::AsyncResultPtr r = ft->begin_send(offset, bs);? offset += bs.size();? ? // 等待请求完成发送 r->waitForSent();? results.push_back(r);? //超过了水位标则阻塞等待.? while (results.size() > numRequests) {? Ice::AsyncResultPtr r = results.front();? results.pop_front();? r->waitForCompleted();? }? }?? //等待剩余的请求完成.? while (!results.empty()) {? Ice::AsyncResultPtr r = results.front();? results.pop_front();? r->waitForCompleted();? }

//同步发送 FileHandle file = open(...); FileTransferPrx ft = ...; const int chunkSize = ...;
Ice::Int offset = 0; while (!file.eof()) { ByteSeq bs; // Read a chunk bs = file.read(chunkSize); // Send the chunk ft->send(offset, bs); offset += bs.size(); }

2.6 完成通知
1.轮询方式的完成通知 1)同步方式与异步方式效率上的差距 2)异步方式AsyncResult队列控制 提高传输效率 控制内存占用

2.6 完成通知
2.通用回调函数方式的完成通知
相对于轮询方式的优点: 解除应用逻辑与异步调用机制的耦合性。利用Ice 运行时完成客户端的异步处理逻辑。 Slice提供的重载方法,红色高亮部分是回调完成通知的特征。
Ice::AsyncResultPtr begin_getName(? Ice::Int number,? const Ice::CallbackPtr& __del,? const Ice::LocalObjectPtr& __cookie = 0);? ? Ice::AsyncResultPtr begin_getName(? Ice::Int number,? const Ice::Context& __ctx,? const Ice::CallbackPtr& __del,? const Ice::LocalObjectPtr& __cookie = 0);

Ice::CallbackPtr: Ice运行时提供的回调函数智能指针,存储了自定义的回调函数实例。 当发生回调时,Ice运行时会执行回调函数对象上绑定的方法。

2.6 完成通知
3.自定义的回调对象示例:
class MyCallback : public IceUtil::Shared {? public:? void finished(const Ice::AsyncResultPtr& r) {? EmployeesPrx e =? EmployeesPrx::uncheckedCast(r->getProxy());? try {? string name = e->end_getName(r);? cout << "Name is: " << name << endl;? } catch (const Ice::Exception& ex) {? cerr << "Exception is: " << ex << endl;? }? }? };? typedef IceUtil::Handle<MyCallback> MyCallbackPtr; ? MyCallbackPtr cb = new MyCallback;? Ice::CallbackPtr d = Ice::newCallback(cb, &MyCallback::finished);? ? e->begin_getName(99, d);

// 应用程序AMI调用示例:
EmployeesPrx e = ...;?

回调对象封装了异步调用实质的业务逻辑,异步调用本身是发出非阻塞的调用请求,但重 要的是发出了请求以后,应用逻辑该如何执行。

2.6 完成通知
4.回调对象的编写要点
1)继承IceUtil::Shared 2) 回调对象中的回调方法,必须符合以下声明方式:
void callback_method(AsyncResultPtr& r)

3)回调对象中的回调方法名称可以为合乎语法的任意名称。 4)回调方法中必须调用end_xxxMethod(); 5)回调方法必须捕获所有可能由end_xxxMethod抛出的异常, 如果遗漏了某异常,Ice运行时默认会在日志中记录,但也会 忽略这个异常(设置Ice.Warn.AMICallback属性为0,可以不记 录日志)。

2.6 完成通知
5.应用程序AMI调用示例:
EmployeesPrx e = ...;? ? MyCallbackPtr cb = new MyCallback;? Ice::CallbackPtr d = Ice::newCallback(cb, &MyCallback::finished);? ? e->begin_getName(99, d);

注意: 1)Ice::newCallback辅助函数,将自定义的回调对象智能指针与 回调方法绑定后,返回Ice::CallbackPtr类型的对象。 2)begin_xxxMethod,需要输入一个Ice::CallbackPtr对象。

2.6 完成通知
6.回调中的Cookie
Ice::AsyncResultPtr begin_getName(? Ice::Int number,? const Ice::CallbackPtr& __del,? const Ice::LocalObjectPtr& __cookie = 0);? ? Ice::AsyncResultPtr begin_getName(? Ice::Int number,? const Ice::Context& __ctx,? const Ice::CallbackPtr& __del,? const Ice::LocalObjectPtr& __cookie = 0); Cookie对象在异步方法调用中用于begin_xxxMethod与end_xxxMethod之间传递信息。

应用场景:
用户在界面上点击产生大量的异步调用,当这些异步调用完成后,每个调用需要更新不同用户界面 组件。在这种情景下,每个调用需要携带它所需要更新的组件信息才能完成操作。 在这种异步调用与回调函数之间需要交互的情况下,可以通过Cookie在应用中绑定相关信息。

2.6 完成通知
7.Cookie的编写
为了满足不同的应用场景,Cookie对象可以自定义。 Cookie编写的唯一要求就是要继承Ice::LocalObject

示例:
class Cookie : public Ice::LocalObject? {? public:? Cookie(WidgetHandle h) : _h(h) {}? WidgetHandle getWidget() { return _h; }? ? private:? WidgetHandle _h;? };? typedef IceUtil::Handle<Cookie> CookiePtr; ////////////////////////////////////////////////////////////// CookiePtr cookie1 = new Cookie(widgetHandle1);? ? // Make cookie for call to getName(42);? CookiePtr cookie2 = new Cookie(widgetHandle2);? ? // Invoke the getName operation with different cookies.? e->begin_getName(99, getNameCB, cookie1);? e->begin_getName(24, getNameCB, cookie2);

void? MyCallback::getName(const Ice::AsyncResultPtr& r)? {? EmployeesPrx e = EmployeesPrx::uncheckedCast(r->getProxy());? CookiePtr cookie = CookiePtr::dynamicCast(r->getCookie());? try {? string name = e->end_getName(r);? cookie->getWidget()->writeString(name);? } catch (const Ice::Exception& ex) {? handleException(ex);? }? }

1)在应用逻辑中创建了Cookie对象 2)将Cookie对象传递给begin_getName方法 3)在对应的回调方法中重新获得Cookie(需要向下 转型) 4)从Cookie对象中获得需要的信息

2.6 完成通知
8.类型安全回调函数方式的完成通知
(1)通用回调方法存在一些类型安全方面的隐患 ? 在回调方法中调用end_xxxMethod前必须将代理对象向下转型 为正确的代理对象类型
EmployeesPrx e = EmployeesPrx::uncheckedCast(r->getProxy());
? ?

调用的end_xxxMethod必须与begin_xxxMethod配对 如果使用了Cookie,那么也需要将Cookie向下转型为正确的 Cookie类型。

CookiePtr cookie = CookiePtr::dynamicCast(r->getCookie());
?

在调用end_xxxMethod方法时,必须要捕获所有可能的异常。 否则无法获知调用在何时失败

Slice工具提供了一套Callback类API解决上述类型安全问题。

2.6 完成通知
8.类型安全回调函数方式的完成通知
(2)Slice工具与类型安全的回调 Slice工具在编译slice文件时,生成一系列相关的回调函数类 别,在这些自动生成的代码中,将类型转换,调用 end_xxxMethod等通用回调方法中的程式化方法以自动生成 的方式代替手工编程。(C++中是以模板的方式实现不同回 调函数的模板实例)

2.6 完成通知
8.类型安全回调函数方式的完成通知
(3)类型安全的回调函数实现要点 ? Slice工具为slice定义的接口中的接口方法自动生成类型安 全的回调类型。 ? 自定义回调类型实现具体的回调操作,处理业务逻辑。
?

?

必须声明一个正常情况下处理回调的方法,该方法返回void类型,参数列表与slice 定义一致。当slice返回类型不是void的时候,则第一个参数用于返回值,后续参数 与slice定义中的参数列表一致。 必须声明一个异常情况下,处理异常的方法(无返回值,只有一个const Ice::Exception& ex 输入参数)。

?

异步方法调用下接口方法对应的类型安全的回调类型:
<module>::Callback_<interface>_<operation>Ptr

?

辅助回调函数用于将自定义的回调类型绑定到类型安全的 回调类型实例上:<module>::newCallback_<interface>_<operation>

2.6 完成通知
9.类型安全的回调与通用回调方式的比较
1)回调方法定义不同
通用回调:
class MyCallback : public IceUtil::Shared {? public:? // 回调函数参数为const Ice::AsyncResultPtr& void finished(const Ice::AsyncResultPtr& r) { … ?}? };

2)调用方式的差别
通用回调:
EmployeesPrx e = ...;? ? MyCallbackPtr cb = new MyCallback;? Ice::CallbackPtr d = Ice::newCallback(cb, &MyCallback::finished);? e->begin_getName(99, d);

类型安全的回调
class MyCallback : public IceUtil::Shared? {? public:? void getNameCB(int number) {? …? } void failureCB(const Ice::Exception& ex) {? cerr << "Exception is: << ex << endl;? }? }? 在回调函数内自行控制类型转换, 以及调用end_xxxmethod方法

类型安全的回调:
MyCallbackPtr cb = new MyCallback;? Callback_Employees_getNamePtr getNameCB =? newCallback_Employees_getName(? cb, &MyCallback::getNameCB, &MyCallback::failureCB);?

?
e->begin_getName(99, getNameCB);?

2.6 完成通知
10.类型安全的Cookie调用方式
MyCallbackPtr cb = new MyCallback; Callback_Employees_getNamePtr getNameCB = newCallback_Employees_getName( cb, &MyCallback::getNameCB, &MyCallback::failureCB); CookiePtr cookie = new Cookie(widgetHandle); e->begin_getName(99, getNameCB, cookie);

class MyCallback : public IceUtil::Shared { public: void getNameCB(const string& name, const CookiePtr& cookie) { cookie->getWidget()>writeString(name); } }

2.6 完成通知
11.类型安全回调方式使用总结 ? 声明自定义回调对象实例 ? 声明类型安全的回调对象实例 ? 声明类型安全回调对象实例时,利用辅助 函数构造回调对象实例 ? 辅助函数中需要传递:
? ?

?

自定义回调对象实例 回调方法 失败处理的回调方法

2.7 单路调用
1)通用的回调方式由于回调类别中回调函数具有输入参数, void callback_method(AsyncResultPtr& r) 因此是双路调用,无法调用单路的代理对象方法。 2)类型安全的回调方式,通过newCallback辅助方法,在回调函 数对象中只指定失败的回调函数,可以调用单路方法。 示例: MyCallbackPtr cb = new MyCallback;

Ice::Callback_Object_ice_pingPtr callback = Ice::newCallback_Object_ice_ping(cb,&MyCallback::failureCB);
p->begin_opVoid(callback);

2.8 流量控制
1.流量控制的起因:
客户端并发请求非常频繁,服务端处理能力赶不上客户 端发送异步请求的速度。客户端将请求发往客户端Ice运行时 请求队列,队列不断增长,在不加流量控制的情况下,最终 耗尽内存。

2.流量控制的功能:
Ice提供了一套流量控制管理的API,应用了流量管理的api 后,当客户端的请求数超过了限定的阈值,则阻塞客户端发 起的新的操作请求直到队列中完成部分请求的处理,有剩余 的空间容纳入队的请求。

2.8 流量控制
4.流量控制回调函数定义
1)通用回调方式
void sent(const Ice::AsyncResult&); 调用情景1和情景2的判断由程序员调用AsyncResult:: sentSynchronously判断,自行编写逻 辑。

2)类型安全的回调方式
void sent(bool sentSynchronously); void sent(bool sentSynchronously, const <CookiePtr>& cookie); 调用情景1与情景2的判断由Ice运行时传递的sendSynchronously参数判断。

2.8 流量控制
3.流量控制的编程要点:
流量控制的基本思想是自定义流量控制的方法。
class MyCallback : public IceUtil::Shared { public: void finished(const Ice::AsyncResultPtr&); void sent(const Ice::AsyncResultPtr&);?// 发送请求的方法,名字可以自定义 }; typedef IceUtil::Handle<MyCallback> MyCallbackPtr; 情景1:当Ice运行时将请求发送到客户端的发送端上后,由调用begin_xxxMethod的方法 的线程接着调用send方法。 ?情景2:当Ice运行时将请求入队时,则由另一线程在Ice运行时将队列中请求发送到发送端 后,调用send方法。 ?以上2种情况调用send的const Ice::AsyncResultPtr& 参数的sentSynchronously方法判断。 ?在send方法中对请求数进行计数,当队列的长度达到高水位标,则阻塞,当运行时将请求 发往发送端,则减少计数。
?

2.9 批量调用
1.批量调用的起因: 对于大量的零散单路调用或数据包请求,服 务端在处理时需要频繁的在用户态和内核态 之间转换,代价非常高昂。因此引入批量调 用。
批量调用的细节可以参考ICE手册32.16节。

2.9 批量调用
2.批量调用的方法:
Ice运行时提供了将客户端对象代理转换为批量调用代理的方法:
// C++ namespace IceProxy { namespace Ice { class Object : /* ... */ { public: //将普通代理转换为批量调用的代理 Ice::ObjectPrx ice_batchOneway() const; Ice::ObjectPrx ice_batchDatagram() const; void ice_flushBatchRequests(); // ... }; } }

2.9 批量调用
3.显式调用与隐式调用 :
显式同步调用: ice_flushBatchRequests 显式异步调用: begin_ice_flushBatchRequests end_ice_flushBatchRequests 隐式调用: Ice.BatchAutoFlush=1(默认) Ice.MessageSizeMax=1MB(默认)

2.10 AMI的并发
Ice运行时通常情况下由独立的线程调用异步 回调方法中的回调函数。因此在回调函数中 可以使用非递归锁而不怕出现死锁的情况。
?

但如果定义了流量控制回调函数,则如前所 述的规则,在不同的情景下,有不同的线程 选择规则。
?

2.11 AMI的限制
采用了AMI异步回调,则不能采用collocated optimization优化。否则抛出 CollocationOptimizationException。 ?collocated optimization优化 参见ICE手册 32.21节
?

2.12 示例1
slice文件:
#ifndef HELLO_ICE #define HELLO_ICE module Demo { exception RequestCanceledException { }; interface Hello { // 注意元数据定义 ["ami", "amd"] idempotent void sayHello(int delay) throws RequestCanceledException; void shutdown(); }; };

#endif

2.12 示例1
int AsyncClient::run(int argc, char* argv[]) { if(argc > 1) { cerr << appName() << ": too many arguments" << endl; return EXIT_FAILURE; } HelloPrx hello = HelloPrx::checkedCast(communicator()>propertyToProxy("Hello.Proxy")); if(!hello) { cerr << argv[0] << ": invalid proxy" << endl; return EXIT_FAILURE; } menu(); CallbackPtr cb = new Callback(); char c; do {

try { cout << "==> "; cin >> c; if(c == 'i') { hello->sayHello(0); } else if(c == 'd') { hello->begin_sayHello(5000, newCallback_Hello_sayHello } else if(c == 's') { hello->shutdown(); } else { cout << "unknown command `" << c << "'" << endl; menu(); } } catch(const Ice::Exception& ex) { cerr << ex << endl; } }

3. AMD编程
3.1元数据标记 3.2 AMD语言映射(C++) 3.3 AMD方式下的异常 3.4示例

3. AMD编程
回顾AMD概述
(1.3节) AMD: 使用 AMD 时,服务器可以接收一个请求,然后挂起其处理, 以尽快释放分派线程。当处理恢复、结果已得出时,服务器要使用 Ice run time 提供的回调对象,显式地发送响应。 AMD 操作通常会把请求数据 (也就是,回调对象和 操作参数)放入队列 ,供应用的某个线程 (或线程池)随后处理用。 这样,服务器就使分派线程的使用率降到了最低限度,能够高效地支 持数千并发客户。

3. 2 AMD方式的语言映射
2. AMD的语言映射(C++) C++ 代码生成器为每个AMD 操作生成以下代码:
1.

一个抽象的回调类:实现用它来通知 Ice run time,操作已完成。
?

类名规则:AMD_class_op
例如,对于在接口I 中定义的名叫foo 的操作 ,对应的类的名字是 AMD_I_foo。

?

void ice_response(<params>);
服务器可以用 ice_response 方法报告操作已成功完成。如果操作的返回类型不是 void, ice_response 的第一个参数就是返回值。与操作的out 参数对应的参数跟 在返回值后面,其次序是声明时的次序。

?

void ice_exception(const Ice::Exception &) :服务器可以用这个版本的
ice_exception报告用户异常或本地异

常。
?

void ice_exception(const std::exception &):服务器可以用这个版本的
ice_exception报告标准的异常。

?

void ice_exception():服务器可以用这个版本的 ice_exception报告 未知异常

3.2 AMD方式的语言映射
2. AMD的语言映射(C++)
(2)分派方法:
? ?

?

其名字有后缀 _async。 这个方法的返回类型是 void 第一个参数是一个智能指针,指向上面描述的回调类的一个实例。 其他的参数由操作的各个 in参数组成,次序是声明时的次序.

3.2 AMD方式的语言映射
interface I { ["amd"] int foo(short s, out long l); }; 下面是为操作foo 生成的回调类: class AMD_I_foo : public ... { public: void ice_response(Ice::Int, Ice::Long); void ice_exception(const Ice::Exception &); void ice_exception(const std::exception &); void ice_exception(); }; 下面是为操作foo 的异步调用生成的分派方法: void foo_async(const AMD_I_fooPtr &, Ice::Short);

3. 3 AMD的异常传递
在两种处理上下文中, AMD 操作的逻辑实现可能需要报 告异常:分派线程 ( 也就是,接收调用的线程),以及响应线 程 (也就是,发送响应的线程 )。 尽管建议你用回调对象来把所有异常报告给客户,实现 抛出异常也是合法的,但只能在分派线程中抛出。 Ice run time 无法捕捉从响应线程抛出的异常;应用 的运行时环境将决定怎样处理这样的异常。因此,响应线程 必须确保抓住所有异常,并用回调对象发送适当的响应。否 则,如果响应线程被未捕捉的异常终止,请求就可能不会完 成,客户将无限期地等待响应。

3. 4 示例
Slice定义
interface Hello { ["ami", "amd"] idempotent void sayHello(int delay) throws RequestCanceledException; void shutdown(); };

3. 4 示例
服务端声明文件代码:
#include <Hello.h> #include <WorkQueue.h> class HelloI : virtual public Demo::Hello { public: HelloI(const WorkQueuePtr&); // 分派函数 virtual void sayHello_async(const Demo::AMD_Hello_sayHelloPtr&, int, const Ice::Current&); virtual void shutdown(const Ice::Current&);

private: WorkQueuePtr _workQueue; };

3. 4 示例
服务端实现文件代码:void HelloI::shutdown(const Ice::Current& curr)
HelloI::HelloI(const WorkQueuePtr& workQueue) : _workQueue(workQueue) { } void HelloI::sayHello_async( const Demo::AMD_Hello_sayHelloPtr& cb, int delay, const Ice::Current&) { if(delay == 0) { cout << "Hello World!" << endl; cb->ice_response(); } else { _workQueue->add(cb, delay); } }

{ cout << "Shutting down..." << endl; _workQueue->destroy(); curr.adapter->getCommunicator()->shutdown(); }

3. 4 示例
服务端实现文件代码:
int AsyncServer::run(int argc, char* argv[]) { if(argc > 1) { cerr << appName() << ": too many arguments" << endl; return EXIT_FAILURE; } callbackOnInterrupt(); Ice::ObjectAdapterPtr adapter = communicator()->createObjectAdapter("Hello"); _workQueue = new WorkQueue(); Demo::HelloPtr hello = new HelloI(_workQueue); //定义服务端servrant adapter->add(hello, communicator()->stringToIdentity(“hello”)); // 将serverant加入适配器 // 启动工作队列 _workQueue->start(); adapter->activate(); // communicator()->waitForShutdown(); _workQueue->getThreadControl().join();//等待工作队列停止 return EXIT_SUCCESS; }

3. 4 示例
服务端实现文件代码:
class WorkQueue : public IceUtil::Thread { public: WorkQueue(); virtual void run(); // 将回调对象加入队列 void add(const Demo::AMD_Hello_sayHelloPtr&, int); void destroy(); private: //存储回调对象以及调用参数的的结构体 struct CallbackEntry { Demo::AMD_Hello_sayHelloPtr cb; int delay; }; IceUtil::Monitor<IceUtil::Mutex> _monitor; std::list<CallbackEntry> _callbacks; bool _done; };

3. 4 示例
服务端实现文件代码:
Void WorkQueue::run() { IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_monitor); while(!_done) { if(_callbacks.size() == 0) // 续 当结束时队列中还有回调对象时,说明客户终止了调用 { list<CallbackEntry>::const_iterator p; _monitor.wait(); for(p = _callbacks.begin(); p != _callbacks.end(); ++p) } { (*p).cb->ice_exception(Demo::RequestCanceledException()); if(_callbacks.size() != 0) } { // 从队列中取下一个回调对象 CallbackEntry entry = _callbacks.front(); // 等待delay秒 _monitor.timedWait(IceUtil::Time::milliSeconds(entry.delay)); if(!_done) { _callbacks.pop_front(); //… 处理业务逻辑 Do Something here。 entry.cb->ice_response(); }

3. 4 示例
服务端实现文件代码:
void WorkQueue::add(const Demo::AMD_Hello_sayHelloPtr& cb, int delay) { IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_monitor); if(!_done) { CallbackEntry entry; void entry.cb = cb; WorkQueue::destroy() entry.delay = delay; { IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_monitor); if(_callbacks.size() == 0) { _monitor.notify(); } _callbacks.push_back(entry); } else { } // // Set done flag and notify. // _done = true; _monitor.notify();

// 发送异常通知 cb->ice_exception(Demo::RequestCanceledException()); } }

3. 5 AMD异步派发实现总结
?

?

?

Slice会生成<module::>AMD_interace_opPtr 回调对象,回调对象的代码不需要程序员 编写。 Slice在skelton代码中生成 <module::>interface::op_async异步派发方 法,服务端severant实例必须实现该方法。 Severant实现代码负责实现异步派发的业务 逻辑。

Any Question?


相关文章:
冻干工艺培训教材第八章、药品冷冻干燥工艺的放大
系列文档 朝鲜历届领导人资料 朝鲜现状 为什么南北...06-冻干工艺培训教材第四章... 10页 2财富值 04...Qm=Aρiceds/dθ 升华速度 θ=Aρiceθm∫ds ...
求是学院第三期学生干部培训策划书
系列文档 策划书基本格式(完整) 策划书写作技巧 ...标题 二、 三、 培训目的、意义 培训主题 四、 五...“ICE BREAK”破冰起航、团队 建设 MBA 发散智慧...
户外知识培训
系列登山包中的珠峰型背包就是典型的大型背包, MASK 和 ICEPICK 属于中型背包...户外媒体知识培训 60页 5下载券 户外广告知识培训 46页 2下载券喜欢...
中小学英语培训,针对特点找对策
关键词:中小学英语培训系列文档 人教版一年级上册语文期末... 二年级语文上册...Ice — Introducing a new topic of conversation 引出话题 By the way, ......
金华室内设计培训-室内设计色彩搭配禁忌_图文
但紫色中所带有红色系列,容易导致人的情绪有抑郁倾 ...(ice dance Carex)青翠挺拔,细长柔顺的金心苔草 ...室内设计色彩搭配 2页 1下载券 金华室内设计培训-软装...
高考书面表达培训课讲义
icebreaker646贡献于2012-02-12 0.0分 (0人评价...同系列文档 2012大纲全国卷高考数学(理... 2012...1/2 相关文档推荐 高考培训资料 87页 20财富值 ...
成才培训三年级英语期末试卷
系列文档 人教版pep小学英语三年级上... 牛津...1/2 相关文档推荐 歌利雅英语培训中心 三... 4...公园 (8)ice cream 冰激凌 A.鸡蛋 B.连衣裙 C....
凤磊专业辅导培训小学六年级下册英语期末测试
系列文档 PEP人教版小学六年级英语上... PEP小学...凤磊专业辅导培训小学六年级下册英语期末测试班级:一...C.Let's go ice-skating this weekend. 2. 当...
【昆明英语培训
系列课程 人教版新目标初二下英... 来自:育英...价格仅培训班 2 折,效果好 3 倍; 2、自由:不...s something in my ice./ais/ ---轻辅音前的元音...
初一进初二暑假培训英语考试试卷[1]
系列文档 八年级英语上学期知识点归... 八年级...暑期培训初一升初二数学试... 4页 2财富值 ...C. Ice cream. 42. You can have a meal in ...
更多相关标签:
ice系列培训 | ice培训 | 小吃系列培训 | java培训系列课程 | 小吃培训卤肉系列培训 | 早餐小吃系列培训 | 油炸烧烤系列培训 | 特色小吃系列培训 |