Пул соединений (Connection pool)

connection pool

Продолжаем писать сайт на java и сегодня мы поговорим о такой теме как пул соединение или connection pool на английском. Напомню, что это уже четвертая статья, посвященная простому java веб приложению. Остальные можно найти в рубрике Java web.

Коннект к базе данных требует затрат определенного времени. Особенно, если база данных находится удаленно. Если под каждых запрос делать подключение к базе, то отклик нашего приложения будет невероятно низким по скорости. Не говоря уже о ресурсах, которые оно потребит.

Во избежании таких проблем есть пул соединений, который открывает соединение только первый раз, а при закрытии соединения он не закрывает его, а отправляет в пул в случае если кто-то будет делать очередной запрос. По сути, приложение работает через специальный драйвер, который является оберткой для обычного jdbc драйвера и который имеет дополнительный функционал по работе с пулом.

Настройки для пула соединений программист может прописать вручную: количество активных соединений, время ожидания и т.д.

Для особенно активных, можно написать свой connection pool: класс, который буде иметь список соединений. У него будет переопределена функция close, которая будет возвращать соединение обратно в список и много других плюшек вроде таймера открытого соединения. Когда нет конекшина долгое время, соединение закрывается.

Мы не будем изобретать свой велосипед, а воспользуемся готовыми решениями. Так как мы используем Tomcat возьмем его реализацию пула соединений.

Для этого нужно в папке webapp создать папку META-INF и в ней создать xml файл context.xml.

создание context.xml

Далее нужно вставить настройки нашего пула в этот файл:

Код   
<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <!-- Specify a JDBC datasource -->
    <Resource name="jdbc/имяПула" - можно выбрать произвольно
             auth="Container"
             type="javax.sql.DataSource"
             username="Пользователь"
             password="Пароль"
             driverClassName="com.mysql.jdbc.Driver" - если используете mysql
             url="jdbc:mysql://хост:3306/имяБазы?autoReconnect=true"
             validationQuery="select 1"
             maxActive="10"
             maxIdle="4"/>

</Context>

 

Здесь указано по минимуму настроек. Полагаю, комментарии излишни.

Теперь осталось только использовать этот код в нашем приложении.

Создадим класс ConnectonPool и сделаем его singletone. Для тех, кто не знает: это когда нельзя создать больше одного экземпляра данного класса.

Для этого делается приватный конструктор и для доступа к экземпляру класса создается специальный метод, который возвращает только один экземпляр класса.

Код   
private ConnectionPool(){
        //private constructor
    }

    private static ConnectionPool instance = null;
   
    public static ConnectionPool getInstance(){
        if (instance==null)
            instance = new ConnectionPool();
        return instance;
    }

 

Далее нужно создать метод, через который мы будем получать соединение, но не напрямую, а через пул соединений.

Код   
public Connection getConnection(){
        Context ctx;
        Connection c = null;
        try {
            ctx = new InitialContext();
            DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/имяПула");
             c = ds.getConnection();
        } catch (NamingException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return c;
    }

 

Тут нужно немного комментариев.

Context — это интерфейс, который представляет собой контекст именования, который состоит из набора привязок имени к объекту. InitialContext — класс, который является исходным контекстом для выполнения операций именования. А говоря простым языком, Context, InitialContext позволяют нам иметь доступ к службам имен и каталогов. Для любопытных советую почитать о Java Naming and Directory Interface (JNDI). Метод lookup() извлекает объект по ссылке. В нашем случае он вынимает объект и кастит его к DataSource. Таким образом мы получили DataSource, который вернет нам Connection.

Полный код:

Код   
import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class ConnectionPool {
   
    private ConnectionPool(){
        //private constructor
    }

    private static ConnectionPool instance = null;
   
    public static ConnectionPool getInstance(){
        if (instance==null)
            instance = new ConnectionPool();
        return instance;
    }
   
    public Connection getConnection(){
        Context ctx;
        Connection c = null;
        try {
            ctx = new InitialContext();
            DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/mydatabase");
             c = ds.getConnection();
        } catch (NamingException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return c;
    }
}

Это все о пуле соединений. Как всегда, видео, где я все это приконнектил к проекту и ссылка на гитхаб: https://github.com/caligula95/simplewebapp-part4

Понравилась статья? Поделиться с друзьями:
Комментарии: 8
  1. Заплатный Ярослав

    Спасибо! Как всегда просто и познавательно))

  2. Alex

    javax.naming.NameNotFoundException: Name [jdbc/warmstar] is not bound in this Context. Unable to find [jdbc].
    Подскажите как эту ошибку исправить?

    1. denburda

      Вы назвали файл warmstar в папке META-INF, а пытаетесь достать файл jdbc.<Resource name="jdbc/имяПула"
      ctx.lookup(«java:comp/env/jdbc/имяПула»);

  3. Bawarez

    Отличная статья. Кратко и по делу. Спасибо!

  4. Ирина

    Спасибо за статью. Подскажите при отработке строчки ds = (DataSource) context.lookup(«java:comp/env/jdbc/mydatabase»); значение ds остается null. В дебаге выводит причину:Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial Что это может быть?

    1. Denys (автор)

      Попробуйте добавить строку: System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
      «org.apache.naming.java.javaURLContextFactory»); перед строкой ds = (DataSource) context.lookup(«java:comp/env/jdbc/mydatabase»);

      Конекшн пул указанный в статье будет работать только в связке с сервером. Если Вы пытаетесь запустить данный функционал в public static void main(String[] args){
      } — то есть без сервера, он будет выбрасывать ошибку, которую Вы описали.

  5. 1

    :twisted:

  6. Eugen

    у вас неправильно написан в примере метод getInstance(). в многопоточном приложении будут ситуации, когда создается несколько пулов с таким подходом.

    getInstance() должен быть потокобезопасным.

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: