Condividi tramite


AX2009批处理究竟是怎么工作的

这篇文章中我将解释AX2009中批处理是怎么工作的。我不是指如何设置批处理组或者任何您可以在手册上找到的东西,而是指每台AOS在决定如何以及何时接受批处理请求并完成它们时在后台是如何运作的。了解这些背景知识对较难的批处理故障排除以及开发场景有帮助。

AX2009中的批处理有所改变,现在AOS可以直接运行批处理进程。如果您想了解批处理过程中发生了什么将会变得更加困难,因为它并不像AX3或者AX4中那样有客户端来运行和查看。

现在的情况是每个AOS都有一个指定的线程来监测批处理。基本上它所做的就是每60秒(间隔无法配置)调用Classes\BatchRun.ServerGetTask(),如果发现有任何需要AOS做的工作,AOS就会接受任务。

接下来我将举个从头到尾的批处理例子来介绍一下:

- 用户发送一张报表到批处理,报表进入批处理队列BATCHJOB (批处理头) and BATCH (批处理任务).

- 每60秒,配置了批处理的AOS(系统管理->设置->服务器配置)会调用X++方法- Classes\BatchRun.serverGetTask()

- X++可以看到serverGetTask()中的逻辑,因此我们可以了解发生了什么,这是决定接受哪些批处理请求的主要代码块。基本上它检查在BATCH表中是否有任何任务等待当前AOS处理,该判断基于当前AOS配置的批处理组以及记录在BATCH表中应该被处理的时间(例如:尽管AOS每60秒轮询一次,但应该每天21:00处理的任务只会在21:00以后被接受)。该方法有几个步骤:

1.首先我们检查是否有待接受的任务(任务是BATCH表中的一条记录),查询如下:

select firstonly pessimisticlock RecId, CreatedBy, ExecutedBy, StartDateTime, Status,
        SessionIdx,SessionLoginDateTime, Company, ServerId, Info
    from batch
    where batch.Status == BatchStatus::Ready
    && batch.RunType == BatchRunType::Server
    && (Session::isServer() || batch.CreatedBy == user)
    join Language from userInfo
        where userInfo.Id == batch.CreatedBy
        && userInfo.Enable == true
    exists join batchServerGroup
        where batchServerGroup.ServerId == serverId
            && batch.GroupId == batchServerGroup.GroupId;

2.如果步骤1中返回任务,我们只需要开始处理任务。如果没有任务返回,我们就查看是否有任何批处理作业需要启动,查询如下:

 update_recordset batchJob setting
                Status = BatchStatus::Executing,
                StartDateTime = thisDate
            where batchJob.Status == BatchStatus::Waiting
                &&  batchJob.OrigStartDateTime   <= thisDate
            exists join batch
                where batch.BatchJobId == batchJob.RecId
            exists join batchServerGroup
                where batch.GroupId == batchServerGroup.GroupId
                && batchServerGroup.ServerId == serverId;

