简体   繁体   English

与DBCP连接池和线程的混淆

[英]Confusion with DBCP connection pooling and Threads

I am trying to get Multi-Threading working in my Java web application and it seems like no matter what I try I run into some sort of issues with connection pooling. 我正在尝试使多线程在我的Java Web应用程序中正常工作,无论我如何尝试,似乎都遇到了连接池问题。

My current process is that I have am looping through all my departments and processing them, which finally generates a display. 我当前的流程是,我正在遍历所有部门并对其进行处理,最终生成了一个显示。 This takes time, so I want to spawn a thread for each department and have them process concurrently. 这需要时间,因此我想为每个部门生成一个线程并使它们并发处理。

After alot of time of first figuring out how to get my hibernate session to stay open in a thread to prevent the lazy initializion loading errors, I finally had the solution of creating a Spring bean of my Thread class, and creating a new instance of that bean for each thread. 在花了很多时间首先弄清楚如何使我的休眠会话保持在线程中打开以防止延迟初始化加载错误之后,我终于有了解决方案,创建了Thread类的Spring bean,并创建了该实例的新实例。每个线程的bean。 I have tried 2 different versions 我尝试了2个不同的版本

1) I directly inject the DAO classes into the Bean. 1)我直接将DAO类注入Bean。 FAILED - After loading the page a few times I would get "Cannot get a connection, pool error Timeout waiting for idle object" for each thread and the app would crash. 失败-几次加载页面后,我将为每个线程获取“无法获得连接,池错误超时,等待空闲对象” ,该应用程序将崩溃。

2) Ok so then I tried injecting the spring SessionFactory into my bean, then create new instances of my DAO and set it with the SessionFactory . 2)好吧,然后我尝试将spring SessionFactory注入到我的bean中,然后创建DAO的新实例并使用SessionFactory设置。 My DAO objects all extend HibernateDaoSupport . 我的DAO对象都扩展了HibernateDaoSupport FAILED - After loading the page a few times I would get "too many connections" error messages. 失败-加载页面几次后,我将收到“连接太多”错误消息。

Now what I am confused about is that my SessionFactory bean is a singleton which I understand to mean that it is a single Object that is shared throughout the Spring container. 现在让我感到困惑的是,我的SessionFactory bean是一个单例,我理解这意味着它是在整个Spring容器中共享的单个Object。 If that is true then why does it appear like each Thread is creating a new connection when it should just be sharing that single instance? 如果这是真的,那么为什么它应该看起来像每个线程在创建一个新连接时都只应该共享一个实例呢? It appears that all my connection pool is getting filled up and I don't understand why. 看来我所有的连接池都快满了,我不明白为什么。 Once the threads are done, all the connections that were created should be released but there not. 线程完成后,应该释放所有已创建的连接,但不要释放。 I even tried running a close() operation on the injected SessionFactory but that has no effect. 我什至尝试在注入的SessionFactory上运行close()操作,但这没有效果。 I tried limiting how many threads run concurrently at 5, hoping that would cause not so many connections to be created at one time but no luck. 我尝试限制在5点同时运行的线程数,希望这一次不会创建太多连接,但是没有运气。

I am obviously doing something wrong but I am not sure what. 我显然做错了,但是我不确定。 Am I taking the wrong approach entirely in trying to get my hibernate Session into my Thread s? 在试图将休眠的Session放入Thread我是否完全采用了错误的方法? Am I somehow not managing my connection pool properly? 我是否某种程度上无法正确管理我的连接池? any ideas would be greatly appreciated! 任何想法将不胜感激!

More info I thought of: My process creates about 25 threads total, which are run 5 at a time. 我想到的更多信息:我的进程总共创建大约25个线程,一次运行5个。 I am able to refresh my page about 3 times before I start getting the errors. 在开始出现错误之前,我能够刷新页面大约3次。 So apparently each refresh creates and holds on to a bunch of connections. 因此,显然每次刷新都会创建并保持一堆连接。

Here is my spring config file: 这是我的弹簧配置文件:

<bean id="mainDataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">

....

<!-- Purposely put big values in here trying to figure this issue out 
since my standard smaller values didn't help.  These big values Fail just the same -->

    <property name="maxActive"><value>500</value></property>
    <property name="maxIdle"><value>500</value></property>
    <property name="minIdle"><value>500</value></property>
    <property name="maxWait"><value>5000</value></property>
    <property name="removeAbandoned"><value>true</value></property>
    <property name="validationQuery"><value>select 0</value></property>
