`
jackle_liu
  • 浏览: 148307 次
文章分类
社区版块
存档分类
最新评论

转载--grails的dbcp连接池链接失效问题的解决方案

 
阅读更多

Grails, DBCP & Stale Connections

July 13, 2009


Does your application break after every few hours of inactivity even though you have enough database connections in the pool? It is a common problem with database connection pooling (and idle sockets connections in general).

I have been running a few Grails app with PostgreSQL database using Apache Commons DBCP connection pooling. Most of these apps are pretty busy, and are working quite well so far. But I have one critical app that doesn’t get used as much. Recently after a bug report, I was watching through the logs and I realized that this app was complaining about Socket Connection Exception after every hour of idle time. Try it again, and it would work. So why was it rejecting the DB connection the first time? Creepy!

I checked out the other apps I have, and all of them were suffering from the same problem - depending on how idle they were. I couldn’t ignore any longer.

I started off with the basics.

Basic configuration:

I have my datasource defined in the DataSource.groovy file under grails-app/conf. I have enabled connection pooling and I am using the appropriate PostgreSQL-JDBC driver. Grails comes with Apache DBCP connection pooling by default. So it just works.

01. environments {
02. production {
03. dataSource {
04. pooled = true
05. driverClassName = "org.postgresql.Driver"
06. dbCreate = "update"
07. url = "jdbc:postgresql://sacharya.com:5432/sacharya_prod_db"
08. username = "myuser"
09. password = "mypassword"
10. logSql=true
11. }
12. }
13. }

This is what my production configuration looks like - running whatever the default behavior that Grails comes with. I printed the datasource object to see what DBCP configurations my app is using by default.

1. dataSource.properties. each { println it }
minIdle=0
initialSize=0
maxIdle=8
numIdle=0
maxActive=8
numActive=0

As the result shows, it is using the default values as defined in Apache DBCP

Problem:

Using netstat, I started watching if the application has any connections to the database open:

$ netstat -na | grep 5432
tcp4       0      0  24.25.26.27.50095     98.129.169.246.5432     ESTABLISHED

So DBCP says it has no connections open (active or idle) to the database yet in the pool. But netstat shows there is a TCP connection established to the database. Where does this connection come from - loading & initializing the Driver?

Now lets use the application for a while. Once you start asking the pool for connections, existing connections idle in the pool are at your service, or if there no connections in the pool, new connections are created and given to you, which get returned back to the pool after you complete the query. So at any given time, there will be minIdle to maxIdle ( 0 to 8 in our case) connections in the pool.

$ netstat -na | grep 5432
tcp4       0      0  24.25.26.27.51308     98.129.169.246.5432     ESTABLISHED
tcp4       0      0  24.25.26.27.50095     98.129.169.246.5432     ESTABLISHED

Now with some connections in the pool, I left the app idle for an hour and then I tried to access the app (netstat still shows 2 TCP connection). The first query got the following exception:

01. org.springframework.dao.DataAccessResourceFailureException: could not execute query;
02. nested exception is org.hibernate.exception.JDBCConnectionException: could not execute query
03.  
04. Caused by: org.hibernate.exception.JDBCConnectionException: could not execute query
05. at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java: 97 )
06.  
07. Caused by: org.postgresql.util.PSQLException: An I/O error occured while sending to the backend.
08. at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java: 217 )
09.  
10. Caused by: org.hibernate.exception.JDBCConnectionException: could not execute query
11. at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java: 97 )
12.  
13. Caused by: org.postgresql.util.PSQLException: An I/O error occured while sending to the backend.
14. at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java: 217 )
15.  
16. Caused by: java.net.SocketException: Broken pipe
17. at java.net.SocketOutputStream.socketWrite0(Native Method)

The subsequent query succeeds. There was one connection (ignore the other TCP connection used during Driver initialization) in the pool, but the database responded by saying that the connection was not valid.

Reasoning:

At this point it was clear that the TCP connection that was sitting idle was already broken, but our app still assumed it to be open. By idle connections, I mean connections in the pool that aren’t in active use at the moment by the application. After some search, I came to the conclusion that the network firewall between my app and the database is dropping the idle/stale connections after 1 hour. It seemed to be a common problem that many people have faced.

By default, DBCP holds the pooled connections open for infinite time. But a database connection is essentially a socket connection, and it doesn’t come for free. The host OS, database host, and firewall have to allocate a certain amount of memory and other resources for each socket connection. It makes sense to those devices not to hold onto idle connections for ever. So the idea is to make sure that you don’t have stale connections in your pool that would otherwise be silently dropped by OS or firewall. The system has no way of knowing if the connection is broken unless is sends a packet and waits for an acknowledgement. So even when the connection is timed out or closed by one side, the other side may still think the connection is open.

While there may not be a firewall between your server and database, even the OS has a timeout on TCP connections. You could probably increase the TCP keepalive of the OS itself, but that will affect the whole system, and yet you are only postponing the problem.

Solution:
Now lets try to modify some of the DBCP settings for the dataSource.

1. Validating Connections : DBCP allows you do define a validation query and do a sanity check of the connection before you actually use it in your application.
By default,

validationQuery=null
testOnBorrow=false
testOnReturn=false
testWhileIdle=false

The validation query must return at least one row, and using the query you can have DBCP test the connection for you while its idle, before you borrow and before you return it.
So lets change it to:

validationQuery="SELECT 1"
testOnBorrow=true
testOnReturn=true
testWhileIdle=true

If any of the connection object fails to validate, it will be dropped from the pool. There might be some performance implications of running these three SQLs (which I am not worried at the momet), and hence you might just want to try testOnBorrow.

2. Evicting Idle Connections : DBCP can run an idle object evictor at a regular interval and evict any connections that are older than some threshold. By default this behavior is turned off since timeBetweenEvictionRunsMillis is set to -1.

timeBetweenEvictionRunsMillis=-1
numTestsPerEvictionRun=3
minEvictableIdleTimeMillis=1000 * 60 * 30

Now lets run the evictor every 30 minutes and evict any connections older than 30 minutes.

timeBetweenEvictionRunsMillis=1000 * 60 * 30
numTestsPerEvictionRun=3
minEvictableIdleTimeMillis=1000 * 60 * 30

It turns out that you cannot change the DBCP settings from the DataSource.groovy file. The datasource object injected in the DataSource.groovy file is an instance of the javax.sql.DataSource. I can however do so by overriding the default DataSource from the BootStrap.groovy, which sets up the settings during start up.

01. import org.codehaus.groovy.grails.commons.ApplicationAttributes
02. class BootStrap {
03.  
04. def init = { servletContext ->
05.  
06. def ctx=servletContext.getAttribute(
07. ApplicationAttributes.APPLICATION_CONTEXT)
08. def dataSource = ctx.dataSource
09.  
10. dataSource.setMinEvictableIdleTimeMillis( 1000 * 60 * 30 )
11. dataSource.setTimeBetweenEvictionRunsMillis( 1000 * 60 * 30 )
12. dataSource.setNumTestsPerEvictionRun( 3 )
13.  
14. dataSource.setTestOnBorrow(true)
15. dataSource.setTestWhileIdle(false)
16. dataSource.setTestOnReturn(false)
17. dataSource.setValidationQuery( "SELECT 1" )
18.  
19. dataSource.properties. each { println it }
20. }
21. }

You can do the same from grails-app/conf/spring/Resource.groovy:

01. import org.apache.commons.dbcp.BasicDataSource
02. beans = {
03. dataSource(BasicDataSource) {
04. minEvictableIdleTimeMillis= 1800000
05. timeBetweenEvictionRunsMillis= 1800000
06. numTestsPerEvictionRun= 3
07.  
08. testOnBorrow=true
09. testWhileIdle=true
10. testOnReturn=true
11. validationQuery= "SELECT 1"
12. }
13. }

This seems to have solved the problem for me. Since my firewall was dropping the socket connections at 60 minutes, all I did was proactively run the idle object evictor every half 30 minutes, flush connections that are idle for more than 30 minutes and regenerate new connections in the pool. I also did sanity check over the connections in the pool.

分享到:
评论
1 楼 jackle_liu 2012-06-25  
转载---对于grails版本在1.3以上的解决方案
Grails turns database connection pooling on by default, and uses the Apache Commons DBCP library to do the pooling.

With many of the popular JDBC drivers, this can cause a failure when the application is accessed after a long idle time, commonly when a business application sits unused overnight, and then is accessed first-thing in the morning. The first user in the morning gets an HTTP 500 error, and then for subsequent users, everything is ok.

There are several ways to solve this, including changing to a different connection pool, but Sudarshan Acharya has a blog post from 2009 that explains a fairly easy way to configure DBCP to resolve this issue. Of course, there’s a “gotcha” here, or I wouldn’t be blogging about it:

As Tom Eastman points out, if you use Sudarshan’s example with Grails 1.3.x, you get an exception something like:

ERROR context.GrailsContextLoader – Error executing bootstraps: groovy.lang.MissingMethodException: No signature of method: org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy.setMinEvictableIdleTimeMillis() is applicable for argument types: (java.lang.Integer) values: [900000]
org.codehaus.groovy.runtime.InvokerInvocationException: groovy.lang.MissingMethodException: No signature of method: org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy.setMinEvictableIdleTimeMillis() is applicable for argument types: (java.lang.Integer) values: [900000]

As Graeme Rocher helpfully points out, this is because the properties of org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy changed with Grails 1.3.0, and you must now set the properties on ‘dataSourceUnproxied’ instead of setting them on ‘dataSource’. With this change, everything works as expected.

So, here’s an example of the new configuration of BootStrap.groovy, with Graeme’s suggested change:
view source
print?
01.def ctx = servletContext.getAttribute(
02.ApplicationAttributes.APPLICATION_CONTEXT
03.)
04.def dataSource = ctx.dataSourceUnproxied
05.
06.println "configuring database connection pool"
07.
08.dataSource.setMinEvictableIdleTimeMillis(1000 * 60 * 30)
09.dataSource.setTimeBetweenEvictionRunsMillis(1000 * 60 * 30)
10.dataSource.setNumTestsPerEvictionRun(3)
11.dataSource.setTestOnBorrow(true)
12.dataSource.setTestWhileIdle(false)
13.dataSource.setTestOnReturn(false)
14.dataSource.setValidationQuery("SELECT 1")
15.
16.dataSource.properties.each { println it }

相关推荐

    Getting-Started-with-Grails-Chinese

    ### Grails 入门经典知识点解析 #### 一、Grails 概述 - **Grails** 是一种基于 **Groovy** 的应用框架,专为简化企业级 Java 应用程序的开发而设计。它利用了 Groovy 语言的强大功能,并且与 Java 虚拟机无缝集成...

    Getting-Started-with-Grails-Chinese.rar_Getting Started_grails

    《Getting Started with Grails》是一本专为初学者设计的指南,旨在介绍使用Groovy语言构建的敏捷Web开发框架——Grails。Grails是基于Java平台的,它利用了Groovy的简洁性和灵活性,为开发者提供了高效且强大的工具...

    grails-petclinic, Grails的介绍性示例应用程序.zip

    grails-petclinic, Grails的介绍性示例应用程序 Petclinic示例应用程序这是Grails标准的介绍性示例应用程序。 要开始使用它,只需克隆存储库,然后从本地副本运行中进行操作: ./gradlew run 在unix系统上,或者 ...

    grails-boot:Grails 与 Spring Boot 的集成

    《Grails与Spring Boot的深度集成解析》 在现代Java开发领域,Grails和Spring Boot都是非常流行的框架。Grails作为Groovy语言构建的Web开发框架,以其强大的生产力和灵活的特性受到开发者的喜爱,而Spring Boot则...

    restful-api, Grails插件,方便暴露非平凡的RESTful api.zip

    restful-api, Grails插件,方便暴露非平凡的RESTful api #RESTful API插件文档尽管随后的更改可能不是向后兼容的,但 ##Status的生产质量也是如此。##Overview在附带的"介绍了rest式API插件插件。"演示文稿中提供了...

    wsdl2java源码-grails-cxf-grails-3:grails-cxf-grails-3

    wsdl2java源码 请注意,当前的 3.x 版本使用 cxf 3.0.4 并且需要 WSS4J ...grails-app\endpoints** 和 grails-app\services** 目录中自动装配配置的类 端点创建脚本 create-endpoint 和 create-endpoin

    grails-pjax:Grails Pjax插件云可帮助您在Grails中使用pjax

    Grails Pjax插件Pjax在Grails中。 Pjax类似于(turbolinks)[ ,在'A'标签,按钮和表单上工作。 参见_pjaxHeader grails templtate文件中的演示代码。 ## Pjax是什么? Pjax是一个jQuery插件,它使用ajax和pushState...

    grails-2.5.6

    Grails是一套用于快速Web应用开发的开源框架,它基于Groovy编程语言,并构建于Spring、Hibernate等开源框架之上,是一个高生产力一站式框架。 Grails这个独特的框架被视为是提升工程师生产效率的动态工具,因为其...

    curso-groovy-grails-2013

    【标题】:“curso-groovy-grails-2013” 暗示这是一个关于Groovy和Grails的在线课程,可能是在2013年创建或更新的。Groovy是一种面向对象的、动态的编程语言,常用于Java平台,而Grails是一个基于Groovy的开源Web...

    grails-fck-editor-0.3.zip_grails_grails-fck-editor

    标题中的"grails-fck-editor-0.3.zip_grails_grails-fck-editor"表明这是一个与Grails框架相关的插件,具体来说是FCKeditor的一个版本。FCKeditor是一款广泛使用的开源富文本编辑器,它允许用户在网页上创建和编辑...

    grails-batch-rest:Grails Spring Batch、CORS、REST API、WebSockets、Spring Security CoreLDAP 演示

    Spring Batch PoC Grails Spring Batch、启用 CORS 的 REST API、Spring Security Core/LDAP 演示先决条件 # GVM - Tool for managing Groovy related frameworks curl -s get.gvmtool.net | bash # install Grails ...

    grails-core:Grails Web应用程序框架

    设置GRAILS_HOME环境变量以指向提取的下载文件的根目录,并将GRAILS_HOME/bin添加到您的可执行文件PATH 。 然后在外壳中,键入以下内容: grails create-app sampleapp cd sampleapp grails run-app 要构建Grails...

    grails 中文第二版

    ### Grails框架中文第二版知识点概述 #### 一、简介 **Grails 2.0新特性** 1. **面向开发的特性** - 改进了开发者体验,例如通过简化配置来提高开发效率。 - 引入了新的工具和功能,如Groovy模板引擎Thymeleaf...

    grails 1.0.4

    Grails专为下一代JavaWeb应用程序而设计的框架,其借助于Groovy动态语言,使Web开发变得简单而方便。Grails尽量为更多现有的Java项目创建一个全面的框架(不仅局限于视图处理),这和当前一些Java框架提供给用户的一...

    grails-shiro-ui:Grails Shiro UI 插件

    Grails Shiro UI 插件是为基于Groovy的Grails框架开发的一款强大而易用的安全管理插件,它集成了Apache Shiro安全框架,为开发者提供了一整套用户认证和授权的解决方案。Apache Shiro是一款轻量级、全面的Java安全...

    groovy-grails-tool-suite-3.6.4.RELEASE-e4.4.2-win32-x86_64.part1

    groovy-grails-tool-suite-3.6.4.RELEASE-e4.4.2-win32-x86_64.part1 共两个压缩包,解压后将扩展名.zip.bak改为.zip再次解压。

    java8-temporal-grails-plugin:Grails插件,可与Grails一起使用Java8新的Date API(Instant,LocalDate等)

    Java8 Temporal Grails插件 Java 8时态插件在Grails中集成了Java 8的新DateTime API(Instant,LocalDate,LocalTime等)。 该插件与Grails 2.5.x(支持JDK8的Grails的第一个版本)兼容。 提供将输入绑定到Java 8 ...

Global site tag (gtag.js) - Google Analytics