2016-01-21 4 views
0

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

Вот мой первый проект внедрения:

Spring Datasource Bean:

private DataSource ds; 
@Resource(name="jdbc") 
public void setDataSource(DataSource ds) { 
    this.ds = ds; 
} 

Initialization метод:

public void checkUsersMulti(List<User> users) throws Exception { 
    if(users!= null || users.size() != 0) { 
     ExecutorService executorService = Executors.newFixedThreadPool(20); 
     Queue<User> queue = new ConcurrentLinkedQueue<>(); 
     queue.addAll(users); 
     for (Useruser: users) { 
      executorService.submit(new ProcessUser(queue)); 
     } 
     executorService.shutdown(); 
     try { 
      executorService.awaitTermination(1, TimeUnit.HOURS); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

    } 
} 

Runnable Класс:

Метод DAO
class ProcessUser implements Runnable { 
    private final Queue<User> queue; 

    public ProcessUser(Queue<User> queue) { 
     this.queue = queue; 
    } 

    public void run() { 
     try { 
      Connection conn = ds.getConnection(); 
      User user = null; 
      while((user = queue.poll()) != null) { 
       userDao.getUser(user , conn)); 
      } 
      DbUtils.closeQuietly(conn); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Пользователь:

public User retrieveUser(User user, Connection conn) { 
    PreparedStatement st = null; 
    ResultSet rs = null; 
    try { 
     String sql = "SELECT firstname, lastname FROM users WHERE id= ?; 

     st = conn.prepareStatement(sql); 
     st.setString(1, user.getId()); 
     rs = st.executeQuery(); 

     while(rs.next()) { 
      user.setFirstName(rs.getString("firstname")); 
      user.setLastName(rs.getString("lastname")); 
     } 
    } catch (Exception e) { 
     return null; 
    } 
    finally { 
     DbUtils.closeQuietly(rs); 
     DbUtils.closeQuietly(st); 
    } 
    return user; 
} 

UPDATE: Tomcat JNDI соединения Параметры пула:

<Resource 
    name="jdbc" 
    auth="Container" 
    type="javax.sql.DataSource" 
    maxTotal ="25" 
    maxIdle="30" 
    maxWaitMillis ="10000" 
    driverClassName="***Vendor Driver***" 
    url="***Valid URL" 
    username="***Valid Username***" 
    password="***Valid Password***" 
/> 

Однако, я получаю эту ошибку иногда:

java.sql.SQLException: Cannot get a connection, pool error Timeout waiting for idle object

Но процесс по-прежнему завершен, как ожидалось, - это может быть в настройках JNDI. Моя большая проблема заключается в том, что это «правильный» способ реализовать это. Я предположил, что лучше всего удерживать объект соединения, поэтому им не нужно повторно инициализировать.

+0

Каков размер 'users'? возникает проблема для любого размера 'users'? –

+0

мой набор данных ~ 200 прямо сейчас, я постараюсь расширить и сжать его, чтобы посмотреть, что такое поведение. Я внедрил предложения @ rmalchow, но мне любопытно посмотреть, как он себя ведет. –

ответ

0

есть несколько проблем с этим кодом, самое главное, я не уверен, что это:

public void checkUsersMulti(List<User> users) throws Exception { 
    if(users!= null || users.size() != 0) { 
     ExecutorService executorService = Executors.newFixedThreadPool(20); 
     Queue<User> queue = new ConcurrentLinkedQueue<>(); 
     queue.addAll(users); 
     for (Useruser: users) { 
      executorService.submit(new ProcessUser(queue)); 
     } 
     executorService.shutdown(); 
     try { 
      executorService.awaitTermination(1, TimeUnit.HOURS); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

    } 
} 

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

  • вы ограничены с точки зрения количества пользователей, вы можете обрабатывать, потому что очередь не может расти до бесконечности (что @sabit хан, вероятно, беспокоит)

  • вам нужно запустить runnable по крайней мере ONCE для каждого пользователя, хотя, если некоторые runnables не выходят из-за ошибки, первые 20 будут продолжать работать до тех пор, пока очередь не будет пустой, а затем выйдут остальные, посмотрите, что очередь пуста, и сразу же выйти.

Возможно, вы должны ограничить количество параллельных процессов, которые вы запускаете. у вас есть пул потоков, равный 20, поэтому никогда не должно быть больше 20 одновременных подключений, если вы правильно закрываете свои подключения.не говоря уже о каких-либо других исключений, но это:

try { 
     Connection conn = ds.getConnection(); 
     User user = null; 
     while((user = queue.poll()) != null) { 
      userDao.getUser(user , conn)); 
     } 
     DbUtils.closeQuietly(conn); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 

, вероятно, следует:

Connection conn = null; 
    try { 
     conn = ds.getConnection(); 
     User user = null; 
     while((user = queue.poll()) != null) { 
      userDao.getUser(user , conn)); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } finally { 
     DbUtils.closeQuietly(conn); 
    } 

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

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

относительно вашего фактического вопроса: то, как вы это делаете, означает, что первые двадцать runnables пытаются получить соединения, а затем удерживают их в течение очень долгого времени. если ваш пул настроен на использование, предоставляйте меньше подключений, чем это (скажем, 15), тогда это может произойти, так как первые 15 будут успешно получать соединение, тогда следующие 5 рабочих будут непрерывно запускать runnable, try, fail, exit , ИЛИ вы используете ровно 20 соединений в вашем пуле, вы можете протекать соединения, потому что вы не закрываете их должным образом, как описано выше.

+0

Итак, делая ** (Пользователь пользователя: пользователи) **, я на самом деле создаю много потоков ... из которых 20 действительно что-то делают. Поэтому я изменил его на ** для (int i = 0; i <20; i ++) **. И я обновился до конца, как вы предлагаете. Является ли проблема с удержанием соединений в том, что они могут тайм-аут и терпеть неудачу? –

+0

Я думаю, что быть более прямым, какие плюсы и минусы удерживать его? Я бы подумал, что есть немного меньше накладных расходов, когда нужно вернуться к источнику данных, чтобы получить соединение ... но я не знаю, какие плохие ситуации могут привести вас или почему рекомендуется вернуть его. –

+0

Длительность не является большой проблемой, но, как писал @kiresays, накладных расходов немного. – rmalchow