</bean>

<!--Failed attempt #1 -->
<bean id="threadBean" class="controller.manager.ManageApprovedFunds$TestThread" scope="prototype">
        <property name="dao"><ref bean="dao" /></property>
    </bean>

<!--Failed attempt #2 -->
<bean id="threadBean" class="manager.ManageApprovedFunds$TestThread" scope="prototype">
        <property name="sessionFactory"><ref bean="sessionFactory" /></property>
    </bean>

Java code: Java代码:

ExecutorService taskExecutor = Executors.newFixedThreadPool(5);

for(Department dept : getDepartments()) {
    TestThread t = (TestThread)springContext.getBean("threadBean");
    t.init(dept, form, getSelectedYear());
    taskExecutor.submit(t);
}

taskExecutor.shutdown();

public static class TestThread extends HibernateDaoSupport implements Runnable, ApplicationContextAware {
        private ApplicationContext appContext;

        @Override
        public void setApplicationContext(ApplicationContext arg0)
                throws BeansException {
            this.appContext = arg0;
        }

        @Override
        public void run() {
            try {
                MyDAO dao = new MyDAO();
                dao.setSessionFactory(getSessionFactory());

                //SOME READ OPERATIONS

                getSessionFactory().close();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }       
    }

Basically you shouldn't try to share a single hibernate Session between threads. 基本上,您不应该尝试在线程之间共享一个休眠Session Hibernate sessions, nor the entity objects, are threadsafe and that can lead to some suprising problems. 休眠会话或实体对象都是线程安全的,这可能会导致一些令人惊讶的问题。 Here and [here]3[] is a small but nice read there is also some value in the comments. 这里和[here] 3 []很小但很不错,注释中也有一些价值。 Basically don't try to share a Session between threads. 基本上,不要尝试在线程之间共享Session

There is also another problem as the whole transaction management is based around ThreadLocal objects, the same goes for obtaining the current session and underlying JDBC connection. 还有一个问题是,整个事务管理都基于ThreadLocal对象,获取当前会话和基础JDBC连接也是如此。 Now trying to spawn threads will lead to suprising problems, one of them would be connection pool starvation. 现在尝试生成线程将导致令人惊讶的问题,其中之一就是连接池不足。 (Note: don't open too many connections, more information here ). (注意:不要打开太多连接,更多信息请点击此处 )。

Next to not to opening to much connections you should be aware of starting to many threads. 除了不要打开太多的连接之外,您还应该知道要启动许多线程。 A Thread is bound to a cpu (or core if you have multiple cores). 线程绑定到cpu(如果有多个核心,则绑定到核心)。 Adding to many threads might lead to heavy sharing of a single cpu/core between to many threads. 添加到多个线程可能会导致多个线程之间大量共享单个cpu /内核。 This can kill your performance instead of increasing it. 这会杀死您的性能,而不是提高性能。

In short IMHO your approach is wrong, each thread should simply read the entity it cares about, do its thing and commit the transaction. 简而言之,恕我直言,您的方法是错误的,每个线程应仅读取其关心的实体,执行其操作并提交事务。 I would suggest using something like Spring Batch for this instead of inventing your own mechanism. 我建议为此使用诸如Spring Batch之类的东西,而不是发明自己的机制。 (Although if it is simple enough I would probably go for it). (尽管如果足够简单,我可能会去做)。

A database connection is not associated with the SessionFactory but with the Session . 数据库连接不与SessionFactory关联,而是与Session关联。 To get your handling correct you have to take care of the following: 为了正确处理,您必须注意以下事项:

  • Only create one instance of SessionFactory (per persistence context) - but you are doing this already 仅创建一个SessionFactory实例(根据持久性上下文)-但是您已经在执行此操作
  • Don't close() the SessionFactory - it's lifetime should end when the application dies - that is at undeployment or server-shutdown. 不要close() SessionFactory它的生命周期应该在应用程序死后结束-处于取消部署或服务器关闭状态。
  • Make sure to always close() your Session - you use one database connection per open Session and if these don't get closed you are leaking connections. 确保始终close() Session close() Session -您在每个打开的Session使用一个数据库连接,如果未关闭这些Session ,则说明正在泄漏连接。

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

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