Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > Пул коннектов


Автор: koroplysov 8.4.2011, 08:22
Описание проблемы:
Для написания приложения использую NetBeans+Tomcat+DB2, версия языка 1.4. На боевом сервере WebSphere(x86)+DB2. Коннект происходит через мост jdbc-odbc.
Далее в исторической последовательности. Все построено на сервлетах. Приложение использует небольшое количество людей(около 50 чел), но они активно вносят данные. Эти данные записываются в базу. В базе все нормализовано и поэтому достаточно много связей. Как следствие при внесения записи в одну таблицу то заносятся и вдругие(в среднем 1 к 5). В день около 500 а всего в итоге в среднем 2500(500*5) записей вносится. Ночью делается бекап базы.  
Вначале делал просто: на каждый insert, update,delete и select открывался отдельный коннект и после использования закрывал. Но через некоторое время(в зависимости от активности, бывало и каждый час) на боевом сервере WebSphere зависала и приходилось перезапускать, хотя сервер(железка) очень быстрый(16 ядер 8Гб).
Второй вариант хранить один глобальный коннект который создавался и запускался(в ServletContextListener) так:
Код

        public static Connection con = null;
        public static void OpenConnectionBD()
        {
            if (con == null)
            {
                try {
                    try {
                        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                    } catch (ClassNotFoundException eс) {
                        return ;
                    }
                    con= DriverManager.getConnection("jdbc:odbc:база","логин","пароль");
                } catch (SQLException e1) {
                    return ;
                }
            }else{
                try {
                    if (con.isClosed()) {
                        try {
                            try {
                                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                            } catch (ClassNotFoundException eс) {
                                return;
                            }
                        con= DriverManager.getConnection("jdbc:odbc:база","логин","пароль");
                        } catch (SQLException e1) {
                            return;
                        }
                    }
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
        }
        public static void CloseConnectionDB()
        {
            try {
                if (con!= null && !con.isClosed()) {
                    try {
                        con.close();
                    } catch (SQLException e3) {
                    }
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }

Это работает замечально, но каждое утро коннект теряется и не восстанавливается. Подозреваю что после ночного резервирования(при этом коннекты отрубаются). Прочитал про пул соединений. Скачал commons-dbcp. Толком поэтапного объяснения не нашел, но нашел одну статейку откуда целиком все и содрал:
Код

import java.io.*;
 import java.sql.*;
 import java.util.*;

import org.apache.commons.dbcp.*;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.*;


public class ConnectionPool {
    protected GenericObjectPool connectionPool=null;

   public void initConnectionPool( String driver, String dbURL, String dbUser, String dbPswd )
   {
     try
     {
       Class.forName( driver ).newInstance();//создаем новый объект класса драйвера,
       //тем самым инициализируя его

       connectionPool = new GenericObjectPool( null );//создаем GenericObjectPool
       //создаем connection factory ("фабрика соединений" - объект который будет создавать соединения)
       ConnectionFactory connectionFactory = new DriverManagerConnectionFactory( dbURL, dbUser, dbPswd );

       //создаем PoolableConnectionFactory
       PoolableConnectionFactory pcf=new PoolableConnectionFactory( connectionFactory, connectionPool, null, "SELECT 1", false, true );

       PoolingDataSource pds=new PoolingDataSource( connectionPool);
       connectionPool.setMaxIdle( 20 );//устанавливаем максимальное кол-во простаивающих соединений
       connectionPool.setMaxActive( 200 );//устанавилваем макс. кол-во активных соединений
     }
     catch( Exception ex )
     {
        ex.printStackTrace();
     }
   }
   public final Connection getConnection()
   {
     try
     {
       if( connectionPool.getMaxActive() <= connectionPool.getNumActive() )
       {
          //System.err.println( "Connections limit is over!!!" );
           int y=0;
       }

       Connection con = (Connection)connectionPool.borrowObject();
       return con;
     }
     catch( Exception ex )
     {
       ex.printStackTrace();
       return null;
     }
   }

   /**
    * Функция возвращения connection в пул
    */
   public final void returnConnection( Connection con )
   {
     if( con == null )
     {
       return;
     }

     try
     {
       connectionPool.returnObject( con );
     }
     catch( Exception ex )
     {
     }
   }

   public static void mainm()
   {
     //создаем наш объект ConnectionPool
     ConnectionPool pool = new ConnectionPool();
     //единожды инициализируем его указав класс драйвера, URL базы данных, а также логин и пароль к базе данных
     pool.initConnectionPool( "sun.jdbc.odbc.JdbcOdbcDriver", "jdbc:odbc:база", "логин", "пароль" );

     //получаем connection
     Connection con = pool.getConnection();

     //что нибудь делаем
     PreparedStatement ps = null;
        try {
            ps = con.prepareStatement("SELECT * FROM USERS");
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        String s="";
        ResultSet rs = null;
        try {
            rs = ps.executeQuery();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        try {
            while (rs.next()) {
                s+=rs.getString("ID_US")+"***";
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        try {
            ps.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }

     //возвращаем соединение в пул
     pool.returnConnection( con );
     s="";
   }
}

Храню глобальный ConnectionPool где и беру коннект, а создаю в ServletContextListener. В mainm пример использования. Каюсь коннект не возвращал после использования(вот при написании увидел), но как-то все слишком сложно. Результат: Через пол часа или меньше(работал я один) на боевом сервере приложение безнадежно виснет.

Собственно вопросы и просьбы:
Объясните как этим пулом пользоваться.
Не повлияет ночной бекап на соединения(они же рвутся)?
Обязательно ли возвращать коннекты?
Можно ли зарезервировать минимальное количество активных коннектов?
Большая просьба привести подробный пример, вы сделаете большое дела для таких как я.
Зависит ли commons-dbcp от Tomcat и будет ли нормально работать на WebSphere?
Спасибо!!!

Автор: dobrolub 8.4.2011, 09:03
>Вначале делал просто: на каждый insert, update,delete и select открывался отдельный коннект.
Начни их возвращать - это будет первый правильный шаг, который, возможно, и решит проблему. 

- WebSphere имеет свой модуль соединений. Тебе надо его сконфигурировать и запрашивать соединения из объекта DataSource, который станет доступен на jndi.
- На томкат тоже научись это делать так, чтобы пул клал javax.sql.DataSource на JNDI.

Шаблон работы с соединениями следующий:

Код

import java.io.*;
import java.sql.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.sql.*;

public class MyServlet extends HttpServlet {
  public void service(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
     InitialContext context = new InitialContext();
     DataSource ds = (DataSource)context.lookup("java:comp/env/mydatasource");
     Connection con;
     try {
       con = ds.getConnection();
       // insert, update, delete
       con.commit();
     } catch (SqlException e) {
        con.rollback();
     } finally {
        conn.close();//возвратим в пул
     }
  }
}

Автор: koroplysov 8.4.2011, 09:39
Спасибо за советы!

Коннекты я всегда conn.close();. Сконфигурировать соединение на вебсфере не проблема. Проблема в логике работы. Использую отдельный пул на случай если на сервере такого нет(использую то odbc). За сервлет спасибо. Не нужно ли будет перенастраивать чего при переходе с Tomcat на WebSphere?

Автор: koroplysov 8.4.2011, 10:37
Что-то приложение не виснет после того как я прописал отдачу в пул коннекта pool.returnConnection( con ); где pool типа ConnectionPool(из второго листинга).
Помоему всетаки по этой причине.
Буду держать в курсе пишущих и смотрящих.

Автор: koroplysov 11.4.2011, 08:44
Все виснет через некоторое время!!!

Автор: batigoal 11.4.2011, 08:58
koroplysov, значит, ищи утечку. Можешь воспользоваться профайлером, либо на стороне базы посмотреть активные коннекты и исполняющийся в них код (не знаю, возможно ли это в DB2).

Автор: koroplysov 11.4.2011, 09:13
Блин, начинаю отчаиваться!!!
Народ научите использовать соединение JNDI, пожалуйста. Для кода в последнем листинге. Как на сервере(WebSphere) настроить я знаю. Как это в NetBeans настроить? smile 

Автор: dobrolub 11.4.2011, 19:04
почитай тут:
http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html#JDBC_Data_Sources

Автор: koroplysov 12.4.2011, 07:43
С праздником всех!!!
Мы всегда будем первыми на земном шаре и в космосе!!!

Теперь по делу. Решил я всетаки настроить второй листинг и изменил:
Код

       connectionPool.setMaxIdle(20);//устанавливаем максимальное кол-во простаивающих соединений
       connectionPool.setMaxActive(200);//устанавилваем макс. кол-во активных соединений
       connectionPool.setMaxWait(10000);
       connectionPool.setTestWhileIdle(true);
       //connectionPool.setTestOnBorrow(true);
       connectionPool.setTimeBetweenEvictionRunsMillis(600000);
       connectionPool.setMinEvictableIdleTimeMillis(1800000);
       connectionPool.setNumTestsPerEvictionRun(20);

Со вчерашней второй половины дня полет нормальный!!! Буду держать в курсе. Ведь удобно

Автор: mbasil 12.4.2011, 11:45
А что под DB2 нет нормального JDBC драйвера? Использовать мост jdbc-odbc в продукционной системе кажется каким-то извращением.

Автор: koroplysov 14.4.2011, 11:02
Да есть конечно. Просто так сложилась, долго объяснять.
А теперь о приятном: РАБОТАЕТ!!! Вот уже два дня работает без сбоев и резервирование базы переваривает.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)