1,需求描述
某项目收集上千个设备的数据,前端程序采集数据后写入数据库。
当某些特定数据满足触发条件时,需要后端程序即时发起业务处理流程。
2,技术方案
2.1 定时扫描数据库
显然,可以采用后端程序定时扫描数据库的办法。此法简单易行,但缺点也很明显,不能满足业务处理的即时性要求。
如果扫描周期过长,则响应延时差;如果扫描周期过短,则数据库压力加大,可能波及整个系统。
2.2 数据库驱动业务
方案2.1是由后台程序主动发起,因此无法发起时机是随机的,相当于在瞎猜。
本方案则采用由数据库发起的思路,这也是很容易想到的。因为数据库对于数据是否变化,以及变化是否满足业务发起条件能在第一时间内加以判别。
采用表触发器可以在数据提交后判断是否需要后台程序处理。如果需要,就向后台程序发送消息,后台程序收到消息后,立即开始执行业务流程。这样就实现了由数据驱动的业务流程。
该方案避免了数据扫描,对数据库压力最小,业务处理响应也是即时性的。
3,SQLServer+Delphi实现
以下以SQLServer2005和Delphi7为例,具体实现由数据驱动的业务流程。主要步骤包括:
数据库表建立触发器,在触发器中发送消息
后台程序中接收消息,执行处理。
3.1 数据库发送消息函数
首先,要在SQLServer2005数据库中建立发送消息的函数SendHttpMsg。
create PROCEDURE [dbo].[SendHttpMsg]
@Params varchar(30) = ''
AS
DECLARE @obj INT
DECLARE @sUrl varchar(200)
DECLARE @response INT
SET @sUrl = 'http://www.xxx.net:30008/QA?' +@Params
EXEC sp_OACreate 'MSXML2.ServerXMLHTTP', @obj OUT
EXEC sp_OAMethod @obj,'open', NULL, 'GET', @sUrl, false
EXEC sp_OAMethod @obj,'send', null
RETURN
3.2 数据库表触发器
在要监控的表上创建触发器,调用SendHttpMsg函数,发送101消息
CREATE TRIGGER [dbo].[tr_em_test_sendCmd]
ON [dbo].[EM_Test]
AFTER UPDATE
AS
BEGIN
--状态变化,更新显示
if @parkingStatus2 <> @parkingStatus0 begin
EXEC SendHttpMsg @Params='cmd=101'
end
END
3.3 后台程序监听处理
Delphi7中可以使用TIdHTTPServer组件来监听Http消息。 绑定30008端口。在其OnCommandGet事件处理过程中执行代码:
procedure TfHttp.IdHTTPServer30008CommandGet(AThread: TIdPeerThread;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
cmd: string;
begin
try
cmd := ARequestInfo.Params.Values['cmd'];
if cmd='101' then begin
self.Timer_ProcHttpMsg.Enabled := True;
end;
end;
然后,在Timer_ProcHttpMsg的定时器事件中执行相应的业务逻辑即可。
3.4 注意事项
注意不能在OnCommandGet事件处理过程中直接调用业务逻辑函数,否则将导致数据库访问超时错误。
原因如下:
- 数据表记录状态变化à
- 数据表的行update触发器启动à
- DB发送Http消息à
- EXE的Http服务端口接收到消息à
- EXE查询数据表统计信息à
- EXE更新业务信息à
- EXE的Http服务端口处理完毕à
- 数据表的行update触发器完成à
- 记录提交à
以上处理流程中第5步实际上无法完成,因为统计查询必须等待第9步完成才能实施。
此处关键是要将第9步记录提交放到第5步之前。
而第9步记录提交依赖于第7步的Http调用返回。
因此问题明确了,以上流程中第5、6步不应该在EXE的Http服务端口处理事件中执行,而是应该另开线程,以便让Http服务端口处理事件迅速返回,从而使数据表记录变更得以快速提交。
实际解决方案是,在EXE的Http服务端口处理事件中启动一个定时器事件,使统计和显示操作延后执行即可。