如何在不使用 Tomcat JDBC 的情况下为基于 Hikari 的项目设置 AWS X-Ray SQL 检测?

How do I set up AWS X-Ray SQL Instrumentation for Hikari based projects without using Tomcat JDBC?

目前AWS Documentation for AWS X-Ray没有对Java项目不使用TomcatJDBC给出任何解决方案。

为了使用 spring-boot-data-jpa 检测数据库查询,您还需要包括 Tomcat JDBC 作为依赖项,并设置 Tomcat DataSource 对象以及你的 Hikari 拦截器,并通过以下任一方式将 XRay 拦截器作为 JDBC 拦截器包括在内:

dataSource.setJdbcInterceptors("com.amazonaws.xray.sql.postgres.TracingInterceptor;");

spring.datasource.jdbc-interceptors=com.amazonaws.xray.sql.postgres.TracingInterceptor

Gradle:

dependencies {
    ...
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    implementation "com.amazonaws:aws-java-sdk-core"
    implementation "com.amazonaws:aws-xray-recorder-sdk-core" // Required for core xray features
    implementation "com.amazonaws:aws-xray-recorder-sdk-spring" // Required for spring annotations
    implementation "com.amazonaws:aws-xray-recorder-sdk-sql-postgres" // required for db callouts

    implementation 'org.apache.tomcat:tomcat-jdbc:9.0.31'
    ...
}

数据库配置(Spring):

    @Bean(name = "dataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        final org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
        dataSource.setUsername(getUsername());
        dataSource.setPassword(getPassword());
        dataSource.setUrl(POSTGRES_URL_PREFIX
                + getHost()
                + ":" + getPort()
                + "/" + getName()
                + "?stringtype=unspecified");
        dataSource.setDriverClassName(getDriver());
        dataSource.setJdbcInterceptors("com.amazonaws.xray.sql.postgres.TracingInterceptor;");

        final HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setDataSource(dataSource);

        return hikariDataSource;
    }

我觉得这很笨拙,如果可能的话,我宁愿不必将 Tomcat JDBC 作为附加依赖项。

不使用 Tomcat 就没有办法解决这个问题吗?

其他说明:

事实证明,someone else had this question. The AWS developers have been working on this in a feature branch 已于 2019 年 11 月合并到 SDK 版本 2.3.0

我还没有找到关于这个新功能的任何文档,所以在深入研究 PR 代码之后,我发现它比以前的实现更简单。

只需将以下依赖项而不是 postgres 特定的依赖项添加到您的 build.gradle 文件(或类似文件)中:

    implementation "com.amazonaws:aws-java-sdk-core:2.4.0"
    implementation "com.amazonaws:aws-xray-recorder-sdk-core:2.4.0"
    implementation "com.amazonaws:aws-xray-recorder-sdk-sql:2.4.0"

然后只需在您的数据源配置中创建一个 TracingDataSource 对象,并将您的原始 javax.sql.DataSource 对象传递给它。这应该在您用来创建 DataSource bean 的任何 @Configuration 注释 class 中完成。

    @Bean(name = "dataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        final DataSource dataSource = DataSourceBuilder
                .create()
                .username(getUsername())
                .password(getPassword())
                .url(POSTGRES_URL_PREFIX
                        + getHost()
                        + ":" + getPort()
                        + "/" + getName()
                        + "?stringtype=unspecified")
                .driverClassName(getDriver())
                .build();

        final TracingDataSource tracingDataSource = new TracingDataSource(dataSource);
        return tracingDataSource;
    }

仅此而已。我真的希望这对某人有帮助,因为我花了很多时间试图在没有 Tomcat 的情况下使它工作,而且 AWS 文档在这里也没有帮助。

J Hamm 的回答无疑提供了一个很好的起点,但我不想手动设置 DataSourceBuilder 属性。此外,没有为我设置 Hikari 特定属性。我最终得到以下结果:

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource")
    //DataSourceProperties will convert `spring.datasource.url` property to hikari's jdbcUrl property
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean(name = "hikariDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.hikari")//set hikari specific properties
    public HikariDataSource hikariDataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean(name = "dataSource")
    @Primary
    public DataSource dataSource(HikariDataSource hikariDataSource) {
        //wrap the spring ds into xray tracing ds
        var tracingDataSource = new TracingDataSource(hikariDataSource);
        return tracingDataSource;
    }

不幸的是,所有这些最终都白费了,因为 x-ray 不记录执行的 SQL 语句,这使得它几乎毫无用处。参见 https://github.com/aws/aws-xray-sdk-java/issues/28

使用 TracingDataSource 解决了同样的问题,同时仍然使用 HikariCP 作为连接池

引用https://github.com/aws/aws-xray-sdk-java/issues/88#issuecomment-570328275

代码:(注意,我正在使用 AWS Secrets Manager JDBC 库 aws-secretsmanager-jdbc 使用存储在 AWS Secrets Manager 中的秘密连接到数据库)

import com.amazonaws.xray.sql.TracingDataSource;

...
...
@Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {

        return TracingDataSource
                .decorate(DataSourceBuilder.create()
                        .driverClassName("com.amazonaws.secretsmanager.sql.AWSSecretsManagerPostgreSQLDriver")
                        .url("jdbc-secretsmanager:postgresql://" + System.getenv("PGHOST") + ":"
                                + System.getenv("PGPORT") + "/" + System.getenv("PGDATABASE"))
                        .username(System.getenv("SECRET_NAME")).build());

    }

依赖关系:

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-xray-recorder-sdk-sql</artifactId>
</dependency>