使用 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
拦截对带注释的方法的调用。
执行流程:
- 记录启动事件 (
LOG.infof("INIT: …")) - 调用
monitorSystem.startPerfmon() - 继续执行
context.proceed() - 在
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. 执行流程
一个请求进入 Quarkus 应用程序。
CDI 拦截器检测
@PerfmonReport注释并拦截方法调用。monitorSystem.startPerfmon()is invoked, triggering IRIS^PERFMONmonitoring.业务方法正常执行(数据访问、转换、逻辑等)。
在方法返回或抛出异常后,拦截器确保:
- 调用
monitorSystem.generateReportPerfmon(...)以创建.txt性能报告。 - 执行
monitorSystem.stopPerfmon()以停止监控会话。 - 使用
Logger.infof(...)记录 Java 端的总执行时间。
- 调用
生成的报告文件存储在 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()。 |
| 配置可移植性 | 使用 @ConfigProperty 和 application.properties 注入连接设置(jdbc.url、username、password) |
| 管理并发监控会话 | 避免对高度并发的端点添加注释。 以后的版本可能会实现会话级隔离。 |
5. 用例和优势
- 可以从 Java 代码实时查看 IRIS 运行时活动。
- 为开发者简化了性能分析和查询优化工作。
- 对基准测试、分析和系统回归测试很有用。
- 可以作为关键操作的轻量级性能审核跟踪。
6. 实际使用示例
请在以下位置查看完整的源代码和部署设置:
- Open Exchange:https://openexchange.intersystems.com/package/quarkus-iris-monitor-system
- GitHub 仓库:https://github.com/Davi-Massaru/quarkus-iris-monitor-system
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 相关操作的强大工具。

6.6 总结
此示例演示了标准 Quarkus 服务如何使用 @PerfmonReport 注释以透明的方式利用 IRIS 原生监控工具。 它结合了:
CDI 拦截器 (Quarkus)
Hibernate PanacheRepositories (ORM)
IRIS 原生 ObjectScript 例程 (^PERFMON)
结果是产生了一种完全自动化、可重现的性能分析机制,这种机制可以应用于应用程序中的任何服务方法。