如何在Java中修复'SQLRecoverableException:Closed Connection'

时间:2019-04-02 13:36:49

标签: java oracle tomcat hybris

我们正在为使用Hybris框架构建的电子商务而努力,目前我们在数据库连接方面存在问题(我想),并且不知道如何解决。它仅在生产环境上并且仅在ESB使用的服务器上发生(总共2台服务器,共40台)。

基本上,有时(1-3 /天),我们发现会话正在等待某些空闲会话(来自客户端的SEL * NET消息)。我们只能手动杀死持有者以释放这些会话。

所有服务器共享相同的应用程序代码,ESB和Frontend服务器之间的主要区别在于调用的控制器和请求计数。

ESB服务器:每分钟10个请求 前端服务器:每分钟300个请求

在应用程序日志中,我在这2台服务器上发现了很多“关闭连接”错误,我认为这与我们的问题有关,但实际上我不知道为什么。 在access.log中,我有此请求:

[26/Mar/2019:09:04:39 +0100] "GET /blockorder?orderCode=XXXX&access_token=XXXX HTTP/1.1" 400 122 "-" "AHC/1.0"

在console.log中,我有这个:

hybrisHTTP8 2019-03-26 09:04:39,184 ERROR [[10.125.31.2] ] () [de.hybris.platform.jdbcwrapper.ConnectionImpl] error resetting AutoCommit
java.sql.SQLRecoverableException: Closed Connection
        at oracle.jdbc.driver.PhysicalConnection.setAutoCommit(PhysicalConnection.java:3763)
        at de.hybris.platform.jdbcwrapper.ConnectionImpl.doSetAutoCommit(ConnectionImpl.java:431)
        at de.hybris.platform.jdbcwrapper.ConnectionImpl.restoreAutoCommit(ConnectionImpl.java:185)
        at de.hybris.platform.jdbcwrapper.ConnectionImpl.unsetTxBound(ConnectionImpl.java:175)
        at de.hybris.platform.tx.Transaction.unsetTxBoundConnection(Transaction.java:920)
        at de.hybris.platform.tx.Transaction.clearTxBoundConnectionAndNotify(Transaction.java:897)
        at de.hybris.platform.tx.Transaction.clearTxBoundConnectionAndNotifyRollback(Transaction.java:887)
        at de.hybris.platform.tx.Transaction.rollbackOuter(Transaction.java:1084)
        at de.hybris.platform.tx.Transaction.rollback(Transaction.java:1028)
        at de.hybris.platform.tx.Transaction.commit(Transaction.java:690)
        at de.hybris.platform.tx.Transaction.finishExecute(Transaction.java:1218)
        at de.hybris.platform.tx.Transaction.execute(Transaction.java:1205)
        at de.hybris.platform.tx.Transaction.execute(Transaction.java:1160)
        at de.hybris.platform.jalo.Item.setAllAttributes(Item.java:2082)
        at de.hybris.platform.jalo.Item.setAllAttributes(Item.java:2057)
        at de.hybris.platform.servicelayer.internal.converter.impl.ItemModelConverter.storeAttributes(ItemModelConverter.java:1503)
        at de.hybris.platform.servicelayer.internal.converter.impl.ItemModelConverter.save(ItemModelConverter.java:730)
        at de.hybris.platform.servicelayer.internal.model.impl.wrapper.ModelWrapper.save(ModelWrapper.java:336)
        at de.hybris.platform.servicelayer.internal.model.impl.ResolvingModelPersister.saveOthers(ResolvingModelPersister.java:64)
        at de.hybris.platform.servicelayer.internal.model.impl.ResolvingModelPersister.persist(ResolvingModelPersister.java:49)
        at de.hybris.platform.servicelayer.internal.model.impl.DefaultModelService.saveViaJalo(DefaultModelService.java:1059)
        at de.hybris.platform.servicelayer.internal.model.impl.DefaultModelService.doJaloPersistence(DefaultModelService.java:648)
        at de.hybris.platform.servicelayer.internal.model.impl.DefaultModelService.persistWrappers(DefaultModelService.java:1002)
        at de.hybris.platform.servicelayer.internal.model.impl.DefaultModelService.performPersistenceOperations(DefaultModelService.java:626)
        at de.hybris.platform.servicelayer.internal.model.impl.DefaultModelService.saveAllInternal(DefaultModelService.java:620)
        at de.hybris.platform.servicelayer.internal.model.impl.DefaultModelService.saveAll(DefaultModelService.java:600)
        at de.hybris.platform.servicelayer.internal.model.impl.DefaultModelService.save(DefaultModelService.java:548)
        at com.test.fulfilment.process.impl.DefaultOrderProcessService.requestForcedOrderCancellation(DefaultOrderProcessService.java:131)
        at com.test.application.order.facades.impl.DefaultOrderFacade.forcedOrderCancel(DefaultOrderFacade.java:62)
        at com.test.application.controllers.OrderController.blockOrder(OrderController.java:520)

我们的池配置如下:

{
    "maxIdle": 90,
    "minIdle": 2,
    "maxActive": 90,
    "maxWait": 10000,
    "whenExhaustedAction": 1,
    "testOnBorrow": true,
    "testOnReturn": true,
    "testWhileIdle": true,
    "timeBetweenEvictionRunsMillis": 10000,
    "numTestsPerEvictionRun": 100,
    "minEvictableIdleTimeMillis": 300000,
    "softMinEvictableIdleTimeMillis": -1,
    "lifo": true
}

我们的tomcat配置为:

tomcat.generaloptions.JDBC=-Doracle.jdbc.ReadTimeout=60000
tomcat.generaloptions.TIMEOUT=-Dsun.net.client.defaultConnectTimeout\=60000 -Dsun.net.client.defaultReadTimeout\=60000
tomcat.ajp.acceptCount=100
tomcat.ajp.maxThreads=400
tomcat.maxthreads=400
tomcat.minsparethreads=50
tomcat.maxidletime=10000
tomcat.connectiontimeout=120000
tomcat.acceptcount=100

我们试图删除oracle.jdbc.ReadTimeout,但结果是我们开始在其他服务器上看到“关闭的连接”。

触发此错误的代码非常简单(并且可以在95%的时间内工作):

    @Override
    public boolean requestForcedOrderCancellation(final OrderModel order) {
        Transaction.current().begin();
        try {
            modelService.lock(order.getPk());
            modelService.refresh(order);
            order.setForcedCancelled(true);
            modelService.save(order);
            Transaction.current().commit();
            return true;
        catch (Exception e) {
            LOG.error(e.getMessage(), e);
            Transaction.current().rollback();
            return false;
        }
    }

我们也尝试了没有显式锁定的问题,这个问题是完全相同的。 似乎连接已经关闭,我们无法回滚(或提交)仍在数据库中等待的事务。

我希望避免这种锁定以及这些关闭的连接错误。

2 个答案:

答案 0 :(得分:0)

您的连接池可能已经为您修复了此问题。尝试增加日志记录,看看是否可以。

背景:数据库讨厌长期存在的连接,因为它会使它们饿死。因此,他们倾向于在一段时间后关闭连接。另一个罪魁祸首是倾向于从其表中删除空闲连接的防火墙。连接池知道如何通过测试连接(上面配置中的所有DbContext选项)来处理此问题。

有时候,您需要告诉您的池如何测试连接。检查文档。对于Oracle,test*是一个很好的测试。

我认为您真正的问题是那些停滞的会议。通过查看Java线程转储来找出他们正在等待什么,您可以使用Java SDK附带的工具select 1 from dual来创建它们。

答案 1 :(得分:0)

我们发现问题是由于事务代码中未捕获的异常/错误引起的。

服务器回答有错误,并且Hybris没有回滚仍处于打开状态的事务。 稍后(也许是几天)重复使用同一线程,并且旧事务仍处于打开状态。

当此损坏的线程用于锁定数据库中的某些行时,即使我们在代码中提交事务,也不会提交给数据库,因为Hybris在内部具有事务计数器来处理内部事务(可能在调用方法中使用) )。仅当我们使用提交/回滚方法且事务计数器为1时,事务才被提交/回滚到数据库。

Request1:
    Transaction.begin() // Hybris Counter = 1
    doSomething() // This throws Exception, Application Exit, Hybris Counter is still 1
    try {
        Transaction.commit()
    } catch (Exception e) {
        Transaction.rollback();
    }

Request2 on same thread:
    Transaction.begin() // Hybris Counter now is 2
    doSomething() // Works OK, Hybris Counter is still 2
    try {       
        Transaction.commit() // HybrisCounter -= 1
        // Transaction is not commited to DB because Hybris counter now is 1
    } catch (Exception e) {
        Transaction.rollback();
    }

Request3 on same thread:
    Transaction.begin() // Hybris Counter now is 2
    lockRow() 
    // Row is locked for the whole transaction (the same opened in R1)
    // Everything is OK
    try {       
        Transaction.commit() // HybrisCounter -= 1
        // Transaction is not commited to DB because Hybris counter now is 1
        // Row is still locked
        // Next requests to the same row will wait lock forever
    } catch (Exception e) {
        Transaction.rollback();
    }