【问题标题】:How to define a data source programatically?如何以编程方式定义数据源?
【发布时间】:2021-10-19 08:24:58
【问题描述】:

我正在尝试使用 OpenTelemetry 收集数据源指标。根据the OpenTelemetry documentation,必须将DataSource 实例包装到OpenTelemetryDataSource 数据源中,如下所示:

new OpenTelemetryDataSource(dataSource);

我浏览了the Quarkus documentation,但没有找到以编程方式覆盖数据源的方法。

如何在 Quarkus 中以编程方式覆盖数据源?或者一般我如何将 OpenTelemetry 连接到 Quarkus 中的数据源?

更新: 当我使用配置属性 (quarkus.datasource.jdbc.url=jdbc:otel:postgresql://localhost:5432/my_db) 设置 JDBC URL 时,应用程序在启动时出现 NPE 失败:

ERROR: Failed to start application (with profile dev)
java.lang.NullPointerException
        at io.quarkus.opentelemetry.runtime.QuarkusContextStorage.getVertxContext(QuarkusContextStorage.java:62)
        at io.quarkus.opentelemetry.runtime.QuarkusContextStorage.current(QuarkusContextStorage.java:54)
        at io.opentelemetry.context.Context.current(Context.java:86)
        at io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryStatement.wrapCall(OpenTelemetryStatement.java:277)
        at io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryPreparedStatement.executeQuery(OpenTelemetryPreparedStatement.java:53)
        at io.agroal.pool.wrapper.PreparedStatementWrapper.executeQuery(PreparedStatementWrapper.java:78)
        at org.flywaydb.core.internal.database.base.BaseDatabaseType.getSelectVersionOutput(BaseDatabaseType.java:195)
        at org.flywaydb.core.internal.database.cockroachdb.CockroachDBDatabaseType.handlesDatabaseProductNameAndVersion(CockroachDBDatabaseType.java:81)
        at org.flywaydb.core.internal.database.DatabaseTypeRegister.getDatabaseTypeForConnection(DatabaseTypeRegister.java:136)
        at org.flywaydb.core.internal.jdbc.JdbcConnectionFactory.<init>(JdbcConnectionFactory.java:69)
        at org.flywaydb.core.Flyway.execute(Flyway.java:510)
        at org.flywaydb.core.Flyway.migrate(Flyway.java:170)
        at io.quarkus.flyway.runtime.FlywayRecorder.doStartActions(FlywayRecorder.java:75)
        at io.quarkus.deployment.steps.FlywayProcessor$createBeansAndStartActions-1520831253.deploy_0(FlywayProcessor$createBeansAndStartActions-1520831253.zig:84)
        at io.quarkus.deployment.steps.FlywayProcessor$createBeansAndStartActions-1520831253.deploy(FlywayProcessor$createBeansAndStartActions-1520831253.zig:40)
        at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:754)
        at io.quarkus.runtime.Application.start(Application.java:101)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:101)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
        at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:98)
        at java.base/java.lang.Thread.run(Thread.java:829)

【问题讨论】:

  • 您要使用什么数据源?文档为什么不适合您?
  • @AndersLindgren 我正在尝试将 quarkus 提供的数据源变形为 OpenTelemetryDataSource。我没有找到如何在 Quarkus 中实现这一点。如果我遗漏了什么,请您指出。
  • 您链接到的 opentelemetry 站点上的文档似乎没问题。我不是 quarkus 或 opentelemetry 方面的专家,但你看过quarkus.io/guides/opentelemetry
  • @AndersLindgren 谢谢。 Opentelemetry 已经连接到应用程序,只是它不包括 JDBC 连接(需要额外配置)。

标签: java jdbc datasource quarkus open-telemetry


【解决方案1】:

[编辑]

没关系,看起来 quarkus 框架确实提供了一种交换数据源实现的方法。 它在 opentelemetry 文档中明确提到。 https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/jdbc/library

查阅 quarkus 框架文档并了解如何配置它以允许手动 DataSource 实例化。在您需要创建的 DataSource 提供程序类中,您只需自己读取包含所有数据库连接详细信息的属性文件(与 quarkus 框架读取的相同),获取适当的键并实例化 AgroalDataSource 对象,如下面的答案所示,将它们包装在 OpenTelemetryDataSource 中并返回 OpenTelemetryDataSource 对象。

不幸的是,数据源不是数据库连接,您根本无法通过在属性文件中操作 jdbc url 来强制您的 quarkus 框架使用您以编程方式定义的数据源。

根据https://quarkus.io/guides/datasource 上的文档 quarkus 使用农业数据源https://github.com/agroal/agroal

查阅 agroal 文档以了解如何构建数据源 https://github.com/agroal/agroal/tree/master/agroal-api 这是我发现的:

@SuppressWarnings( "ALL" )
class AgroalSampleUsage {

