文章
· 十二月 25, 2025 阅读大约需 4 分钟

如何按时间间隔或计划运行进程?

当我开始使用 InterSystems IRIS,尤其是在互操作性方面时,我最初遇到的一个常见问题是:如何在间隔时间或计划内运行某项功能?在本专题中,我想分享两个简单的类来解决这个问题。我很惊讶在 EnsLib 的某个地方没有找到类似的类。也许是我搜索得不好?总之,这个主题并不意味着是复杂的工作,只是为初学者提供了几个片段。

假设我们有一项任务是"从 API 获取数据并将其放入外部数据库"。要完成这项任务,我们需要

  1. Ens.BusinessProcess,其中包含我们数据流的算法:如何准备获取数据的请求,如何将 API 响应转换为数据库请求,如何通过数据流生命周期处理错误和其他事件
  2. EnsLib.REST.Operation 用于使用 EnsLib.HTTP.OutboundAdapter 向 API 提出 HTTP 请求
  3. Ens.BusinessOperationEnsLib.SQL.OutboundAdapter,用于通过 JDBC 连接将数据输入外部数据库

关于这些业务主机的实现细节不在本文讨论范围之内,我们假设已经有了一个进程和两个操作。但如何运行这一切呢?进程只能通过入站请求运行......我们需要一个启动器(Initiator)!我们需要一个启动器!这个启动器将通过间隔运行,并向我们的进程发送一个虚假请求。

下面就是这样一个启动器类。我添加了一些额外的功能:使用同步或异步调用,如果有许多主机作为目标,可以在出错时停止或不停止进程。但这里主要是一个目标列表。我们将向列表中的每个项目(业务主机)发送请求。请注意 OnGetConnections 事件——它是在生产用户界面中正确建立链接所必需的。

/// Call targets by interval
Class Util.Service.IntervalCall Extends Ens.BusinessService
{

/// List of targets to call
Property TargetConfigNames As Ens.DataType.ConfigName;
/// If true, calls are made asynchronously (SendRequestAsync)
Property AsyncCall As %Boolean;
/// If true, and the target list contains more than one target, the process will stop after the first error
Property BreakOnError As %Boolean [ InitialExpression = 1 ];
Property Adapter As Ens.InboundAdapter;
Parameter ADAPTER = "Ens.InboundAdapter";
Parameter SETTINGS = "TargetConfigNames:Basic:selector?multiSelect=1&context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},AsyncCall,BreakOnError";
Method OnProcessInput(pInput As %RegisteredObject, Output pOutput As %RegisteredObject, ByRef pHint As %String) As %Status
{
    Set tSC = $$$OK
    Set targets = $LISTFROMSTRING(..TargetConfigNames)

    Quit:$LISTLENGTH(targets)=0 $$$ERROR($$$GeneralError, "TargetConfigNames are not defined")

    For i=1:1:$LISTLENGTH(targets) {
        Set target = $LISTGET(targets, i)
        Set pRequest = ##class(Ens.Request).%New()

        If ..AsyncCall {
            Set tSC = ..SendRequestAsync(target, pRequest)
        } Else  {
            Set tSC = ..SendRequestSync(target, pRequest, .pResponse)
        }
        Quit:($$$ISERR(tSC)&&..BreakOnError)
    }

    Quit tSC
}

ClassMethod OnGetConnections(Output pArray As %String, pItem As Ens.Config.Item)
{
    If pItem.GetModifiedSetting("TargetConfigNames", .tValue) {
        Set targets = $LISTFROMSTRING(tValue)
        For i=1:1:$LISTLENGTH(targets) Set pArray($LISTGET(targets, i)) = ""
    }
}

}

之后,您只需将该类添加到生产中,并在 TargetConfigNames 设置中标记我们的业务流程。

但如果需求发生了变化怎么办?现在我们需要每周一上午 08:00 运行数据抓取器。最好的办法是使用任务管理器(Task Manager)。为此,我们需要创建一个自定义任务,以编程方式运行启动器。下面是该任务的简单代码:

/// Launch selected business service on schedule
Class Util.Task.ScheduleCall Extends %SYS.Task.Definition
{

Parameter TaskName = "Launch On Schedule";
/// Business Service to launch
Property ServiceName As Ens.DataType.ConfigName;
Method OnTask() As %Status
{
    #dim tService As Ens.BusinessService
    Set tSC = ##class(Ens.Director).CreateBusinessService(..ServiceName, .tService)
    Quit:$$$ISERR(tSC) tSC
    
    Set pRequest = ##class(Ens.Request).%New()
    Quit tService.ProcessInput(pRequest, .pResponse)
}

}

这里有两件重要的事情:

  • 您必须将 Initiator 业务服务的 Pool Size 设置为 0,以防止通过调用间隔运行它(选项 Call Interval,您可以清除或保持原样——当 Pool Size 为 0 时,它不会被使用)。

  • 您需要在 Task Manager 中创建一个任务,选择"Launch On Schedule" 作为任务类型(不要忘记选中命名空间),将我们的启动程序业务服务名称设置为 ServiceName 参数,并设置所需的日程。请参阅:System Operation > Task Manager > New Task

奖励

我经常遇到这样的情况:我们需要在生产中按需运行某些功能。当然,我们可以为此在 CSP 上创建一些自定义用户界面,但重新发明轮子不是我们的办法。我认为最好使用管理门户(Management Portal)的典型用户界面。因此,我们之前创建的任务也可以手动运行。只需将任务运行类型更改为 On Demand。按需任务列表可在 System > Task Manager > On-demand Tasks,请参见 Run 按钮。此外, Run 按钮(手动运行)可用于任何类型的任务。

就是这样。现在,我们的业务主机有了一个漂亮的互操作性架构。还有 3 种运行数据采集器的方式:按时间间隔、按时间表或手动。

讨论 (0)1
登录或注册以继续