在 Oracle DB 中唯一标识源 JDBC 进程

Uniquely identify source JDBC process in Oracle DB

我们正在使用 java(Spring 和 Spring 启动)开发微服务,并且通过 JDBC Oracle 驱动程序访问我们的 Oracle 数据库。

问题是我们的DBA只能在Oracle端看到连接了一个"JDBC Thin Client"。连接的应用程序没有更好的逻辑表示。如果没有这样的识别,就很难知道哪个微服务可能表现不佳。其他非 JDBC 客户端使用主机名清楚地表明自己。

有什么方法可以更改标识字符串,使其代表源的明确身份application/process?

注意:我们的系统在使用容器的 Cloud Foundry 上运行,因此实际上不可能提供机器名称或类似名称 - 最好是逻辑应用程序名称。

谢谢

数据库用户

如果您将用于连接的数据库用户命名为微服务,DBA 应该能够将连接映射到微服务。

IP 地址

对于数据库,连接还包含传入 IP 地址。使用微服务的 table IP 地址,DBA 可能能够将连接映射到微服务。

恭敬

对于DBA来说,寻找程序员的错误可能是一项令人兴奋的工作。如果程序员和 DBA 之间的关系协调,谈话可能会解决他们出现的问题(在代码中)。如果不能很快协调一致,更详细的合同或规范可能是一个解决方案。

这看起来像是一种变通方法。错误在代码中,让我们在代码中找到它。

取消资格 API

如果您想通过数据库连接来识别微服务,您将取消使用 API 进行数据库访问的资格。如果您想将单一真实来源作为微服务,数据库 API 可能会有用。

根据您的 Oracle 版本,该功能在方法中实现 setEndToEndMetrics(自 12.1 起已弃用,取而代之的是 setClientInfo()) 或 setClientInfo

这里是用法的一个小例子。客户端(您的服务)在获取连接(通常来自连接池)后设置属性 actionclientIdmodule

String[] metrics = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
 metrics[OracleConnection.END_TO_END_ACTION_INDEX] = 'myAction1';
 metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = 'myClient';
 metrics[OracleConnection.END_TO_END_MODULE_INDEX] = 'myModule1';
 con.setEndToEndMetrics(metrics, (short) 0);

并在返回连接之前重置它们。

DBA 可以通过以下查询观察 V$SESSION 中的设置

select sid,  client_info, module, action from v$session

因此她不仅可以将数据库会话与服务相关联,而且客户端/模块和操作的组合可以提供服务状态的更多详细信息。

需要考虑三件事:

仅当所有服务在设置值时都建立了特定的纪律 时才有效。在重新使用连接池中的会话时,很容易 "inherit" 先前服务的错误设置。我建议将其作为连接池资源处理的一个方面来实现。

进一步 Java 版本,JDBC 驱动程序和 Oracle Server 必须具有 兼容版本 ,因此最好在简单的脚本。

最后 不要 用于设置 PL/SQL API(PL/SQL 开发人员自然会这样做)。最大的区别在于 PL/SQL API 会触发到数据库的往返,而 JDBC API 不会(值随下一个请求一起发送)。

JDBC 连接 属性“oracle.jdbc.v$session.process”可以设置(作为系统属性 -D)为唯一标识您的微服务的值,然后可以在V$SESSION 视图("process" 列)。

谢谢大家的建议, 我都试过了,遗憾的是他们没有用。

这可能是因为我使用 Spring 数据和默认的 Hikari 连接池进行连接。

在花费数小时后,最终解决方案在这里找到:Spring Boot 1.3.5 with Hikari Connection Pool not able to set program name in v$session

spring:
  datasource:
    hikari:
      data-source-properties:
         v$session.program: AppName

简单,无需更改代码,而且有效!

如果您有权访问连接到 Oracle 数据库的代码,您可以尝试:

private static String getProcessId(final String fallback) {
 // Note: may fail in some JVM implementations
 // therefore fallback has to be provided
 // something like '<pid>@<hostname>', at least in SUN / Oracle JVMs
 final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
 final int index = jvmName.indexOf('@');

 if (index < 1) {
   // part before '@' empty (index = 0) / '@' not found (index = -1)
   return fallback;
 }

 try {
   return Long.toString(Long.parseLong(jvmName.substring(0, index)));
   } catch (NumberFormatException e) {
   // ignore
   }
 return fallback;
 }



public void init() {
    java.util.Properties props = new java.util.Properties();
    String javaPid;
    try {
        oracleConnexionPool = new OracleConnectionPoolDataSource();
        oracleConnexionPool.setDriverType(oracle.jdbc.driver.OracleDriver);
        //Java 9+ version:
        //long pid = ProcessHandle.current().pid();
        //Java < 9 version:
        try
        {
            javaPid = getProcessId("<PID>");
            props.put("v$session.process", javaPid);
            props.put("v$session.program", "<Your program name>");
            oracleConnexionPool.setConnectionProperties(props);
        }
        catch (SQLException e) {
            }

        oracleConnexionPool.setURL(<DB URL>);

如果您可以访问正在启动的命令行 java,请尝试:

java ...-Doracle.jdbc.v$session.process=$$ ...

用于识别会话 "belongs to" 的可用关键字(Unix 样式语法):

java ...-Doracle.jdbc.v$session.process=<My PID> \
  -Doracle.jdbc.v$session.machine="<My machine>" \
  -Doracle.jdbc.v$session.osuser="<My OS username>" \
  -Doracle.jdbc.v$session.program="<My program>" \
  -Doracle.jdbc.v$session.terminal="<My term>" ...