文章
· 十月 6, 2022 阅读大约需 10 分钟

集成产品的业务行为监控

最近一些用户问到监控集成平台业务行为查询的问题,例如如何查询服务的平均耗时、发生错误的服务数量...

业务行为监控对于集成平台来说非常重要,可以帮助我们:

  • 监控系统健康情况 — 查看系统性能表现。例如发现队列积压和长耗时的消息处理,都可能是性能问题的表现。
  • 排查异常 — 通过查看业务行为数据,帮助我们判断特定的业务组件配置是否是造成性能瓶颈的主要原因。
  • 做业务规划 —  通过业务行为数据,了解各个业务量变化情况,并辅助我们做业务规划。
  • 做硬件规划 —  通过长期跟踪消息吞吐量的变化了解性能的变化和业务量的增长,进而辅助我们做硬件计划,避免出现在性能问题。

仅提供这些查询是很容易的,但要更好地监控集成平台的业务行为,需要更深入的了解InterSystems集成架构。

 

InterSystems集成架构

无论Ensemble、Health Connect还是InterSystems IRIS,它们都具有下面的集成架构:

通过业务服务向外发布服务、通过业务操作连接第三方接口、通过业务流程协同业务操作,它们统称为集成业务组件。可以简单理解为业务行为是由这些业务组件完成的:

  • 业务服务 = 服务,它启动一个业务流程;
  • 业务操作 = 接口,它通过接口操作第三方业务系统;
  • 业务流程 = 流程,它控制一笔业务需要按什么流程调用哪些接口。

要监控业务行为,服务、接口和流程都需要监控。

另一个需要了解的概念是关于耗时的:

完整的业务流程由会话(Session)表达,会话完成的耗时(会话耗时)和服务耗时、接口耗时不是同一件事。例如下图的一笔业务,HL7FileService这个服务在第4步就结束了,但整笔业务还在还在继续,直到第9步才结束。由服务调用者来看,服务耗时是第4步到第1步的时间差;而整个业务流程(会话)耗时是第9步到第1步的时间差。所以要注意区分服务耗时和会话耗时的概念,而通常这2个耗时都需要关注。

 

监控业务行为的手段

InterSystems提供了多种方式和数据,帮助监控业务行为,包括消息业务活动量采集自动化的业务行为报警等多种手段。

1. 消息

InterSystems 集成产品的所有消息都是自动持久化的,也就是都保存在内建的数据库里,并且通过SQL就可以查询分析的。消息分为消息头和消息体:消息头记录消息传递的流程、时间、状态、目标等信息;消息体记录具体传递的消息内容。

消息头表(Ens.MessageHeader)有以下重要字段:

字段

说明

Id

消息头主键。会话的第一个消息的Id和其SessionId相同。

SessionId

消息所属的会话。可以用这个字段将所有相同会话的消息关联起来。

IsError

是否处理错误。

TimeCreated

消息创建时间。

TimeProcessed

消息处理时间。

CorrespondingMessageId

与此消息是请求/响应消息对里的另一个消息Id。可以通过它获取相应的响应消息。

MessageBodyId

对应的消息体对象Id。可以用它找到消息体数据

可以看出,通过分析消息头表,就可以得出几乎所有业务行为的分析结果,借助消息体表,更可以分析所有的业务行为。

但实际分析业务行为时,我们并不总是用消息头表。原因是:

  • 消息头表可能非常大:例如,我们国内最大的医院客户的日消息吞吐量已经超过1亿条,按消息3个月的保存需求计算,就是90亿条以上的记录数。在不使用消息仓库的情况下,对这么大量的消息头进行频繁、复杂的SQL分析显然不会得到多好的性能。
  • 定期的消息清除:考虑到硬件资源的限制,通常都会设置定期清除消息,例如自动删除3个月前的消息。只保留有限时间的消息,无法支持更长时间范围的业务行为分析。

因此,InterSystems提供了业务活动量采集,来弥补消息分析的不足。

2. 业务活动量采集

业务活动量采集是通过一个系统提供的业务操作自动采集、统计业务活动,并保存在特定的SQL表中供查询分析。只要将Ens.Activity.Operation.Local加入集成产品(Production)中,它会自动采集所有业务组件的活动量数据,并保存到活动数据表中。

活动数据表是三张表(Ens_Activity_Data.Days\ Ens_Activity_Data.Hours\ Ens_Activity_Data.Seconds): 这3张表记录按天\小时\秒来统计的业务组件(BS\BP\BO)的调用历史。因为仅是统计数据,数据量远小于消息头表、且不受消息头表定期清除的影响,适合更长时间范围的业务活动分析。而这3张表提供了时间尺度的统计数据,方便由粗到细追踪到问题根源。

这些表有以下重要字段:

