添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

数据库应用开发过程中,我们可能会遇到一个问题:应用使用了数据库连接池,每经过指定时间后,发出到数据库服务器的任何请求都会失败,而且有且仅有一次失败,之后的正常访问都没有问题。尤其是在Web应用中,如果晚上时段没有访问,而第二天第一个访客的经历就是碰到一个数据库访问错误,如果开发系统的程序员没有注意这个问题的话,可能终端用户访问会看到抛出的一堆数据库异常信息。

其实,这个问题的主要原因是,应用中数据库连接池中会保存指定数量的数据库连接实例,而这些连接实例并没有定时地检测其到数据库服务器连接是否正常;数据库服务器可以配置一个数据库连接实例的超时时间,超过时间后它会自动断开连接。也就是,被断开的那个连接此时仍然保存在应用的数据库连接池内,下次被使用的时候就会发生数据库连接断开而导致一次访问失败。

解决上述连接关闭的方案有两种值得推荐:

  • 如果能够提供这样一种检测机制,在应用的连接池管理中定时地检测连接池中连接的有效性,就完全可以避免上面描述的问题。
  • 在应用代码中通过异常处理机制,来实现该次业务的重新处理,也可以很好地避免。

我们举一个例子,使用Java开发的Web系统,Tomcat作为HTTP服务器,MySQL作为数据库,抛出异常的信息如下所示:

[http-bio-8080-exec-10] 2012-11-28 00:55:43 [org.shirdrn.wm.de.action.StatAction]-[WARN] 
com.ibatis.dao.client.DaoException: Error ending SQL Map transaction.  Cause: java.sql.SQLException: Already closed.
	at com.ibatis.dao.engine.transaction.sqlmap.SqlMapDaoTransaction.rollback(SqlMapDaoTransaction.java:51)
	at com.ibatis.dao.engine.transaction.sqlmap.SqlMapDaoTransactionManager.rollbackTransaction(SqlMapDaoTransactionManager.java:85)
	at com.ibatis.dao.engine.impl.DaoContext.endTransaction(DaoContext.java:112)
	at com.ibatis.dao.engine.impl.DaoProxy.invoke(DaoProxy.java:77)
	at $Proxy8.selectByExample(Unknown Source)
	at org.shirdrn.wm.de.service.impl.StatItemsServiceImpl.countItems(Unknown Source)
	at org.shirdrn.wm.de.action.StatAction.makeStat(Unknown Source)
	at org.shirdrn.wm.de.action.StatAction.doGet(Unknown Source)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:987)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:579)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:307)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:619)
Caused by: java.sql.SQLException: Already closed.
	at org.apache.commons.dbcp.PoolableConnection.close(PoolableConnection.java:84)
	at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.close(PoolingDataSource.java:181)
	at com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransaction.close(JdbcTransaction.java:81)
	at com.ibatis.sqlmap.engine.transaction.TransactionManager.end(TransactionManager.java:93)
	at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.endTransaction(SqlMapExecutorDelegate.java:734)
	at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.endTransaction(SqlMapSessionImpl.java:176)
	at com.ibatis.sqlmap.engine.impl.SqlMapClientImpl.endTransaction(SqlMapClientImpl.java:153)
	at com.ibatis.dao.engine.transaction.sqlmap.SqlMapDaoTransaction.rollback(SqlMapDaoTransaction.java:49)
	... 25 more

