文章
· 11 hr 前 阅读大约需 9 分钟

使用 Java Native SDK 分析 IRIS ^PERFMON 运行时性能

技术文档 — Quarkus IRIS Monitor System

1. 目的与范围

此模块支持在基于 Quarkus 的 Java 应用程序与 InterSystems IRIS 的原生性能监控功能之间进行集成。
它使开发者可以通过 @PerfmonReport 对方法添加注释,这样可以在执行方法时自动触发 IRIS 的 ^PERFMON 例程,以生成性能报告,而无需人工干预。


2. 系统组件

2.1 注释:@PerfmonReport

  • 定义为 CDI InterceptorBinding
  • 可应用于方法或类。
  • 指示使用 IRIS 监控逻辑包装方法执行的框架。

2.2 拦截器:PerfmonReportInterceptor

  • 拦截对带注释的方法的调用。
  • 执行流程:

    1. 记录启动事件 (LOG.infof("INIT: …"))
    2. 调用 monitorSystem.startPerfmon()
    3. 继续执行 context.proceed()
    4. finally 块中:
    • 调用 monitorSystem.generateReportPerfmon(...)
    • 调用 monitorSystem.stopPerfmon()
    • 记录结束事件及执行时间
  • 确保即使抛出异常,监控也始终结束。

2.3 DAO Bean:MonitorSystem

  • 带有 @ApplicationScoped 注释的 CDI bean。
  • 保存启动时初始化的 IRIS 的单个实例。
  • 通过 @ConfigProperty 注入的配置(JDBC URL、用户、密码)。
  • 使用 DriverManager.getConnection(...) 获得原始 IRISConnection
  • 包含方法:

    • startPerfmon()
    • generateReportPerfmon(String reportName)
    • stopPerfmon()
  • 每一个都通过 iris.classMethodVoid(...) 调用 iris.src.dc.AdapterPerfmonProc 中合适的 ObjectScript 方法。

2.4 ObjectScript 适配器:iris.src.dc.AdapterPerfmonProc

  • 定义封装 ^PERFMON 逻辑的例程:

    Class iris.src.dc.AdapterPerfmonProc Extends %RegisteredObject
    {
        ClassMethod start() As %Status
        {
            Set namespace = $NAMESPACE
            zn "%SYS"
            set status = $$Stop^PERFMON()
            set status = $$Start^PERFMON()
            zn namespace
            return status
        }
    
        ClassMethod generateReport(nameReport As %String = "report.txt") As %Status
        {
            Set namespace = $NAMESPACE
            zn "%SYS"
            Set tempDirectory = ##class(%SYS.System).TempDirectory()
            set status = $$Report^PERFMON("R","R","P", tempDirectory_"/"_nameReport)
            zn namespace
    
            return status
        }
    
        ClassMethod stop() As %Status
        {
            Set namespace = $NAMESPACE
            zn "%SYS"
            Set status = $$Stop^PERFMON()
            zn namespace
    
            return status
        }
    }
    
  • 在命名空间 %SYS 中运行以访问 ^PERFMON 例程,然后返回到原始命名空间。


3. 执行流程

  1. 一个请求进入 Quarkus 应用程序。
  2. CDI 拦截器检测 @PerfmonReport 注释并拦截方法调用。
  3. monitorSystem.startPerfmon() is invoked, triggering IRIS ^PERFMON monitoring.
  4. 业务方法正常执行(数据访问、转换、逻辑等)。
  5. 在方法返回或抛出异常后,拦截器确保:
    • 调用 monitorSystem.generateReportPerfmon(...) 以创建 .txt 性能报告。
    • 执行 monitorSystem.stopPerfmon() 以停止监控会话。
    • 使用 Logger.infof(...) 记录 Java 端的总执行时间。
  6. 生成的报告文件存储在 IRIS 临时目录中,通常为:/usr/irissys/mgr/Temp/

    • 文件名遵循以下模式:
      <ClassName><MethodName><timestamp>.txt

4. 技术挑战和解决方案

挑战 解决方案
在使用池化 JDBC 连接时出现 ClassCastException 使用 DriverManager.getConnection(...) 获得原生 IRISConnection 而非池化 ConnectionWrapper
反复打开连接产生的开销 @ApplicationScoped bean 中维护单个 IRIS 实例,通过 @PostConstruct 初始化。
确保即使在出现异常时 ^PERFMON 也始终停止 在拦截器中使用 try-finally 来调用 stopPerfmon()generateReportPerfmon()
配置可移植性 使用 @ConfigPropertyapplication.properties 注入连接设置(jdbc.urlusernamepassword
管理并发监控会话 避免对高度并发的端点添加注释。 以后的版本可能会实现会话级隔离。

5. 用例和优势

  • 可以从 Java 代码实时查看 IRIS 运行时活动。
  • 为开发者简化了性能分析查询优化工作。
  • 基准测试分析系统回归测试很有用。
  • 可以作为关键操作的轻量级性能审核跟踪

6. 实际使用示例

请在以下位置查看完整的源代码和部署设置:


6.1 概述

该应用程序运行一个 Quarkus 服务器,后者连接到使用 FHIRSERVER 命名空间配置的 InterSystems IRIS 实例
ORM 层使用带 PanacheRepository 的 Hibernate ORM 实现,允许在 Java 实体与 IRIS 数据库类之间直接映射。

当应用程序(通过 docker-compose up)启动时,它会打开:
- IRIS 容器,托管 FHIR 数据模型和 ObjectScript 例程(包括 AdapterPerfmonProc);
- Quarkus 容器,公开 REST 端点并通过原生 JDBC 驱动程序连接到 IRIS。


6.2 REST 端点

REST 资源会公开一个简单的端点来检索患者信息:

@Path("/patient")
public class PatientResource {

    @Inject
    PatientService patientService;

    @GET
    @Path("/info")
    @Produces(MediaType.APPLICATION_JSON)
    public PatientInfoDTO searchPatientInfo(@QueryParam("key") String key) {
        return patientService.patientGetInfo(key);
    }
}

此端点接受一个查询参数(密钥),该参数标识了 FHIR 数据仓库中的患者资源。


### 6.3 带有 @PerfmonReport 的服务层

PatientService 类包含用于检索和撰写患者信息的业务逻辑。
它带有 @PerfmonReport 注释,这意味着对 /pantion/info 的每个请求都会触发 IRIS 性能监控:

@ApplicationScoped
public class PatientService {

    @Inject
    PatientRepository patientRepository;

    @PerfmonReport
    public PatientInfoDTO patientGetInfo(String patientKey) {

        Optional<Patient> patientOpt = patientRepository.find("key", patientKey).firstResultOptional();
        Patient patient = patientOpt.orElseThrow(() -> new IllegalArgumentException("Patient not found"));

        PatientInfoDTO dto = new PatientInfoDTO();
        dto.setKey(patient.key);
        dto.setName(patient.name);
        dto.setAddress(patient.address);
        dto.setBirthDate(patient.birthDate != null ? patient.birthDate.toString() : null);
        dto.setGender(patient.gender);
        dto.setMedications(patientRepository.findMedicationTextByPatient(patientKey));
        dto.setConditions(patientRepository.findConditionsByPatient(patientKey));
        dto.setAllergies(patientRepository.findAllergyByPatient(patientKey));

        return dto;
    }
}

6.4 执行流程

GET /patient/info?key=Patient/4 提出请求

Quarkus 将请求路由到 PatientResource.searchPatientInfo()

CDI 拦截器在 PatientService.patientGetInfo() 中检测 @PerfmonReport 注释。

在执行服务逻辑前:

  • 拦截器调用 MonitorSystem.startPerfmon(),这会调用 IRIS 类 iris.src.dc.AdapterPerfmonProc.start()

  • 方法执行业务逻辑,使用 Hibernate PanacheRepository 映射查询患者数据。

在方法完成后:

  • 调用 MonitorSystem.generateReportPerfmon() 以创建性能报告。

  • MonitorSystem.stopPerfmon() 停止 IRIS 性能监视器。

usr/irissys/mgr/Temp/ 下生成一个 .txt 报告

示例文件名:PatientService_patientGetInfo_20251005_161906.txt

6.5 结果

生成的报告包含详细的 IRIS 运行时统计信息,例如:

                         Routine Activity by Routine

Started: 10/11/2025 05:07:30PM                    Collected: 10/11/2025 05:07:31PM

Routine Name                        RtnLines  % Lines   RtnLoads  RtnFetch  Line/Load Directory
----------------------------------- --------- --------- --------- --------- --------- ---------
Other                                     0.0       0.0       0.0       0.0         0
PERFMON                                  44.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
%occLibrary                         3415047.0      34.1   48278.0       0.0      70.7 /usr/irissys/mgr/irislib/
iris.src.dc.AdapterPerfmonProc.1          7.0       0.0       2.0       0.0       3.5 /usr/irissys/mgr/FHIRSERVER/
%occName                            5079994.0      50.7       0.0       0.0         0 /usr/irissys/mgr/irislib/
%apiDDL2                            1078497.0      10.8   63358.0       0.0      17.0 /usr/irissys/mgr/irislib/
%SQL.FeatureGetter.1                 446710.0       4.5   66939.0       0.0       6.7 /usr/irissys/mgr/irislib/
%SYS.WorkQueueMgr                       365.0       0.0       1.0       0.0     365.0 /usr/irissys/mgr/
%CSP.Daemon.1                            16.0       0.0       1.0       0.0      16.0 /usr/irissys/mgr/irislib/
%SYS.TokenAuth.1                         14.0       0.0       5.0       0.0       2.8 /usr/irissys/mgr/
%Library.PosixTime.1                      2.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislib/
%SYS.sqlcq.uEXTg3QR7a7I7Osf9e8Bz...      52.0       0.0       1.0       0.0      52.0 /usr/irissys/mgr/
%SYS.SQLSRV                              16.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
%apiOBJ                                 756.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislib/
FT.Collector.1                            0.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
SYS.Monitor.FeatureTrackerSensor.1        0.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
%SYS.Monitor.Control.1                    0.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
%SYS.DBSRV.1                            252.0       0.0       4.0       0.0      63.0 /usr/irissys/mgr/
%sqlcq.FHIRSERVER.cls12.1                19.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislocaldata/
%sqlcq.FHIRSERVER.cls13.1                74.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislocaldata/
%sqlcq.FHIRSERVER.cls14.1                74.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislocaldata/
%sqlcq.FHIRSERVER.cls15.1                52.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislocaldata/
%SYS.System.1                             1.0       0.0       0.0       0.0         0 /usr/irissys/mgr/

通过这些数据,可以精确了解 IRIS 在 REST 调用期间内部执行了哪些例程 — 包括 SQL 编译、执行和 FHIR 数据访问。

洞察%sqlcq.FHIRSERVER.* 例程会捕获 Quarkus 在该方法中执行的所有 SQL 缓存查询。 监控这些例程可以让开发者分析查询执行情况、了解代码行为并发现潜在的性能瓶颈。 这使它们成为开发和调试 FHIR 相关操作的强大工具。

image

6.6 总结

此示例演示了标准 Quarkus 服务如何使用 @PerfmonReport 注释以透明的方式利用 IRIS 原生监控工具。
它结合了:

  • CDI 拦截器 (Quarkus)

  • Hibernate PanacheRepositories (ORM)

  • IRIS 原生 ObjectScript 例程 (^PERFMON)

结果是产生了一种完全自动化、可重现的性能分析机制,这种机制可以应用于应用程序中的任何服务方法。

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