字段

说明

timeslot

统计时间。

HostName

业务组件名称。

HostType

业务组件类型:1为BS、2为BP、3为BO。

totalduration

在统计时间段内的总处理时间。

totalcount

在统计时间段内的总消息数量。

 

如何开启业务活动量采集?只要把Ens.Activity.Operation.Local加入Production的BO中:

* 注意,如果业务服务使用 SendRequest 开启业务流程,需要做代码开启,参考:https://docs.intersystems.com/irisforhealth20221/csp/docbook/DocBook.UI....

通过分析活动数据表,我们也可以得到业务行为监控的数据。但分析消息表、活动数据表,都是被动的、事后分析的。要想得到即时的性能提醒,InterSystems还提供了一系列的业务行为报警设置,让其自动报警。

3. 业务行为报警

业务组件上有很多业务行为报警设置,例如业务操作和业务流程组件有队列长度计数告警和队列等待处理时间告警设置,在队列长度超过设置值、队列中消息等待时间超过设置值的情况下,自动发出警告。很多用户设置将这些警告发送到管理员的钉钉、微信或短信平台上,以及时提醒。

通过上面提到的这些业务行为监控手段,可以加强对集成产品的监控、了解业务情况。下面我们具体看看常见的监控需求如何实现。

 

常见业务行为查询需求

1. 服务、接口和流程总览。了解集成产品有哪些组件,以及这些组件的当前运行状态(启用/禁用)。

对集成产品中业务组件及状态查询,InterSystems提供了一个查询:Ens.Config.Production类的EnumerateConfigItems查询。它的头两个入参是Production名称和组件类型。组件类型为:1 - 业务服务;2 - 业务流程;3 - 业务操作。

例如下面代码列出所有的业务服务组件、备注、是否启用、请求消息类型和响应消息类型:

