티스토리 뷰

공부

Jedis 사용시 유의할점

Vincent Park 2016.09.28 19:08

안정된 서비스의 길은 멀고도 험난하다.. 

이번에도 큰 삽질을 했으니 또다시 같은 실수를 반복하지 않기 위하여 기록으로 남겨볼까 한다.


내가 담당하고 있는 API application 에서 cache 서버로 사용중인 redis 를 jedisPool 로 맺어서 캐시용도로 사용중이다.

cache get/set 에 문제가 발생하면 jedisPool connection 을 다시 맺도록 로직을 넣어두었는데,

아 reconnection 로그가 너무 많이 떨어진다.... 왜그럴까?;;


결론만 이야기 하자면, 

JedisPool.getResource() 해온 jedis 객체를 최초 한번만 가져와서 static 변수에 넣어두고 사용했던것이 화근이었다.

아래 PoolBenchmark 코드를 참고로 보면, 스레드로 돌리면서 jedis 객체를 매번 pool.getResource() 하고 있다.


결국 뭐냐면, jedisPool 에서jedis 객체를 다시 가져와서 재사용을 하던, 새로 생성해서 사용을 하던 pool 에서 매번 사용가능한 jedis 객체를 가져와서 사용했어야 하는건데, 처음에 한번만 가져와서 static 객체에 넣어두고 그 jedis 객체 하나만 사용하다 보니;;

결국 그 하나의 jedis 객체가 소화가능한 한계치에 다다르게 되면 cache fail 이 나면서 다시 reconnect 가 일어나게 되었던 것이다.


그리고 또 한가지.

그렇게 매번 가져와서 사용한 jedis 객체는 사용이 끝나면 바로 close() 해서 pool 로 반환해주어야 한다.

그렇지 않으면 맺어진 connection 이 좀비처럼 남아서 서버에 리소스만 잡아먹게되는 현상이 발생하게된다.


jedisPool 도 마찬가지 이유로 reconnect 가 일어날때에는 반드시 destroy() 를 해주어야 한다.   


리팩토링 후에 모니터링 결과 전혀 문제가 발생하지 않음을 확인! ㅠㅠ 휴~~ 


아.. 진짜 제대로 쓸줄 모르면 쓴다고 깝치면 안된다. ㅜㅜ

많이 반성했고, 앞으로 오픈소스를 쓸때는 귀찮다고 아무렇게나 다른 솔루션 쓰던 가닥으로 대충 사용하지 말고,

제대로 알아보고 공부해서 최적의 상태로 사용하자!! ㅠㅠ



package redis.clients.jedis.tests.benchmark;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.tests.HostAndPortUtil;

public class PoolBenchmark {
  private static HostAndPort hnp = HostAndPortUtil.getRedisServers().get(0);
  private static final int TOTAL_OPERATIONS = 100000;

  public static void main(String[] args) throws Exception {
    Jedis j = new Jedis(hnp.getHost(), hnp.getPort());
    j.connect();
    j.auth("foobared");
    j.flushAll();
    j.quit();
    j.disconnect();
    long t = System.currentTimeMillis();
    // withoutPool();
    withPool();
    long elapsed = System.currentTimeMillis() - t;
    System.out.println(((1000 * 2 * TOTAL_OPERATIONS) / elapsed) + " ops");
  }

  private static void withPool() throws Exception {
    final JedisPool pool = new JedisPool(new GenericObjectPoolConfig(), hnp.getHost(),
        hnp.getPort(), 2000, "foobared");
    List<Thread> tds = new ArrayList<Thread>();

    final AtomicInteger ind = new AtomicInteger();
    for (int i = 0; i < 50; i++) {
      Thread hj = new Thread(new Runnable() {
        public void run() {
          for (int i = 0; (i = ind.getAndIncrement()) < TOTAL_OPERATIONS;) {
            try {
              Jedis j = pool.getResource();
              final String key = "foo" + i;
              j.set(key, key);
              j.get(key);
              j.close();
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        }
      });
      tds.add(hj);
      hj.start();
    }

    for (Thread t : tds)
      t.join();

    pool.destroy();

}


참조 : https://github.com/xetorthio/jedis/blob/master/src/test/java/redis/clients/jedis/tests/benchmark/PoolBenchmark.java


저작자 표시
신고
댓글
댓글쓰기 폼