    public static void main() {
        AgroalDataSourceConfigurationSupplier configuration = new AgroalDataSourceConfigurationSupplier()
                .dataSourceImplementation( DataSourceImplementation.AGROAL )
                .metricsEnabled( false )
                .connectionPoolConfiguration( cp -> cp
                        .minSize( 5 )
                        .maxSize( 20 )
                        .initialSize( 10 )
                        .connectionValidator( defaultValidator() )
                        .acquisitionTimeout( ofSeconds( 5 ) )
                        .leakTimeout( ofSeconds( 5 ) )
                        .validationTimeout( ofSeconds( 50 ) )
                        .reapTimeout( ofSeconds( 500 ) )
                        .connectionFactoryConfiguration( cf -> cf
                                .jdbcUrl( "jdbc:h2:mem:test" )
                                .connectionProviderClassName( "org.h2.Driver" )
                                .autoCommit( false )
                                .jdbcTransactionIsolation( SERIALIZABLE )
                                .principal( new NamePrincipal( "username" ) )
                                .credential( new SimplePassword( "secret" ) )
                        )
                );

        try ( AgroalDataSource dataSource = AgroalDataSource.from( configuration ) ) {
            Connection connection = dataSource.getConnection();
            connection.close();
        } catch ( SQLException e ) {
            System.out.println( "Oops! " + e.getMessage() );
        }
    }
}

除非相关框架的可配置性足以让您交换数据源实现,即不使用 argoal,而是使用另一个。如果不修改框架,您将无法实现您想要实现的目标

如果您希望访问框架实例化的数据源

根据在 https://quarkus.io/guides/datasource#named-datasource-injection

quarkus 数据源可以通过依赖注入来检索。

public class MyDatasourceWrapper {

    private Object lock = new Object();

    @Inject
    @DataSource("users") // or ignore
    private AgroalDataSource usersDataSource;

    private OpenTelemetryDataSource wrappedDataSource;

    public AgroalDataSource getUsersDataSource() {
        return usersDataSource;
    }

    public OpenTelemetryDataSource getUsersOpenTelemetryDataSource() {
        if(wrappedDataSource == null) {
            synchronized (lock) {
                wrappedDataSource = new OpenTelemetryDataSource(usersDataSource);
            }
        }
        return wrappedDataSource;
    }
}

这样使用:

public class ClassUsingDataSource {

    @Inject
    private MyDatasourceWrapper dataSourceWrapper;

    public void run() {
        OpenTelemetryDataSource dataSource = dataSourceWrapper.getUsersOpenTelemetryDataSource();
         // dataSource. use ()
    }
}

但是框架不会使用封装的 OpenTelemetryDataSource。

您可以做的一件事是假设使用框架本身实例化的数据源的框架部分将通过依赖注入和 DataSoruce 接口使用,在这种情况下,您可以创建一个 DataSource 拦截器并包装传入的DataSource 对象到 OpenTelemetryDataSource 对象。 但这种方法也可能做出假设。

如果你有权限并且被允许修改quarkus框架我建议在属性文件中创建一个属性,称之为数据源包装器,它的值应该是一个包含方法的类

public DataSource wrap(DataSource dataSource);

如果你有这种访问权限,还要确保在框架的源代码中的任何地方都使用接口 DataSource 而不是 AgroalDataSource 类,因为这可能会导致 ClassCastException

【讨论】:

    【解决方案2】:

    this open issue 中似乎已经提出了一些解决方法,但以编程方式配置 Quarkus 数据源仍然是一个功能请求。

    另一个issue,虽然与 OpenTracing 而不是 OpenTelemetry 相关,但似乎证实了这一点。

    可能最好的方法是尝试使用配置属性来配置此集成。

    请根据Quarkus documentation,并以OpenTracing为例,尝试如下配置:

    quarkus.datasource.db-kind=postgresql
    quarkus.datasource.jdbc.driver=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver
    quarkus.datasource.jdbc.url=jdbc:otel:postgresql://localhost:5432/hibernate_orm_test
    # other configuration properties ...
    

    请注意 URL 中的前缀。该示例是针对 PostgreSql 的,但对于其他数据库应该非常相似。

    配置灵感来自OpenTelemetry documentation

    根据你的cmets,这个配置和Quarkus在初始化时进行的Flyway数据库迁移过程有冲突。

    为了解决该问题,您可以在启动时禁用 Quarkus Flyway 迁移并以编程方式执行迁移。

    Quarkus documentation 中所述,您可以通过首先将配置属性quarkus.flyway.migrate-at-start 设置为false(默认为true)来实现此行为。

    然后,以编程方式运行迁移:

    @ApplicationScoped
    public class MigrationService {
        // You can Inject the object if you want to use it manually
        @Inject
        Flyway flyway; 
    
        public void checkMigration() {
            flyway.clean(); 
            flyway.migrate();
        }
    }
    

    【讨论】:

    • 谢谢。不幸的是,为 JDBC URL 添加前缀不起作用,它在加载 Quarkus 上下文时因 NPE 而失败。
    • 嗨@SashaShpota。非常感谢您的反馈。得知司机的方法不起作用,我感到非常遗憾。请,如果您不介意,您能提供错误堆栈跟踪吗?
    • @SashaShpota,你能用quarkus.datasource.db-kind=postgresql代替quarkus.datasource.db-kind=other吗?
    • 是的,以编程方式运行 Flyway 解决了获取 null vertx 的问题。谢谢?
    • 欢迎您@SashaShpota。我很高兴听到这个消息!!我将更新答案以包含该信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-27
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    相关资源
    最近更新 更多