ClassMethod GetServiceList() 
{
    Set tStatement = ##class(%SQL.Statement).%New(),tRet=0
    Set tStatus = tStatement.%PrepareClassQuery("Ens.Config.Production","EnumerateConfigItems")
    Set tResult = tStatement.%Execute("HCCPKG.FoundationProduction",1)
    While (tResult.%Next() '= 0) 
    {
        W tResult.%Get("ConfigName"),"--",tResult.%Get("CommentOrClassName"),"--",tResult.%Get("Enabled"),"--",tResult.%Get("RequestClasses"),"--",tResult.%Get("ResponseClasses"),!
    }
    Do tResult.%Close(),tStatement.%Close()
    Quit tRet
}

可以使用表Ens_Config.Item,它提供类似的信息,但没有区分业务组件类型。

2. 服务和接口的调用次数和耗时

通过上面提到的业务活动量采集表Ens_Activity_Data.*,可以容易地获得这类调用信息。

例如下面的SQL是查询业务服务当日被调用的次数和平均耗时,并按平均耗时降序排序:

SELECT hostname AS "服务名称", 
       SUM(totalcount) AS "调用次数"ROUND(SUM(totalduration)/SUM(totalcount),4 )*1000 AS "平均服务耗时(ms)"
  FROM Ens_Activity_Data.Days 
 WHERE timeslot = (SELECT Date(NOW()))  
   AND HostType=1
   AND HostName not LIKE 'ENS.%'
 GROUP BY hostname
 ORDER BY "平均服务耗时(ms)" DESC

3. 服务调用失败(错误)的次数

可以使用SQL通过消息头表的消息处理错误列(IsError),并关联到会话查询其业务服务:

SELECT B.sourceconfigname AS "服务名称",
       Count(*) AS "错误次数"
  FROM (SELECT distinct sessionid as sessionid FROM Ens.MessageHeader
          WHERE IsError =1
            AND TimeCreated BETWEEN '2022-08-22 00:00:00' AND '2022-08-23 00:00:00') A,
        Ens.MessageHeader B
WHERE A.sessionid=B.id
  AND NOT B.SourceConfigName %STARTSWITH 'ENS.'
GROUP BY B.sourceconfigname

 

4. 查询具有特定消息体信息的消息历史

例如查询HL7 V2消息处理历史,并返回服务名称、患者编号、开始时间、结束时间、服务耗时、服务状态、请求消息内容,响应消息内容。这类查询涉及到消息体内容(患者编号),以及请求消息和响应消息的关联关系。

对于结构化消息体,可以直接关联到消息体表;但对于半结构化消息体,例如HL7 V2消息或自定义XML字符串的消息,可以通过SearchTable技术,建立对特定消息体内容的索引表,加速查询速度。在上面的查询例子中,要找到HL7 V2消息里的患者编号,可以通过HL7的SearchTable表:EnsLib_HL7.SearchTable 进行查询,而患者编号是其字段PropId = 4的记录中PropValue字段的值。对于请求和响应消息的关联,通过消息头表的CorrespondingMessageId字段即可获得。

因此上面的查询例子,可以用下面的SQL获得:

SELECT MI."服务名称",
       MI."患者编号",
       MI."服务开始时间",
       MI."服务结束时间", 
       MI."服务耗时(ms)",
       MI."服务状态",
       MB.RawContent As "请求消息",
       ME.RawContent As "响应消息"
  FROM
(SELECT MHB.Id,MHB.sourceconfigname AS "服务名称",
       ST.PropValue AS "患者编号",
       MHB.TimeCreated AS "服务开始时间",
       MHE.TimeProcessed AS "服务结束时间", 
       DATEDIFF(ms, MHB.TimeCreated, MHE.TimeProcessed) AS "服务耗时(ms)",
       MHB.Status AS "服务状态",
       MHB.MessageBodyId As MHBBId,
       MHE.MessageBodyId As MHEBId
  FROM Ens.MessageHeader MHB 
  LEFT JOIN EnsLib_HL7.SearchTable ST ON MHB.MessageBodyId = ST.DocId AND ST.PropId = 4
  LEFT JOIN Ens.MessageHeader MHE ON MHE.CorrespondingMessageId = MHB.Id
 WHERE MHB.ID = MHB.SessionId 
   AND MHB.MessageBodyClassName='EnsLib.HL7.Message'
   AND TimeCreated BETWEEN '2022-08-22 00:00:00' AND '2022-08-23 00:00:00') MI
  LEFT JOIN EnsLib_HL7.Message MB ON MI.MHBBId=MB.ID
  LEFT JOIN EnsLib_HL7.Message ME ON MI.MHEBId=ME.ID

 

可能大家好奇,我是怎么知道的患者编号在EnsLib_HL7.SearchTablePropId = 4的记录中?

在管理门户(SMP)中,大家经常通过消息查看器页面查询和检索消息,查询和检索条件选项非常丰富,包括消息头、消息体、虚拟文档字段和Search Table字段都可以作为查询条件。

在消息查看器页面里设置好查询条件,进行搜索后再查看它生成的SQL语句(通过按钮“显示查询”),我们就知道怎么写SQL了:

没看到“显示查询”按钮?在对应的命名空间下执行下面的命令即可:

Set ^Ens.Debug("UtilEnsMessages","sql")=1

 

5. 查询业务组件间的调用关系

例如想获得所有的服务名称、对应生产方和消费方。

这类的需求很典型,但在InterSystems集成产品里,并不是那么直观可以获得的。为什么?因为消费方并非是固定的。

  • 在基于消息路由规则的场景下,消费方是消息最终发送的目标接口,理论上可以通过查询路由规则获得。
  • 在业务流程模型里,调用方(消费方)可以是动态的。
  • 当在发布/订阅模式下,订阅方并不是“编码”实现的,而且可以灵活的参与或取消订阅。

因此要想知道明确的生产方和消费方需要知道很多信息,而这也是业务流程灵活性的体现。我们可以考虑通过对消息流程历史的分析来简单获得:

SELECT distinct B.sourceconfigname AS '生产方',A.TargetConfigName AS '消费方'
 FROM
      (SELECT sessionid,TargetConfigName
        FROM  Ens.MessageHeader 
       WHERE TargetBusinessType='3') A,
             (SELECT sessionid,sourceconfigname 
               FROM  Ens.MessageHeader 
              WHERE id=sessionid) B
WHERE A.sessionid=B.sessionid

因为消息可能被删除,因此需要注意使用它的局限性。(这里的TargetBusinessType='3'中的数字3表示BusinessOperation)

 

6. 查询会话耗时

前面介绍了服务耗时和会话耗时的差异,会话耗时和服务耗时有时并不一致。要分析每笔会话的完整耗时?可以参考这个会话耗时的SQL查询方法:

SELECT SourceConfigName AS "服务名称",
       max(ti) AS "会话最大耗时",
       min(ti) AS "会话最小耗时",
       avg(ti) AS "会话平均耗时"
 FROM
 (SELECT a.SourceConfigName, 
        a.SessionId, 
        tp,TimeCreated,
        {fn TIMESTAMPDIFF(SQL_TSI_SECOND,TimeCreated,tp)} as ti
       FROM (SELECT SourceConfigName ,SessionId, TimeCreated 
               FROM Ens.MessageHeader 
              WHERE ID= SessionId) a,
            (SELECT SessionId, max(TimeProcessed) tp 
               FROM Ens.MessageHeader 
              GROUP BY SessionId) b
      WHERE a.sessionid=b.sessionid)
GROUP BY SourceConfigName

 

如果大家有其它对业务行为分析的需求,欢迎留言。

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