ICE 的回調
使用分佈式計算中間件ICE到現在已經有一年多了,在這一年裏裏面對ICE的理解、應用比較熟悉。
使用ICE寫分佈式軟件,確實是很方便:ICE比較穩定、可靠,調用返回速度低延遲,使用簡單,學習曲線不是很陡。
總之利用ICE開發分佈式軟件,是一個可行的選擇。
在實際的過程中,ICE客戶端跟服務端的數據流動是單項的,也就是客戶端獲取服務端的一個代理,從而與服務端進行數據交互,如果服務端要主動給客戶端數據的時候,則需要我們給服務端傳去一個客戶端的代理。這時客戶端也是一個服務器。在ICE中客戶端、服務端沒有嚴格的限制。
在ICE中有三種提供回調的方法,以下將具體介紹這三種方法:
1:適合服務器、客戶端在如下的環境。客戶和服務器程序或者運行在同一個主機上,或者運行在沒有網絡限制的多個主機上。這樣的環境下,提供回調是最簡單的。
以下是簡單的例子,首先先寫slice文件:
// **********************************************************************
//
// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved.
//
// This copy of Ice is licensed to you under the terms described in the
// ICE_LICENSE file included in this distribution.
//
// **********************************************************************
#ifndef CALLBACK_ICE
#define CALLBACK_ICE
module Demo
{
interface CallbackReceiver
{
void callback();
};
interface CallbackSender
{
void initiateCallback(CallbackReceiver* proxy);
void shutdown();
};
};
#endif
這個例子直接從ICE提供的demo中獲得的。
然後利用ICE提供的slice2cpp程序,生成骨架代碼,具體就不說了。
接着客服端實現利用如下的方式,提供回調代理:
CallbackSenderPrx senderPrx =
CallbackSenderPrx::checkedCast( communicator()->propertyToProxy("Callback.CallbackServer"))
"Callback.CallbackServer"是通過配置文件配置的,該代碼是利用繼承Ice::Application,重載它的main成員函數。communicator是ICE提供的通信器。
以下,是客戶端提供回調的形式化代碼:
Ice::ObjectAdapterPtr adapter = communicator()->createObjectAdapter("Callback.Client");
adapter->add(new CallbackReceiverI, communicator()->stringToIdentity("callbackReceiver"));
adapter->activate();
CallbackReceiverPrx twowayR = CallbackReceiverPrx::uncheckedCast(
adapter->createProxy(communicator()->stringToIdentity("callbackReceiver")));
然後調用:
senderPrx ->initiateCallback(twowayR);
這樣就把客戶端的一個回調代理傳給服務端。
不過這樣的傳回調在比較負責的網絡環境下,回調是會失敗的。在實際的開發過程中,我就遇到這樣的問題。然後去文檔中尋求,解決方法。
我遇到的問題,在文檔中Glacier有介紹,客戶端和服務端的網絡環境如圖:
在這樣的情況下,ICE回調給客戶端是失敗的,原因是ICE通過那個回調對象,不能找到客戶端的路由信息。
因此有兩種可選的方式,一種是利用Glacier,另外一種Bidirectional Connections(可能是要ICE3.2.0及其以後的版本才支持)。
我在實際的過程中,是利用Bidirectional Connections解決了上面的那個問題,是通過了實際的測試。Glacier方法,我沒實際測試過。
因此,具體介紹Bidirectional Connections方法。在ICE文檔3.2.1中提到,如何使用這個方法,大家有興趣可以去看看。
下面提供一些形式化的代碼,並說明注意事項。
還是先把slice文件(來自ICE DEMO)提供出來,以下是slice文件:
// **********************************************************************
//
// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved.
//
// This copy of Ice is licensed to you under the terms described in the
// ICE_LICENSE file included in this distribution.
//
// **********************************************************************
#ifndef CALLBACK_ICE
#define CALLBACK_ICE
#include <Ice/Identity.ice>
module Demo
{
interface CallbackReceiver
{
void callback(int num);
};
interface CallbackSender
{
void addClient(Ice::Identity ident);
};
};
#endif
客戶端的形式化代碼:
CallbackSenderPrx server =
CallbackSenderPrx::checkedCast(communicator()->propertyToProxy
("Callback.Client.CallbackServer"));
Ice::ObjectAdapterPtr adapter = communicator()->createObjectAdapter("");
Ice::Identity ident;
ident.name = IceUtil::generateUUID();
ident.category = "";
adapter->add(new CallbackReceiverI, ident);
adapter->activate();
server->ice_getConnection()->setAdapter(adapter);
server->addClient(ident);
服務端的形式化代碼:
CallbackReceiverPrx client = CallbackReceiverPrx::uncheckedCast(current.con->createProxy(ident));
使用這個方式的一個注意事項:
1:在配置文件
#
# ACM must be disabled for bidirectional connections
#
Ice.ACM.Client=0
要將Ice.ACM.Client屬性禁用掉。如果回調對象被關閉的話,只能重新
if(server->ice_getConnection()->getAdapter() == NULL)
server->ice_getConnection()->setAdapter(adapter);
server->addClient(ident);
因爲ICE有一個機制如果在一定的時間內,沒有發生數據交互,連接會被關閉
其實也可以利用一個hook線程對回調代理進行ice_ping()。
2:利用Bidirectional Connections,這樣的代理是在原來的連接基礎上建立起來的。因此不能修改回調代理的安全、超時等屬性。
以上是我在實際開發過程中,獲得的經驗,希望對大家有幫助。