简体   繁体   English

多线程环境中的连接池超时问题

[英]connection pooling timeout problems in multithreading environment

My team has to make some changes and renew an old web application. 我的团队必须进行一些更改并更新旧的Web应用程序。 This application has one main thread and 5 to 15 daemon threads used as workers to retrieve and insert data in a DB. 该应用程序具有一个主线程和5至15个守护程序线程,这些线程用作工作程序以在DB中检索和插入数据。

All those threads have this design (here simplified for convenience): 所有这些线程都具有这种设计(为方便起见,此处进行了简化):

public MyDaemon implements Runnable {

     // initialization and some other stuffs

     public void run() {
         ...
         while(isEnabled) {
              Engine.doTask1();
              Engine.doTask2();
              ...
              Thread.sleep(someTime);
         }
     }
}

The Engine class provides a series of static methods used to maipulate other methods of DataAccessor classes, some of those methods been static: Engine类提供了一系列静态方法,这些方法用于处理DataAccessor类的其他方法,其中一些方法是静态的:

public Engine {

    public static doTask1() {
        ThisDataAccessor.retrieve(DataType data);
        // some complicated operations
        ThisDataAccessor.insertOrUpdate(DataType data);
    }

    public static doTask2() {
        ThatDataAccessor da = new ThatDataAccessor();
        da.retrieve(DataType data);
        // etc.
    }
    ...
}

DataAccessor classes usually interact with DB using simple JDBC statements enclosed in synchronized methods (static for some classes). DataAccessor类通常使用包含在同步方法中的简单JDBC语句与DB交互(某些类是静态的)。 DataSource is configured in the server. DataSource在服务器中配置。

public ThatDataAccessor {

    public synchronized void retrieve(DataType data) {
         Connection conn = DataSource.getConnection();
         // JDBC stuff
         conn.close();
    }
    ...
}

The problem is that the main thread needs to connect to DB and when these daemon threads are working we run easily out of available connections from the pool, getting "waiting for connection timeout" exceptions. 问题是主线程需要连接到数据库,并且当这些守护程序线程正在工作时,我们很容易用尽池中的可用连接,从而出现“等待连接超时”异常。 In addition, sometimes even those daemon threads get the same exception. 此外,有时甚至那些守护程序线程也会获得相同的异常。

We have to get rid of this problem. 我们必须摆脱这个问题。

We have a connection pool configured with 20 connections, and no more can be added since that "20" is our production environment standard. 我们有一个配置有20个连接的连接池,由于“ 20”是我们的生产环境标准,因此无法添加。 Some blocks of code need to be synchronized, even if we plan to move the "synchronized" keyword only where really needed. 即使我们计划仅将“ synchronized”关键字移到真正需要的地方,也需要同步一些代码块。 But I don't think that it would make really the difference. 但是我认为这不会真正改变。

We are not experienced in multithreading programming and we've never faced this connection pooling problem before, that's why I'm asking: is the problem due to the design of those threads? 我们没有多线程编程方面的经验,并且以前从未遇到过连接池问题,这就是为什么我要问: 问题出在这些线程的设计上吗? Is there any flaw we haven't noticed? 有没有我们没有注意到的缺陷?

I have profiled thread classes one by one and as long as they are not running in parallel it seems that there's no bottleneck to justify those "waiting for connection timeout". 我已经逐一剖析了线程类,只要它们不是并行运行的,似乎没有瓶颈可以证明那些“等待连接超时”。 The app is running on WebSphere 7, using Oracle 11g. 该应用程序使用Oracle 11g在WebSphere 7上运行。

You are likely missing a finally block somewhere to return the connections back to the pool. 您可能在某个地方丢失了finally块,无法将连接返回到池中。 With hibernate, I think this is probably done when you call close() or possibly for transactions, when you call rollback(). 使用休眠,我认为这可能在您调用close()时完成,或者对于事务(当您调用rollback()时)完成。 But I would call close anyway. 但是我还是会打电话给关闭。

For example, I wrote a quick and dirty pool myself to extend an old app to make it multithreaded, and here is some of the handling code (which should be meaningless to you except the finnally block): 例如,我自己编写了一个快速且肮脏的池,以扩展一个旧应用程序以使其成为多线程,这是一些处理代码(除了finnally块,这对您来说没有任何意义):

try {
    connection = pool.getInstance();
    connection.beginTransaction();
    processFile(connection, ...);
    connection.endTransaction();
    logger_multiThreaded.info("Done processing file: " + ... );
} catch (IOException e) {
    logger_multiThreaded.severe("Failed to process file: " + ... );
    e.printStackTrace();
} finally {
    if (connection != null) {
        pool.releaseInstance(connection);
    }
}

It is fairly common for people to fail to use finally blocks properly... For example, look at this hibernate tutorial, and skip to the very bottom example. 人们无法正确使用finally块是相当普遍的。例如,看一下这个休眠教程,然后跳到最底层的例子。 You will see that in the try{} he uses tx.commit() and in the catch{} he uses tx.rollback(), but he has no session.close(), and no finally. 您会看到在try {}中他使用了tx.commit(),在catch {}中他使用了tx.rollback(),但是他没有session.close(),并且最终也没有。 So even if he added a "session.close()" in try and in catch, if his try block threw something other than a RuntimeException, or his catch caused an additional Exception before the try or a non-HibernateException before the rollback(), his connection would not be closed. 因此,即使他在try和catch中添加了“ session.close()”,如果他的try块抛出了RuntimeException以外的东西,或者他的catch在try之前引发了另一个异常,或者在rollback()之前引发了非HibernateException异常,他的联系不会被关闭。 And without session.close(), I don't think that is actually very good code. 没有session.close(),我认为这实际上不是很好的代码。 But even if the code is seemingly working, a finally gives you assurance that you are protected from this type of problem. 但是,即使代码似乎可以正常工作,最终也可以确保您免受此类问题的侵害。

So I would rewrite his methods that use Session to match the idiom shown on this hibernate documentation page. 因此,我将重写他的使用Session来匹配休眠文档页面上显示的惯用法的方法。 (and also I don't recommend his throwing a RuntimeException, but that is a different topic). (而且我也不建议他抛出RuntimeException,但这是一个不同的主题)。

So if you are using Hibernate, I think the above is good enough. 因此,如果您使用的是Hibernate,我认为以上内容就足够了。 But otherwise, you'll need to be more specific if you want specific code help, but otherwise the simple idea that you should use a finally to ensure the connection is closed is enough. 但是否则,如果需要特定的代码帮助,则需要更加具体,但是否则,您应该使用finally确保连接关闭的简单想法就足够了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM