Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Delphi进行透明远程处理的最简单解决方案是什么?_Delphi_Design Patterns_Remoting_Rpc_Rtti - Fatal编程技术网

使用Delphi进行透明远程处理的最简单解决方案是什么?

使用Delphi进行透明远程处理的最简单解决方案是什么?,delphi,design-patterns,remoting,rpc,rtti,Delphi,Design Patterns,Remoting,Rpc,Rtti,我有一个两层的Delphi for Win32应用程序,在一个god对象中实现了许多业务逻辑,我想将其外包到一个单独的服务中。多个客户端应通过TCP/IP telnet样式的协议访问此单独的服务 我如何使转换变得最简单 确切地说,我希望保持这种简单性:我只想定义每个函数一次。例如,如果我想在我的应用程序中添加pin码登录功能,我只需要定义 function Login(Username: string; PinCode: integer): boolean; 在服务器上的某个对象中运行,然后

我有一个两层的Delphi for Win32应用程序,在一个god对象中实现了许多业务逻辑,我想将其外包到一个单独的服务中。多个客户端应通过TCP/IP telnet样式的协议访问此单独的服务

我如何使转换变得最简单

确切地说,我希望保持这种简单性:我只想定义每个函数一次。例如,如果我想在我的应用程序中添加pin码登录功能,我只需要定义

 function Login(Username: string; PinCode: integer): boolean;
在服务器上的某个对象中运行,然后我可以从客户端使用它,而无需任何额外的工作

在最坏的情况下,我必须实现三个功能,而不是一个。首先是服务器上的函数体本身,其次是从网络接收文本行的解组器,将其解压缩并检查其有效性:

 procedure HandleCommand(Cmd: string; Params: array of string);
 begin
   ...
   if SameText(Cmd, 'Login') then begin
     CheckParamCount(Params, 2);
     ServerObject.Login(
       Params[0],
       StrToInt(Params[1])
     );
   end;
 end;
第三,封送拆收器,当客户端调用它时,会打包参数并将其发送到服务器:

function TServerConnection.Login(Username: string; PinCode: integer): boolean;
begin
  Result := StrToBool(ServerCall('Login '+Escape(Username)+' '+IntToStr(PinCode)));
end;
显然,我不想要这个

到目前为止,我已经设法摆脱了破译者。使用Delphi RTTI,我编写了一个通用的解组器,它按名称查找已发布的方法,检查参数并调用它

现在,我可以向服务器对象添加一个已发布的方法,并可以从telnet调用它:

 function Login(Username: string; PinCode: integer): boolean;

 > login john_locke
 Missing parameter 2 (PinCode: integer)!
但是写封送员我该怎么办?我无法动态获取服务器函数列表并向客户端对象添加函数。我可能会保留某种动态伪函数集合,但这会使我的客户端调用变得丑陋:

ServerConnection.Call('Login', [Username, Password]);
另外,这会破坏类型安全性,因为每个参数都作为变量传递。如果可能,我希望保持编译时类型的安全性

也许客户端代码自动生成?我可能可以在我的服务器中编写“GetFunctionList()”和“GetFunctionPrototype(名称:string)”:

> GetFunctionList
Login
Logout
IsLoggedIn

> GetFunctionPrototype Login
function Login(Username: string; PinCode: integer): boolean;
因此,每当我需要更新客户机时,我只需从服务器上重新查询所有函数原型,并自动为它们生成marshaller代码。但这会将编译与执行混为一谈:我必须首先编译服务器,然后启动它并查询它的函数,然后构建一个客户机封送器,然后再重新编译客户机。复杂

另一个选项是编写一个通用marshaller函数,然后从所有客户机原型函数调用它:

procedure TServerConnection.GenericMarshaller(); assembler;
asm
  //finds the RTTI for the caller function, unwinds stack, pops out caller params,
  //packs them according to RTTI and sends to the server.
  //receives the result, pushes it to stack according to RTTI, quits
  //oh god
end;

function TServerConnection.Login(Username: string; PinCode: integer): boolean; assembler;
asm
  call GenericMarshaller
end;
这样可以节省我每次编写手动打包的时间(减少出错的机会),但仍然需要我手动将服务器功能原型复制到客户机对象中。另外,编写这个通用的marshaller可能会是一个活生生的地狱

还有使用RPC的选项,但我不喜欢它,因为我需要重新定义IDL中的所有函数。而Delphi的IDL编辑器很差劲。对于OLE接口,Delphi强制生成“safecall”函数,这也很糟糕。除了检查对RPC类的每个函数调用之外,没有其他方法实现自动检测断开连接和自动恢复连接功能:

function TServerWrapper.Login(Username: string; PinCode: integer): boolean;
begin
  try
    RealObject.Login(Username, Pincode);
  except
    on E: EOleException do
      if IndicatesDisconnect(E) then
        Disconnect;
      Reconnect;
      RealObject.Login(Username, Pincode);
  end;
end;
我们回到了几个函数而不是一个


你们有什么建议?我还缺少什么选择?Delphi中是否有常见的远程处理模式或现成的解决方案?

您可以编写一个程序,使用RTTI为您构建必要的单元,而不是手工编写所有代码。我曾经用一个包含数千个表的ORM系统来实现这一点……编写一个代码生成器,从数据库模式中输出与系统一起工作所需的类和单元,速度更快,也更容易


这种方法的另一个优点是更容易测试,因为它可以用可预测的行为进行缩放。

您应该看看RemObjects SDK之类的框架。您可以使用他们的工具定义一个接口,它可以为您完成所有工作。然后,您只需实现这些函数,并在客户机中调用它们。我将业务逻辑组件作为接口,将单个应用程序转换为客户机/服务器应用程序。无需使用您自己的有线协议。

您至少应该在Delphi 2010中使用DataSnap,最好在Delphi XE中使用。您描述的基本上就是DataSnap。它是一个非常强大的RPC系统,允许您创建可以向客户机提供任何Delphi类型或类的服务器。一旦服务器就位,客户端就可以创建代理代码来调用服务器,就像它是应用程序的本机部分一样。没有IDL,没有COM,只有干净的Delphi代码。服务器甚至可以生成REST/JSON组合,以便与非Delphi clinets一起使用


DataSnap正是您想要的——干净、整洁、功能强大、简单。

就个人而言,我认为您不应该基于透明远程处理构建分布式系统。过程级RPC对于健壮和高性能的客户机-服务器交互来说,只是粒度级别错误;让它变得可容忍将迫使您编写定义不清的过程,这些过程会处理大量的事情,并获取大量的参数,只是为了避免聊天(=>大量小调用)API的网络往返对性能和可靠性的影响

从信息的角度考虑更多。考虑在客户机上建立一个消息队列以传递给服务器,可能有与每个消息相关联的回调来处理返回值,如果有的话(匿名方法对回调非常有用!)。然后,一次性将所有消息发送到服务器,并接收和解析结果,根据需要为处理的每个消息调用回调等

如果您的服务器在处理复合请求(来自客户机的消息队列)时能够以某种方式将所有状态更改封装到事务中,那么它的效果会更好:您可以获得处理第n条消息上发生的错误的好方法—只需放弃服务器上完成的所有工作,然后继续,就好像从未收到客户请求一样。这通常会简化客户的unde