在 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
这里是用法的一个小例子。客户端(您的服务)在获取连接(通常来自连接池)后设置属性
action
、clientId
和 module
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>" ...
我们正在使用 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
这里是用法的一个小例子。客户端(您的服务)在获取连接(通常来自连接池)后设置属性
action
、clientId
和 module
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>" ...