【问题标题】:Spring jdbcTemplate - Connection is ClosedSpring jdbcTemplate - 连接已关闭
【发布时间】:2017-02-24 05:51:31
【问题描述】:

我的 Jersey REST 应用程序存在一个长期运行的问题,该应用程序利用 Spring jdbcTemplate 对我们的数据库进行基本的 SELECTINSERTUPDATEDELETE 查询(我们使用 DB2) .

这个问题每隔几天就会发生一次,所以我没有 System.out 的错误内容(下次发生这种情况时,我会附上错误的屏幕截图)。每隔几天左右,我的 REST 服务中的一些查询就会由于“连接已关闭”而开始失败。错误。每当我收到此错误时,我只需重新启动 tomcat 应用程序服务器,问题就会在几天内得到解决,直到它再次出现。

一旦我们的最终用户开始使用它,每隔几天重新启动一次服务器将不是一个可接受的解决方案。因此,如果有人知道为什么会发生这种情况以及如何永久解决该问题,请告诉我。

这是我的 Spring 数据源配置:

package com.my.package;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    @Bean (name = "dataSource1")
    @Primary
    @ConfigurationProperties(prefix = "ds1.datasource")
    public DataSource dataSource1() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "ds1")
    public JdbcTemplate jdbcTemplate1(@Qualifier("dataSource1") DataSource dataSource1) {
        return new JdbcTemplate(dataSource1);
    }

    @Bean (name = "dataSource2")
    @ConfigurationProperties(prefix="ds2.datasource")
    public DataSource dataSource2() { return DataSourceBuilder.create().build(); }

    @Bean(name = "ds2")
    public JdbcTemplate jdbcTemplate2(@Qualifier("dataSource2") DataSource dataSource2) {
        return new JdbcTemplate(dataSource2);
    }
}

这是我的 application.properties:

ds1.datasource.url=url1
ds1.datasource.username=user1
ds1.datasource.password=pass1
ds1.datasource.driver-class-name=com.ibm.db2.jcc.DB2Driver

ds2.datasource.url=url2
ds2.datasource.username=user2
ds2.datasource.password=pass2
ds2.datasource.driver-class-name=com.ibm.db2.jcc.DB2Driver

我的 pom.xml 包含 spring jdbc 和驱动程序依赖项

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>ibm.db2</groupId>
    <artifactId>db2jcc4.jar</artifactId>
    <version>4.19.26</version>
</dependency>

最后,我使用 jdbcTemplate 执行简单的SELECT 查询的示例

@Autowired
@Qualifier("ds1")
private JdbcTemplate jdbcTemplate;

List<Something> sampleQuery(){
    String sqlQuery = "SELECT * FROM TABLE";
    try {
        return this.jdbcTemplate.query(
                sqlQuery,
                (rs, rowNum) -> {

                    Something something = new Something();
                    something.setVal1(rs.getString("FIELD1").trim());
                    something.setVal2(rs.getString("FIELD2").trim());

                    return something;
                });
    }catch (Exception ex){
        ex.printStackTrace();
        System.out.println("error...");
        return new ArrayList<>();
    }
}

编辑:错误仍在发生。这次我能够捕获日志。它基本上只是说“无法验证新建立的连接”。我不知道为什么。

org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: Failed to validate a newly established connection.
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:615)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:680)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:712)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:722)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:772)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:205)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
    at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
    at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:261)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:115)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.sql.SQLException: Failed to validate a newly established connection.
    at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:811)
    at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:626)
    at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:185)
    at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:127)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
    ... 81 more

EDIT#2: 最后将@exoddus 的答案标记为正确答案。在阅读了他的答案并查看了他提供的 Spring JDBC 文档(这使我了解了底层的 tomcat JDBC 连接池文档)之后,我开始尝试使用许多不同的属性,直到它正常工作为止。

这是我在生产环境中使用的当前配置:

ds1.datasource.url=jdbc:db2://database.domain.com:12345/DBMS
ds1.datasource.username=admin
ds1.datasource.password=admin
ds1.datasource.driver-class-name=com.ibm.db2.jcc.DB2Driver
ds1.datasource.max-active=200
ds1.datasource.max-idle=200
ds1.datasource.max-wait=20000
ds1.datasource.min-idle=50
ds1.datasource.test-while-idle=true
ds1.datasource.test-on-borrow=true
ds1.datasource.validation-query=SELECT 1 FROM SYSIBM.SYSDUMMY1
ds1.datasource.time-between-eviction-runs-millis=30000
ds1.datasource.remove-abandoned=true
ds1.datasource.remove-abandoned-timeout=30
ds1.datasource.abandon-when-percentage-full=50
ds1.datasource.initial-size=50
ds1.datasource.jdbcInterceptors=ResetAbandonedTimer

重复 ds2。自从我大约 6 个月前实施以来,此配置一直运行良好。

【问题讨论】:

  • 你使用连接池吗?
  • 我相信我知道吗?我认为spring-boot-starter-jdbc 默认使用 tomcat-jdbc 池
  • 感谢您在 6 个月后跟踪此问题,接受回复并分享您的想法!

标签: java spring jdbc spring-jdbc jdbctemplate


【解决方案1】:

根据您的上下文,我会尝试配置 DataSource bean 的一些参数。也许几天后您会失去联系,因为其中一些从未发布或完成(我只是猜测)。

尝试将这些属性添加到您的 application.properties:

ds1.datasource.max-active=50
ds1.datasource.max-idle=8
ds1.datasource.max-wait=10000
ds1.datasource.min-idle=4
ds1.datasource.test-on-borrow=true

ds2 也一样

正确的值取决于您的环境/硬件。查看here 以获得关于数据源和池的简洁明了的说明。

Here你可以在 spring 上找到另一个关于 DataSource 的有趣问题,这可能会对你有所帮助。

【讨论】:

  • 还可以查看 Spring Boot 关于连接池的部分:docs.spring.io/spring-boot/docs/current/reference/html/…
  • 谢谢,我现在将实施这些,看看是否能解决问题。因为“连接已关闭”通常需要 1-3 天。发生错误,我会等到下周五再说错误发生
  • 我更新了您提供的属性的值并添加了更多:ds1.datasource.validation-query="SELECT 1 FROM SYSIBM.SYSDUMMY1"ds1.datasource.initial-size=10。同样,由于“连接已关闭”,这有点难以验证/测试。每隔几天左右就会出现问题。我将在本周剩余时间内保持该应用程序正常运行。如果“连接已关闭”。一周结束后问题未出现,我将假设此解决方案有效并将其标记为正确答案。
  • 您也可以尝试更改正在抛出异常的包的调试级别并尝试捕获更多信息。希望它有所帮助:)
  • @exoddus 我也会考虑这样做。尽管如此,仍然会将其标记为正确答案。自从我实现这些数据源属性以来已经整整 1 周了,而且我还没有抛出一次“连接已关闭”异常。所有查询都在正常执行。但是,如果问题再次出现,我将继续监控并根据需要添加/删除/更新数据源属性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-14
  • 1970-01-01
  • 2017-02-17
  • 2011-06-02
  • 2013-12-23
  • 2011-07-03
相关资源
最近更新 更多