Путаница с пулами соединений DBCP и потоками

1

Я пытаюсь заставить Multi-Threading работать в моем веб-приложении Java, и, похоже, что бы я ни старался, я сталкиваюсь с некоторыми проблемами с пулом соединений.

Мой текущий процесс заключается в том, что я прохожу через все мои отделы и обрабатываю их, что в итоге создает дисплей. Это требует времени, поэтому я хочу создать поток для каждого отдела и заставить их обрабатывать одновременно.

После долгого времени, когда вы впервые выяснили, как заставить мою сессию спящего режима оставаться открытой в потоке, чтобы предотвратить ленивые ошибки загрузки инициализации, я наконец получил решение о создании Spring-компонента моего класса Thread и создав новый экземпляр этого bean для каждого потока. Я пробовал две разные версии

1) Я непосредственно вставляю классы DAO в Bean. FAILED - после загрузки страницы несколько раз я получаю сообщение "Не удается получить соединение, ошибка пула" Тайм-аут ", ожидающий незанятого объекта" для каждого потока, и приложение будет аварийно завершено.

2) Хорошо, поэтому я попытался ввести SessionFactory в свой bean-компонент, затем создать новые экземпляры моего DAO и установить его с помощью SessionFactory. Мои объекты DAO расширяют HibernateDaoSupport. FAILED - После загрузки страницы несколько раз я получаю сообщения об ошибках "слишком много соединений".

Теперь я запутался в том, что мой компонент SessionFactory является синглом, который, как я понимаю, означает, что это единственный объект, который используется во всем контейнере Spring. Если это так, то почему это выглядит так, как будто каждый поток создает новое соединение, когда должен просто делиться этим единственным экземпляром? Похоже, что все мои пулы подключений заполняются, и я не понимаю, почему. После того, как потоки будут выполнены, все созданные соединения должны быть выпущены, но там нет. Я даже попытался выполнить операцию close() на введенном SessionFactory но это не имеет никакого эффекта. Я попытался ограничить количество потоков, выполняемых одновременно в 5, надеясь, что это приведет к тому, что не так много соединений будет создано за один раз, но не удастся.

Я, очевидно, делаю что-то неправильно, но я не уверен, что. Я принимаю неверный подход полностью, пытаясь получить Session спящего режима в моей Thread? Я как-то неправильно управляю пулом подключений? Благодарим за любую идею!

Больше информации Я думал: Мой процесс создает около 25 потоков, которые запускаются по 5 за раз. Я могу обновить свою страницу примерно 3 раза, прежде чем я начну получать ошибки. Таким образом, очевидно, что каждое обновление создает и удерживает кучу соединений.

Вот мой конфигурационный файл весны:

<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:

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();
            }
        }       
    }
  • 0
    Вам не нужно прыгать через эти обручи, чтобы заставить работать базовый DBCP. Взгляните на alvinalexander.com/blog/post/java/… Также наличие TestThread в качестве статики кажется немного странным.
  • 0
    @ScaryWombat - получить базовую работу с DBCP не проблема, она прекрасно работает ... пока я не введу параллелизм, и в этот момент мой пул соединений будет засорен. Вот что я пытаюсь выяснить
Показать ещё 2 комментария
Теги:
multithreading
spring
hibernate
apache-commons-dbcp

2 ответа

1

В принципе, вы не должны пытаться использовать один Session спящего режима между потоками. Спящие сеансы или объекты сущностей являются потокобезопасными, что может привести к некоторым неожиданным проблемам. Здесь и [здесь] 3 [] является небольшим, но приятным чтением в комментариях также есть некоторая ценность. В принципе, не пытайтесь совместно использовать Session между потоками.

Существует и другая проблема, так как все управление транзакциями основано на объектах ThreadLocal, то же самое касается получения текущего сеанса и базового соединения JDBC. Теперь попытка создания потоков приведет к проблемам с сюрпризом, одним из которых будет голод пула соединений. (Примечание: не открывайте слишком много соединений, здесь больше информации).

Рядом с тем, чтобы не открывать много соединений, вы должны знать о начале многих потоков. Тема связана с процессором (или ядром, если у вас несколько ядер). Добавление во многие потоки может привести к большому совместному использованию одного процессора/ядра между многими потоками. Это может убить вашу производительность, а не увеличивать ее.

Короче ИМХО, ваш подход ошибочен, каждый поток должен просто читать сущность, о которой он заботится, делать свою работу и совершать транзакцию. Я бы предложил использовать что-то вроде Spring Batch для этого, вместо того, чтобы изобретать собственный механизм. (Хотя, если это достаточно просто, я, вероятно, поеду за ним).

  • 0
    Когда я это читаю, он делится не Session а SessionFactory которая является правильным способом сделать что-то.
  • 0
    Это не то, что я прочитал :). Цитировать @ raymond-holguin After alot of time of first figuring out how to get my hibernate session to stay open in a thread что означает, что он пытается поделиться сеансом. Если это не так, его подход по-прежнему ошибочен, и он должен просто использовать базовую конфигурацию tx (чего можно добиться без использования собственных потоков, просто используя @Async вместе с @Transactional в вашем методе обработки).
Показать ещё 4 комментария
1

Соединение с базой данных не связано с SessionFactory а с Session. Чтобы получить правильное обращение, вы должны позаботиться о следующем:

  • Создайте только один экземпляр SessionFactory (в контексте персистентности), но вы делаете это уже
  • Не close() SessionFactory - это время жизни должно заканчиваться, когда приложение умирает - то есть при развертывании или отключении сервера.
  • Убедитесь, что вы всегда close() ваш Session - вы используете одно соединение с базой данных на каждый открытый Session и если они не закрываются, вы пропускаете соединения.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню