大家好,欢迎来到IT知识分享网。
R003-Remote Invocation
Remote Invocation可以让客户端像调用本地方法一样调用远程服务器上的方法.这个过程叫做Remote Procedure Call (RPC).
为了实现这个功能,客户端首先就要和一个类似于远程服务器的代理对象接口通讯.
这个代理对象叫做代理(proxy). 代理可以将方法调用转换成流在网络中传递.
服务器端接收到这个数据包,欢迎他的信息,调用真正的方法. 最后将执行结合或错误信息打包发送到客户端的代理.
最后,代理发送执行结果给调用者.像调用本地方法一样.
RemObjects SDK 3.0 简介中我们看到了他的新特性和工具,这些都可以给我们开发方式带来很大的改进. 这篇文档我们看看关于产品的概况和令人兴奋的新特性.
Service Tester 用处
分布式系统提供的灵活性和适用性是传统的CS结构无法比拟的. 但是开发和测试分布式系统通常复杂费时.在基于服务的客户端程序开发中我们只是调试本程序还是不够的,还有大量的交互式请求和特定的服务过程大量触发的效率等.
一般的测试方案产生出脚本并测试最终用户应用, 但不能做到真正的模拟大量用户来代理远程服务器或使用一些RemObjects 特性.
这样你可以自己写测试应用程序或使用像DUnit的测试工具,但是还要有很多工作要作.
RemObjects服务测试(ROST) 可以解决这些问题. 为每个服务方法产生测试脚本并使用多线程并发调用这些方法.测试完成ROST提供测试图解,其他的事物,每秒钟请求数量,网络传输情况等信息. 测试脚本可以保存,你修改了远程服务方法后可以再次运行查看修改后对系统效率和功能改善.
ROST支持所有数据类型: 简单数量类型,复杂的结构体,二进制流和数组. 通过直观的界面可以输入测试函数的参数并观测最终执行结果.
下面的截图可以看到如何启动ROST来执行MegaDemo 中的EchoPerson 方法:
下面的截图是测试正在进行时的情况:
ROST经配置后使用RemObjects SDK提供的所有通道(channels)和消息(message).
下面的截图显示连接配置对话框:
ROST是一个很有价值的好帮手,使用它可以在测试评估你的应用程序服务器时节省大量时间. 这完全是RAD操作!
客户端均衡负载和容错处理
RemObjects SDK的设计目标是成为一个可升级系统.
默认的,RemObjects不指定固定连接,我们不用写代码就可以实现连接单个服务器或多个服务器.在Cisco范例中, Windows 网路均衡负载, F5 Networks hardware,和其他硬件软件都与RO-based 系统无缝的集成在一起.
RemObjects 3.0 的传输通道已经升级支持客户端的均衡负载和容错.本质上你可以为你的通道的TROServerLocator属性明确的提供一系列的服务器列表. 你可以控制通道的均衡负载请求,或在当前服务器故障时选择另外一个.通道可以寻找服务并自动发现在线的服务器,但如果要尝试搜寻所有可能的目的源时很能会报异常.
下面的截图是在TROWinINetHTTPChannel中设置属性:
下面的截图展示了独立的TROServerLocator列表:
最后,这个截图显示测试应用程序的两个服务器通过连接和断开模拟断线条件输出的结果:
COM 和Active 脚本支持
虽然RemObjects服务器可以连接任何操作系统和客户端(Windows, Linux, Pocket PCs, .Net, Mono, 和其他用ANSI-C 编译的程序), 但在Windows系统下,COM几乎成了标准的用法. 一些应用程序像 Excel, Word, Lotus Notes可以通过OLE操纵, 有的高级用户可以用VBScript脚本控制它们. 在Web开发中,ASP的Web页面经常使用相似及的技术操作COM对象(VBScript 或JavaScript).
RemObjects 3.0使用COM DLL规则,这样可以使一种语言能使用不同的RemObjects服务器. RemObjects SDK 3.0生成的COM library发布的库如下:
ROServer.CreateService方法用服务方法名作参数返回一个对象可以将COM方法调用转换为RemObjects调用(二进制流或Soap).
通过使用ChannelType 和MessageType属性 (都是字符串), 你可以确定组件的类型,像TROBINMessage 和TROWinINetHTTPChannel, 与服务器通信.
通过使用SetXXXProperty 和ReadXXXProperty方法你可以自定义属性,例如TargetURL 或自定义压缩.
下面的截图展示了Excel电子表格调用MegaDemo服务器的Sum方法:
与按钮相关的代码如下:
Private Sub CommandButton1_Click()
Dim result
Set ROServer = CreateObject("RemObjects.COMServer")
ROServer.MessageType = "TROBINMessage"
ROServer.ChannelType = "TROWinInetHTTPChannel"
ROServer.SetChannelProperty "TargetURL", http://localhost:8099/BIN
Set MegaDemoService = ROServer.CreateService("NewService")
Dim A, B
A = Sheets("Sheet1").Cells(9, 4).Value
B = Sheets("Sheet1").Cells(10, 4).Value
result = MegaDemoService.Sum(A, B)
MsgBox "Sum " & A & "+" & B & "=" & result
Set MegaDemoService = Nothing
Set ROServer = Nothing
End Sub
下面的截图展示了ASP页面向用户显示简单信息:
代码如下:
<%
Dim result
Set ROServer = Server.CreateObject("RemObjects.COMServer")
ROServer.MessageType = "TROBINMessage"
ROServer.ChannelType = "TROWinInetHTTPChannel"
ROServer.SetChannelProperty "TargetURL", "http://localhost:8099/BIN"
Set MegaDemoService = ROServer.CreateService("NewService")
Response.Write "<STRONG>This is an ASP page that invokes methods "
Response.Write "of the RemObjects MegaDemo</STRONG><BR><BR>"
result = MegaDemoService.Sum(100, 200)
Response.Write "The result of 100+200 is " & result & "<BR>"
result = MegaDemoService.GetServerTime
Response.Write "The time on the server is " & result
Set MegaDemoService = nothing
Set ROServer = nothing
%>
广播服务事件 – 服务器端
广播服务事件是RemObjects3.0新特性,运行服务器异步执行客户端的回调函数. 回调函数是在客户端定义而由服务器相应一个事件时调用.
假设一个聊天程序,当一个用户类型信息被中心服务器接受后,再转发到其他所有的用户.为了发送消息,服务器端需要调用客户端事件句柄(如OnSendMessage 方法). 只有当服务器能通过客户端的IP与其交互,或客户端在服务器回调中是启动的,才能完整这个功能.
RemObjects SDK 2.0中可以让应用程序的客户端和服务器端同时调用我们描述的回调函数,但是这只限于局域网(由于网路地址转换的问题);
下图是解释:
当客户端1发送一个消息,由于服务器知道客户端2的直接IP地址(192.168.0.101)可以将其发送到客户端2.而客户端3和4是不能到达的,因为使用了共享IP地址(24.100.3.36)与聊天程序服务器相连.事实上不同局域网要共享相同的IP地址增加了其他的复杂性. 网络中的设备像网关,防火墙给整个问题带来了更大的复杂性.
通常,写不可升级和多网络碎片的分布式系统回调可以避免. 要使你的系统支持升级并让客户端接受远程通知的唯一解决方法就是使用基于客户端轮询和服务器队列的通道.而这就是广播服务器事件提供的.
RemObjects 3服务器可以将客户端定时发送的消息存储在队列中,如下图:
当客户端接受一个消息,他们将调用特定的事件处理程序(于Delphi的事件规则相同).
这个基于RemObjects3.0的聊天程序展示了如何生成用户连接一个或多个服务器上的程序. 这个例子可以让系统管理员远程断开客户端或适时的向客户端发送系统消息.
下面的截图展示了两个客户聊天的Session,收到了一个服务器要重新启动的消息.
在RemObjects 3.0中这种功能很好实现. 服务器端我们定义了一个ChatService服务:
IChatService = interface
['{6893042C-3354-4AE6-B5FA-E7A637475C30}']
procedure Login(const UserID: String);
procedure Logout;
procedure SendMessage(const MessageText: String);
end;
下面截图展示Service Builder:
这个例子的目标是当一个方法被调用将在每个客户端抛出特定的事件. 在RemObjects 3.0中等以事件使用新类型“Callback Sink” [这个名字将在发布版本中改变].
聊天服务程序包含2个callback sinks 例子: ChatEvents (OnLogin, OnLogout, OnSendMessage)(当其他客户端有操作时客户端接收)和ChatServerEvents (OnSystemShutdown, OnMandatoryClose)(系统管理员向客户端发送消息).
下图展示了如何在Service Builder定义ChatEvents:
RemObjects 预处理程序可以自动生成代码和callback sinks必要的接口声明.这是Delphi声明的ChatEvents和ChatServerEvents:
{ IChatEvents }
IChatEvents = interface
['{75F9A466-518A-4B09-9DC4-9272B1EEFD95}']
procedure OnLogin(const aUserID: String);
procedure OnLogout(const aUserID: String);
procedure OnSendMessage(const aUserID: String;
const aMessage: String);
end;
{ IChatServerEvents }
IChatServerEvents = interface
['{E80B0A2E-96ED-4F38-A6AC-E4E0B59F27F3}']
procedure OnSystemShutdown(const ShutdownDelay:
Integer; const Reason: String);
procedure OnMandatoryClose(const ClientID: String;
const Reason: String);
end;
下面的代码是执行在CharService 中的Login方法,他触发了所有客户端OnLogin事件:
procedure TChatService.Login(const UserID: String);
begin
{ Checks if the user is already logged in }
if fUsers.IndexOf(UserID)>=0
then raise Exception.CreateFmt('User %s is already logged in',
[UserID]);
{ Adds the user to the list of logged users }
fUsers.Add(UserID);
Session.Values['UserID'] := UserID;
{ Generates the OnLogin event }
(EventRepository as IChatEvents_Writer).OnLogin(Session.SessionID,
UserID);
end;
最后一行代码(EventRepository asIChatEvents_Writer).OnLogin(Session.SessionID, UserID); 在所有的客户端触发OnLogin事件. 虽然开起来有些零乱,但是这样很简单而且与标准控件事件同样工作.这段代码与TButton.Click方法等效:
procedure TButton.Click;
begin
//...
if Assigned(OnClick) then OnClick(Self);
end;
主要的不同是事件1在服务器端触发并有多个接收者(广播),而事件2不是立即执行的,因为实际上OnLogin调用存储后有其它用户触发.
EventRepository属性指向一个拖放到主窗体的存储对象,其中存储了OnLogin,OnSendMessage等序列化方法和其参数. IChatEvent_Writer是在声明ChatEvents时生成的特殊接口,并可以在存储器中初始化IChatEvent消息.代理依赖事件存储器就像标准的RemObjects代理依赖传输端口一样.
TChatEvents_Writer.OnLogin被包含在ChatLibrary_Intf.pas文件:
procedure TChatEvents_Writer.OnLogin(const Sender : TGUID;
const aUserID: String);
var __eventdata : Binary;
begin
__eventdata := Binary.Create;
try
__Message.Initialize(NIL, 'ChatLibrary',
EID_ChatEvents, 'OnLogin');
__Message.Write('aUserID', TypeInfo(String), aUserID, []);
__Message.Finalize;
__Message.WriteToStream(__eventdata);
Repository.StoreEventData(Sender, __eventdata);
except
__eventdata.Free;
end;
end;
就像我们期待的一样,这也是自动生成的.
在服务器方法中,下面的是在客户端向其他客户端发送信息时由此客户端触发的代码:
procedure TChatService.SendMessage(const MessageText: String);
var thisuserid : string;
begin
{ Extract the name of the user invoking this method
by reading session information }
thisuserid := VarToStr(Session.Values['UserID']);
{ Generates the OnSendMessage event }
(EventRepository as IChatEvents_Writer).OnSendMessage(
Session.SessionID, thisuserid, MessageText);
end;
又一次使用了一行代码触发远程事件.
广播服务事件 – 客户端
写客户端相应服务器事件也很简单. 基本上只要实现事件接口(IChatEvents和IChatServerEvents)及通知RemObjects框架对象已经实现了这些接口.
一个新的控件TROEventReceiver 在轮询远程事件存储器,周期性的查看服务器是否有未处理的消息并在需要的时候调用对象方法.
客户端包含一个叫做EventReceiver 的TROEventReceiver 控件. 在客户端例程中,主窗体实现了IChatEvents 和IChatServerEvents 接口,声明如下:
TClientForm = class(TForm, IChatEvents, IChatServerEvents)
//...
protected
{ IChatEvents }
procedure OnSendMessage(const aSender, aMessage : string);
procedure OnLogin(const aUserID : string);
procedure OnLogout(const aUserID : string);
{ IChatServerEvents }
procedure OnSystemShutdown(const ShutdownDelay: Integer;
const Reason: String);
procedure OnMandatoryClose(const ClientID: String;
const Reason: String);
//...
end;
当用户点击Login按钮,下面的代码将执行:
procedure TClientForm.bbLoginClick(Sender: TObject);
begin
try
ROChannel.TargetURL := cbServer.Text;
{ Calls the remote login method }
fChatService.Login(UserID);
{ Register the event handlers using the
TROEventReceiver called EventReceiver }
EventReceiver.RegisterEventHandlers(
[EID_ChatEvents, EID_ChatServerEvents],
[Self, Self]);
{ Starts polling }
EventReceiver.Active := TRUE;
fLoggedIn := TRUE;
finally
ToggleControls;
end;
end;
方法TROEventReceiver.RegisterEventHandlers获取一系列的回调接口ID (parameters [EID_ChatEvents, EID_ChatServerEvents])及实现他的对象 (这个例子都是self,因为这里都是用MainForm实现这两种事件处理接口的).
EID_xxx是与回调接口同名的字符串常量,声明如下:
unit ChatLibrary_Intf;
//...
const
//...
{ Event ID's }
EID_ChatEvents = 'ChatEvents';
EID_ChatServerEvents = 'ChatServerEvents';
当IChatEvents 或IChatServerEvents事件到达,MainForm中相关的方法将自动执行,这就像OnClick事件一样,这些都是对开发者透明的.
下面的一小段程序展示了OnLog,OnSendMessage,OnSystemShutdown的实现,这些方法觉定了在运行期如何输出信息:
procedure TClientForm.OnLogin(const aUserID: string);
begin
WriteMessage(mtSystem, '', 'User '
+aUserID
+' just logged in');
end;
procedure TClientForm.OnSendMessage(const aSender,
aMessage: string);
begin
WriteMessage(mtMessage, aSender, aMessage);
end;
procedure TClientForm.OnSystemShutdown(const ShutdownDelay: Integer;
const Reason: String);
begin
Beep;
MessageDlg(Format('The server will be shut down in %d minutes.'
+#13'Reason: %s', [ShutdownDelay, Reason]),
mtWarning, [mbOK], 0);
end;
WriteMessage是一个向MainForm窗体的Memo控件显示文本信息的简单函数.紧记,除了调用EventReceiver.RegisterEventHandlers 和用户注销时的EventReceiver.UnregisterEventHandlers,实现消息处理接口外,不再需要其他的处理事件了.
Master Server
The RemObjects SDK允许我们将Session信息存储在内存中或数据库中, 如果你想要你的系统均衡负载和容错,需要将Session保存到每个服务器都可以存取的数据库中,这样客户端无论连接到哪台服务器都可以取出一致的Session信息.这个功能就是RemObjects SDK 2.0的 TRODBSessionManager提供的. 但是,数据库Session管理折中了速度和需求.
存取远程数据库比使用内存数据要慢,并且要设置存储Session信息必需的表结构.
RemObjects Master Server (ROMS)按RemObjects SDK 3.0标准提供,可集中高效的处理我们的Session和服务器事件数据.安装Snap后可以高效简单的对数据库表操作.
ROMS以NT服务或独立可执行程序的方式发布API,使用这些API我们就可以存取Session信息和服务器事件. 这些API使用RemObjects二进制流消息通过HTTP,TCP,UDP或Win消息通道传递.由新的控件TROMasterServerSessionManager 和TROMasterServerEventRepository封装.
记住,你可以使用这些API,并且有完整的ROMS源码,但是RO3.0提供的新控件使我们不需要直接去调用ROMS方法,事实上我们只需用TROMasterServerSessionManager替换TROInMemorySessionManager 并设置一组属性就可以了.
ROMS使用嵌入的 NexusDB服务器保存数据, 可以使数据吞吐率更好于重量级的数据库. 使用RemObjects消息读取ROMS可以在不同的局域网中共享Session.
下面的截图展示了高版本的聊天范例:
这个高版本的聊天程序展示了两个客户端连接到分离的两个服务器上,可以像连接到一个服务器一样交换信息. 这两个服务器可能是一个服务器丛的一部分,或在远程存放并连接到同一个ROMS.
ROMS联合客户端特性可以支持均衡负载和容错处理,这样我们的系统可以24X7不间断运行,并可以跨多局域网段,提供Session数据共享功能.
ROMS源码包含于RemObjects SDK 3.0,需要非附加许可证文件.
ServiceBuilder 3
RemObjects SDK 3.0的一个新特性就是提供了一个新的ServiceBuilder 3,它完全从写,提供了更多用户接口和自定义插件.事实上SB3中所有的功能都是插件式的,以后会增加新的功能.
这里是SB3的插件基础,这些可以用附加的插件修改和扩充:
- Library编辑器– 就像你在上面的截图中看到的默认编辑器 (使用grids和edit boxes 编辑库). 附加的插件可以提供不同的编辑界面.他们可以含基于UML的库视图,允许你在类图中编辑服务方法和结构体,或高亮的语法显示.
- Library验证器– Library 验证器可以实现检查自定义的规格.默认验证器可以检查错误元素(像声明数组没有类型等),或不合法标识(使用保留字if),自定义验证器可以扩展这些功能,例如强制命名规则或其它标准.
- Library 视图– 像Library编辑器一样, Library 试图可以自定义RODL Library视图符合你的特殊要求.可能是服务方法和参数类型相互依赖的图表视图,WSDL视图,或其它的视图.
- CodeGens – 可能是最重要的可扩展特性, 插件式的CodeGens允许SB3为不同的语言扩展自定义的代码生成器. 这样将使RemObjects SDK在将来扩展支持其他的平台 .SB3可支持C#,VB.NET,C/C++,Java或其它语言. 你也可以为现存的任何语言加入自己修改的代码生成器,或提供修改的接口适合特殊要求.
- Identifier Validators – 标识符验证插件提供在特定语言中验证标识符是否合法的功能. 例如,this这个方法名字可能在Delphi中是可以接受的,但是在C#和C/C++中与预留字符串向违背.
- Importers –自定义导入接口可以让你从自定义数据中引入库,默认的,库元素可以从WSDL,Pascal,COM类型库或RODL文件中引入.通过SB3插件功能可以增加自己的导入接口.
SB3 插件式的SDK,有很多建立自定义插件的信息和范例, 将对所有的RemObjects SDK 3用户免费.
插件将在Delphi7和Hydra下构建,提供新的修订的TRODLLibrary 结构可轻易工作和扩展加载库.
下面的截图展示了一系列插件在SB3中加载:
SB3增加功能:
- 按可视节点分组的库元素有利于更好的组织大型库
- 增强的文档功能可以查看所有的库文档,包括个别函数和结构体成员.
- 支持新回调接收器Callback Sink类型和复杂的异常类型.
下面的截图展示了运行中的SB3, 在CodeGen菜单中支持不同的语言和两个三方插件编辑库,
结论
RemObjects SDK 3.0建立在2.0版本,增加了很多新的强大功能:
- 服务器端测试和图形功能
- 改进的Service Builder可以通过Hydra插件扩展
- 广播服务器事件模拟无状态,可预定的回调
- 支持COM 和Active 脚本
- 强大的Master Server保证高级可预定性
就像我们说的:远程,灵活的方式.
源址:http://blog.csdn.net/henreash/article/details/2260950
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/12438.html