3.步骤2后我们会运行\batchRun.serverProcessDependencies()。这里发生了些有趣的事 - 我们会关注"BatchGlobal"表,因为有可能在一个环境中会有几台AOS运行批处理,对于有些操作我们会根据这张表判断是否已经有其它AOS做了些什么,从而决定当前AOS是否需要做些什么。为了防止关联,我们只需要确认其它AOS当前没有处理相同的任务。因此如果我们继续,如下查询就是我们用来设置更多任务(再次提醒任务只是BATCH表中的记录)为待处理的 - 在查询中您可以看见我们如何更新BATCH表记录的状态,我们只会更新待处理并且没有任何限制未完成的记录。

    //There are no more available tasks and the user is asking for any task. Search for more tasks with
    //dependencies
    update_recordset batch setting Status = BatchStatus::Ready
    where batch.Status == BatchStatus::Waiting
        && batch.ConstraintType == BatchConstraintType::Or
    exists join batchJob
        where batchJob.Status == BatchStatus::Executing
            && batch.BatchJobId == batchJob.RecId
    exists join constraintsOr
        where constraintsOr.BatchId == batch.RecId
    exists join batchDependsOr
      where
     (
        ((batchDependsOr.Status == BatchStatus::Finished
            && (constraintsOr.ExpectedStatus == BatchDependencyStatus::Finished
                || constraintsOr.ExpectedStatus == BatchDependencyStatus::FinishedOrError))
        || (batchDependsOr.Status == BatchStatus::Error
            && (constraintsOr.ExpectedStatus == BatchDependencyStatus::Error
                || constraintsOr.ExpectedStatus == BatchDependencyStatus::FinishedOrError)))
        && constraintsOr.DependsOnBatchId == batchDependsOr.RecId
     );

    update_recordset batch setting Status = BatchStatus::Ready
    where batch.Status == BatchStatus::Waiting
        && batch.ConstraintType == BatchConstraintType::And
    exists join batchJob
        where batchJob.Status == BatchStatus::Executing
            && batch.BatchJobId == batchJob.RecId
    notexists join constraintsAnd exists join batchDependsAnd
    where
     (
        constraintsAnd.DependsOnBatchId == batchDependsAnd.RecId
        && constraintsAnd.BatchId == batch.RecId
        && ((batchDependsAnd.Status != BatchStatus::Finished && batchDependsAnd.Status != BatchStatus::Error)
            || (constraintsAnd.ExpectedStatus == BatchDependencyStatus::Finished
                && batchDependsAnd.Status == BatchStatus::Error)
                || (constraintsAnd.ExpectedStatus == BatchDependencyStatus::Error
                && batchDependsAnd.Status == BatchStatus::Finished))
     );

 

4.当步骤3中的serverProcessDependencies()完成后,我们再次调用serverGetOneTask() (和步骤1一样),如果步骤3中有更多任务设为"待处理",我们有可能会再接受一个任务并开始工作。当然如果步骤3没有任务为"待处理",我们就不会做任何事。

-如果按照以上步骤1-4,我们发送到批处理的报表应该是待处理,因此我们接受任务。接下来在AOS内核中会启动一个工作会话,它可以被想象为是没有客户端的一个客户会话,拥有自己的会话ID,并且该ID是Batch表中一条记录的内容。从这里开始它会调用BatchRun.runJobStatic()并真正运行批处理进程 - 正常的X++代码运行进程。当runJobStatic()完成后我们调用BatchRun.ServerFinishTask(),该方法仅仅是将BATCH表中的记录状态设置成"完成"或者"错误"(如果因为某种原因而失败)。

- 现在我们的批处理任务完成了 - 记录已经在BATCH表里。但是批处理的头,即BatchJob表记录尚未完成。对于这个部分,有另一个后台进程调用BatchRun.serverProcessFinishedJobs(),它在每台AOS上每60秒运行一次。我们可以在X++方法中看到它做了什么 - 我们再次使用BatchGlobal表来确认我们最多每60秒在所有AOS 上仅仅检测完成的作业。如果到了60秒,我们会运行一整套查询(太多了不方便复制到这里,但您可以在那里查看)来创建批处理历史记录(不同的表),将BatchJob记录设置成完成,并且删除完成的任务和限制。

AOS内核中还有一些其它的后台事件用于批处理:

  • 1. 每5分钟系统会调用BatchRun.serverCleanUpTasks() - 我们还是会用到BatchGlobal表,这样我们只会每5分钟在所有AOS上运行。该方法只会在工作会话(我在之前提到过 - 我们在开始处理任务时创建工作会话)ID不再有效时将任务设回"待处理"状态 - 基本上如果任务由于X++异常或者类似事件而失败,工作会话将会终止。如果此时您配置批处理任务,允许它多次尝试,那该方法就会重置任务使其重试。
  • 2. 每5分钟每个AOS会检查服务器设置来确认它是否应该处理相同的批处理组,或者它是否不再是一台批处理服务器之类的设置

 

原文地址:

https://blogs.msdn.com/b/emeadaxsupport/archive/2011/02/22/how-batch-processing-works-under-the-hood-ax2009.aspx