我有两个应用程序:X和Y.
X是主应用程序,它处理大量XML文件。它有超过10年的历史,已有六种技术用于存储,处理和处理这些XML文件。
Y是我正在开发的一个调试工具,它可以处理和显示XML文件。更易读的形式。基本上,它只有一组样式表,可以检测XML格式,如果它识别格式,它会将XML转换为HTML,显示在TWebBrowser组件中。
问题:
当Y处于活动状态时,我希望X将其执行的任何XML发送到Y以进行显示。但只有当Y在运行时!如果Y没有运行,X就不会做任何事情。
Y的检测需要在任何时刻完成,需要快速。我考虑过使用TCP / IP通信,但是由于缺少Y而造成的延迟太长了。特别是因为有时会处理很多XML。与命名管道和类似的基于网络的解决方案相同的问题。我需要快速确定Y是否正在运行且可用,如果是,请快速发送XML然后继续X.
我还考虑使Y成为基于COM的应用程序或者可能添加基于COM的DLL允许进程间通信的事件。 DLL解决方案会很有趣,因为它会向X公开一个方法来上传XML文件,然后向Y发送一个事件来处理XML。 这似乎是最好的选择虽然我还需要检查DLL是否已注册。如果没有,则X甚至无法调用它!
应用程序X也将被不会收到Y或其他DLL的客户使用,因此在大多数情况下,DLL将不会被注册。 (正如我所说,这意味着在调试期间提供帮助......)
但也许还有其他选择吗? TCP / IP太慢,COM有点过于复杂。
X和Y将在同一系统上运行。或者只是X将在系统上,Y完全丢失。
关于使用内存映射文件......虽然实用,但我需要记住,大多数情况下,Y不会运行,因此MMF会浪费内存。 X中的XML数据最大可达4 MB,因此在内存中具有此大小的多个块有点矫枉过正。它可用于在X和Y之间发送状态消息,但内存有时是应用程序X的一个问题。虽然MMF可以连接到物理文件,但我试图完全避免写入任何临时文件。这是一个很好的解决方案,但我担心还不够好。
我认为还有一些额外的解释。应用程序X是一个应用程序,将使用几个小时,用户执行大量操作,转换为大量处理的XML数据。 Application X是一个桌面应用程序,它与多个Web应用程序(REST),Web服务(SOAP)和其他应用程序进行通信,其中大部分是通过XML进行的。
应用程序Y只是为了窥视X正在运行的进程。基本上,X工作20分钟,Y弹出。从那一刻开始,X应该开始向Y发送XML,直到Y再次消失或者X终止。在大多数情况下,Y只会运行以捕获正在运行的任务的一小部分,甚至可能多次启动。但我可能会想到整个方向都是错误的。也许X应该是Y注册的服务器......当Y找不到X时,这不是一个真正的问题。但是X找不到Y不会导致延迟或其他问题...
答案 0 :(得分:11)
请查看我的IPC:
http://www.cromis.net/blog/downloads/cromis-ipc/
它快速,免费且具有可设置的超时,因此您可以将其设置为非常小的量(例如50ms)。因为它非常快(典型的消息周期请求 - >进程 - >响应时间不到1毫秒,大约0.1毫秒),您可以获得非常小的超时。它有客户端服务器构建,因此很多客户端没有问题。它运行线程与后面的任务池,因此它不会冻结您的程序,它具有非常灵活的数据包,以方便写入/读取数据。
如前所述,如果调试器正在运行,您甚至可以使用其他方式进行检查。
答案 1 :(得分:6)
你可以让X将其输出写入memory-mapped file - 如果数据正在运行,Y可以检索数据。这样X不关心Y是否正常。
X可以在已知位置写入某种控制信息(例如,存储从映射文件中的偏移0开始的最后1000个写入的XML的偏移量),并使用文件的其余部分作为原始数据的循环缓冲区
如果您需要Y作为X中操作的决定因素,请让Y创建映射文件,然后使用其存在/不存在来检查“通道”X侧的数据生成。有创建者和第二个用户here的示例代码。
答案 2 :(得分:5)
你可以更简单地做到这一点,因为你只是试图找出一个应用程序是从另一个应用程序运行。只要它们由同一个用户在同一台机器上运行,您就可以让X只使用FindWindow
()来查看Y当前是否正在运行。只要确保给Y一个有意义的名字(在下面的例子中,它是TXMLFormatterForm):
var
XMLWindow: HWnd;
begin
XMLWindow := FindWindow('TXMLFormatterForm', nil);
if XMLWindow > 0 then
// Y is running
end;
你也可以使用Y的窗口标题(标题),只要你确定它是独特的:
XMLWindow := FindWindow(nil, 'Workshop Alex's XML Formatter');
答案 3 :(得分:4)
命名管道很快,因为它们基于内存映射文件,用于实现。可能很慢的是服务器故障时的超时......
如果您需要在同一台计算机上快速响应,为什么不使用旧的GDI消息?
您可以在普通控制台或后台服务应用程序中使用这些消息,即使没有用户界面或可视化表单(如果是服务应用程序,安全设置必须指定此服务必须与桌面交互,即必须能够接收和发送消息)。
诀窍是处理WM_COPYDATA消息。 例如,参见我们的TSQLRestClientURIMessage类,以及TSQLRestServer的ExportServerMessage / AnswerToMessage方法,在http://synopse.info/fossil/finfo?name=SQLite3/SQLite3Commons.pas中实现
实际上,我发现GDI消息比命名管道要快得多,因为少量数据(每条消息高达64 KB或更高)。
您可以在Looking for an alternative to windows messages used in inter-process communication
中找到替代方案答案 4 :(得分:2)
根据不同的客户/服务器调查,以下是一些关于速度的真实数据。
所有基准测试都在一台计算机上本地运行。您可以在直接访问时每秒执行超过15000次查询,在HTTP / 1.1远程访问上每秒可执行4300次查询。这是使用Centrino2 CPU和AntiVirus ON的笔记本电脑的基准测试。
2.5. Client server access:
- Http client keep alive: 3001 assertions passed
first in 7.87ms, done in 153.37ms i.e. 6520/s, average 153us
- Http client multi connect: 3001 assertions passed
first in 151us, done in 305.98ms i.e. 3268/s, average 305us
- Named pipe access: 3003 assertions passed
first in 78.67ms, done in 187.15ms i.e. 5343/s, average 187us
- Local window messages: 3002 assertions passed
first in 148us, done in 112.90ms i.e. 8857/s, average 112us
- Direct in process access: 3001 assertions passed
first in 44us, done in 41.69ms i.e. 23981/s, average 41us
Total failed: 0 / 15014 - Client server access PASSED
此基准测试测试客户端和服务器速度,并且不是多线程的(即使我们的框架是多线程安全的)。
因此,您可以猜测,对于每个请求,JSON内容的4 KB数据块:
保持活动连接是HTTP / 1.1连接,多连接是普通HTTP / 1.0,每个请求都有一个新连接。多连接并不是那么糟糕,因为从服务器的角度来看,我们使用了基于I / O完成端口的高效线程池。
答案 5 :(得分:1)
使用内存映射文件。它们非常适合您的特定任务。如果您没有时间或打算实施自定义方案,我们MsgConnect的MMF传输将会解决。
答案 6 :(得分:1)
我会去内存映射文件的方向,但我不会直接在代码路径中实现..我会通过一个中间对象来执行写入内存映射文件,这样它可以替换为如果数据不存在,那就放弃了数据。
当程序首次启动时(或被告知通过配置更改进行检查),系统将创建存根“不执行任何操作”或“通过内存映射文件记录”对象。这也使您能够在需要时添加更高版本的调试器...例如用于网络日志记录的UDP记录器等。
您可以使用“FindWindow”调用来查看调试器是否正在运行。
答案 7 :(得分:0)
如果您愿意使用临时文件作为传输,那么它将很容易
两个程序在启动时都会注册相同的自定义消息 MsgAppNotifyID:= RegisterWindowMessage(......);
程序Y必须具有处理自定义消息的代码 使用SetWindowLong或任何等效的
程序X,使用FindWindow查看程序Y是否正在运行
程序X,如果Y正在运行,请使用一致的文件名(例如XXYY1234(XXYYn)格式)在两端的已知位置/目录上创建临时文件,其中1234 = n
程序X,使用BroadcastSystemMessage向程序Y发送信号 NotifyRecipients:= BSM_APPLICATIONS; BroadcastSystemMessage(BSF_IGNORECURRENTTASK 或BSF_POSTMESSAGE或BSF_FORCEIFHUNG,@ NotifyRecipients,MsgAppNotifyID,任何自定义值,n);
编程y,使用WParam作为n处理上述消息,并重新构建进程文件
干杯 祝你好运
答案 8 :(得分:0)
运行一个只在连接上(作为触发器)的TCP服务器非常快,应该做你想做的事情