【问题标题】:HikariCP NullPointerExceptionHikariCP NullPointerException
【发布时间】:2016-06-20 19:40:26
【问题描述】:

我们正在尝试将 HikariCP(2.4.4 版)集成到我们的应用程序中。使用一段时间后,池无法获取新连接抛出:

    java.lang.NullPointerException
        at com.zaxxer.hikari.pool.PoolBase.setNetworkTimeout(PoolBase.java:464) ~[HikariCP-2.4.4.jar:?]
        at com.zaxxer.hikari.pool.PoolBase.isConnectionAlive(PoolBase.java:131) ~[HikariCP-2.4.4.jar:?]
        at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:171) ~[HikariCP-2.4.4.jar:?]
        at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:147) ~[HikariCP-2.4.4.jar:?]
        at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:83) ~[HikariCP-2.4.4.jar:?]

我们使用的 jdbc 驱动程序是 ojdbc-7,版本为 12.1.0.2。池使用以下配置:

    allowPoolSuspension.............false
    autoCommit......................false
    catalog.........................null
    connectionInitSql..............."BEGIN EXECUTE IMMEDIATE 'SET ROLE SOME_ROLE IDENTIFIED BY SOME_PASSWORD '; END;"
    connectionTestQuery............."SELECT 1 FROM DUAL"
    connectionTimeout...............15000
    dataSource......................null
    dataSourceClassName.............null
    dataSourceJNDI..................null
    dataSourceProperties............{v$session.machine=host, password=<masked>, v$session.program=my application}
    driverClassName................."oracle.jdbc.OracleDriver"
    healthCheckProperties...........{}
    healthCheckRegistry.............null
    idleTimeout.....................60000
    initializationFailFast..........true
    isolateInternalQueries..........false
    jdbc4ConnectionTest.............false
    jdbcUrl........................."jdbc:oracle:thin:@//host.company.com:1521/database.company.com"
    leakDetectionThreshold..........1800000
    maxLifetime.....................0
    maximumPoolSize.................18
    metricRegistry..................null
    metricsTrackerFactory...........null
    minimumIdle.....................2
    password........................<masked>
    poolName........................"TEST_POOL"
    readOnly........................false
    registerMbeans..................false
    scheduledExecutorService........null
    threadFactory...................null
    transactionIsolation............null
    username........................"USER_NAME"
    validationTimeout...............5000

是错误还是配置错误?

【问题讨论】:

  • 你是否closeing 连接完成后将它们返回到池中?
  • @OldCurmudgeon 是的,大多数情况下我们只是简单地调用 connection.close()。有时我们会在调用 close 之前额外使用 evictConnection。
  • 大部分???您应该在 finally 中使用 close,以便它始终被释放到池中。没有充分理由使用 evictConnection 可能不是一个好主意 - 你为什么要这样做?
  • @OldCurmudgeon 保证调用 close(主要是不调用 evictConnection)。我们有一个系统特定的租户系统。当租户在会话上初始化时,一旦无法为另一个租户重用该会话。出于这个原因,我们需要移除该连接。

标签: java oracle jdbc datasource hikaricp


【解决方案1】:

我不是 100% 确定,但看起来像:

  • 在调用close() 后,您正在驱逐连接,这是不允许的。或者..
  • 您正在驱逐连接,然后调用 close(),这是不允许的。

当你驱逐一个连接时,你必须是那个连接的所有者(从getConnection()获得,随后你不能close()这个连接(它会自动关闭)。如上所述,如果你有已经调用了close(),连接已经回到池中,驱逐它是无效的,因为你不再是所有者。

编辑:让我更清楚。通过研究如何达到此异常,您似乎很清楚首先关闭连接,然后驱逐连接。反过来(驱逐然后关闭)不会导致这个错误。

这被称为“返回后使用”,类似于没有垃圾收集的语言中的“释放后使用”错误。当您关闭连接时,它会返回到池中。从那一刻起,另一个线程就可以使用该连接——close() 的调用者不再是所有者。

这与在 C/C++ 中的内存上调用 free() 完全相同。这样做之后,内存立即可供另一个调用者使用——free() 的调用者不再是所有者。在 C/C++ 的情况下,如果您继续使用对已释放内存的引用,您可能会损坏另一个线程的数据,该线程现在已经分配了它。

对于 Java 中的几乎所有池化库(连接或其他方式),一旦您将对象释放回池中,您就不再是所有者。没有什么可以阻止您保留对返回对象的引用。

在这种情况下,一旦您调用了close(),对象就会立即返回到池中。如果另一个线程合法地从池中获取连接(getConnection()),同时前任所有者调用evict(),你很容易遇到这个问题。

我们可以选择强化此代码路径(或不强化)。 HikariCP 在哲学上并不是特别家长式的;偏爱文档而不是代码。例如,如果您将null 传递给evict(),您将在某处遇到NPE。我们可以检查null 并忽略它吗?当然。在整个代码库中乘以这种方法,它可以轻松增长 20%。或者,开发者,不这样做怎么样?

这是一个相当简单的合同:

  • 您只能驱逐您拥有的连接。
  • 关闭连接后,您就不再拥有它。

【讨论】:

  • 你的第二点是正确的,我们总是在连接上调用关闭,即使在这种情况下它已被驱逐。你能说明这些信息的来源吗?我在文档或 javadocs 中找不到任何关于它的信息。这种行为对我来说似乎不是很明显。
  • 我的理解是,evictConnection是把连接标记为驱逐,而不是直接关闭。
  • @sebastan 您对 evictConnection 的解读也是正确的。 NPE 是一个可以在 hikaricp 中查找和修复的错误。
  • HikariDataSource.evictConnection() 将立即驱逐一个连接,而不仅仅是标记它。 HikariPool.evictConnection() 将被调用,它将调用 softEvictConnection() 并将 owner 设置为 true,它将调用 closeConnection(),而不是标记它。我不认为 N​​PE 是一个错误,但我同意 evictConnection() 的文档需要明确说明它的用法。
  • 除此之外,文档在这一点上并不明显。当达到这种非法状态时接收 NPE 也不是很有帮助。当evictConnection 已经被调用时,不应该在调用close 之后立即有一个IllegalStateException 吗?我还想补充一点,evictConnection 的调用可能位于应用程序结构的更高级别,就像close 的调用一样。这迫使程序员向下获取这些信息,这不是很舒服。
猜你喜欢
  • 2017-11-29
  • 2018-01-03
  • 2019-10-13
  • 2017-11-09
  • 2021-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多