我们通过上面给出的第二种方案来解决,对应异常中实现的代码,进行异常处理的逻辑如下所示:

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		boolean retry = false;
		String type = request.getParameter(RequestParams.ITEM_TYPE);
		String top = request.getParameter(RequestParams.TOP_N);
		Byte itemType = Byte.parseByte(type);
		Integer topN = super.topN;
		if(top!=null) {
			try {
				topN = Integer.parseInt(top);
			} catch (Exception e) {}
		Target target = itemNames.get(itemType);
		try {
			makeStat(request, response, itemType, topN, target);
		} catch (Exception e) {
			LOG.warn("", e);
			// com.ibatis.dao.client.DaoException: Error ending SQL Map transaction.  Cause: java.sql.SQLException: Already closed.
			if(!retry && e instanceof DaoException) {
				LOG.warn("Try to obtain database connection again.");
				retry = true;
				this.makeStat(request, response, itemType, topN, target);
			} else {
				response.sendError(500, e.toString());
				return;
		request.getRequestDispatcher(target.url).forward(request, response);
	private void makeStat(HttpServletRequest request, HttpServletResponse response, 
			Byte itemType, Integer topN, Target target) throws IOException, ServletException {
		List<StatItems> items = statItemsService.countItems(itemType, new Date(), topN);
		for (StatItems statK : items) {
			if(statK.getItemName()!=null && !"null".equalsIgnoreCase(statK.getItemName())) {
				pieDataset.setValue(statK.getItemName().trim() + " (" + statK.getPercentage() + ")", statK.getItemValue());
		String imageUrl = super.generateImage(pieDataset, target.title, request);
		request.setAttribute("items", items);
		request.setAttribute("imageUrl", imageUrl);
		if(items!=null && !items.isEmpty() && items.size()<topN) {
			topN = items.size();
		request.setAttribute("topN", topN);

上面代码,判断如果是发生连接失败,则保存请求参数,再重新处理该请求。

另一种不推荐的方案,就是修改数据库服务器的连接超时配置。因为在实际项目中,通常应用上线的相关人员未必是DBA,对于修改数据库服务器的配置可能会给其它上线业务带来风险。解决方法如下:

以MySQL为例,查看文件/etc/my.cnf,查询有关超时配置的参数:

mysql> show variables like '%timeout';
+----------------------------+----------+
| Variable_name              | Value    |
+----------------------------+----------+
| connect_timeout            | 10       |
| delayed_insert_timeout     | 300      |
| innodb_lock_wait_timeout   | 50       |
| innodb_rollback_on_timeout | OFF      |
| interactive_timeout        | 28800    |
| lock_wait_timeout          | 31536000 |
| net_read_timeout           | 30       |
| net_write_timeout          | 60       |
| slave_net_timeout          | 3600     |
| wait_timeout               | 28800    |
+----------------------------+----------+

我们可以在属性组mysqld下面修改如下两个参数:

  • interactive_timeout
  • wait_timeout

MySQL数据库服务器配置的连接超时时间默认是8小时,如果修改的超时时间足够长的话,就不会出现前面发生的连接断开的问题。但是,如果有很多应用都在使用数据库连接池,大量的数据库连接资源一直被占用,严重的话可能使数据库服务器宕机,而且,也会使一些攻击者伪造大量请求,使数据库服务器负荷过载而宕机,从而影响应用处理业务。

数据库应用开发过程中,我们可能会遇到一个问题:应用使用了数据库连接池,每经过指定时间后,发出到数据库服务器的任何请求都会失败,而且有且仅有一次失败,之后的正常访问都没有问题。尤其是在Web应用中,如果晚上时段没有访问,而第二天第一个访客的经历就是碰到一个数据库访问错误,如果开发系统的程序员没有注意这个问题的话,可能终端用户访问会看到抛出的一堆数据库异常信息。其实,这个问题的主要原因是,应用中数
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:撤销:Ctrl/Command + Z 重做:Ctrl/Command + Y 加粗:Ctrl/Command + B 斜体:Ctrl/Command + I 标题:Ctrl/Command + S
1.延长maxLifetime大小 注:maxLifetime设置一个connection在连接的存活时间 默认30分钟 ,如果设置为0,表示存活时间无限大。如果不等于0且小于30秒则会被重置回30分钟。 2.更改配置allowPoolSuspension: true registerMbeans: true 效果当池被暂停时,调用 getConnection()将不会... mysql 8小时断开连接问题:mysql的默认设置下,当一个连接的空闲时间超过8小时后,mysql 就会断开连接,而连接池认为连接依然有效。在这种情况下,如果客户端代码向连接池请求连接的话,连接池就会把已经失效的连接返回给客户端,客户端在使用该失效连接的时候即抛出异常。 解决方案: 可以通过数据库连接池的配置来解决此问题。下面通过例子进行说明为了便于模拟,首先将数据库的最大超...
* 我们在平时开发的时候可能会遇到超时连接的场景:就是调用某个方法的时候,如何该方法在指定的时间里得到了相应的结果,那么将结果返回,如未得到结果,直接返回默认值。现在我们来模拟一下连接数据库连接池可能遇到的超时连接* 首先我们自定义一个数据库连接类 public class ConnectionDriver { static class ConnectionHandler implement
Hadoop格式化HDFS报错java.net.UnknownHostException: localhost.localdomain: localhost.localdomain 67390 Hadoop格式化HDFS报错java.net.UnknownHostException: localhost.localdomain: localhost.localdomain 最近在自学Hadoop,通过这片文章,解决了hadoop启动的问题。 Activiti 5.3:子流程(subProcess) 闪电小飞龙: 大神,第二种方式怎么在流程图上体现,是我在主流程上要新增一个call activiti的 任务点吗?还有通过这个 怎么调用子流程? 可以动态调用吗?