jedis returnBrokenResource 메서드에 대한 오해
jedis의 returnBrokenResource 메서드에 대해 잘못 알고 있는 부분이 있어 정리한다.
@Before
public void init() {
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(20);
config.setBlockWhenExhausted(true);
config.setMaxWaitMillis(1000);
JedisShardInfo shard1 = new JedisShardInfo("test.com", 6300);
JedisShardInfo shard2 = new JedisShardInfo("test.com", 6301);
JedisShardInfo shard3 = new JedisShardInfo("test.com", 6302);
jedisShardInfos = new ArrayList<>();
jedisShardInfos.add(shard1);
jedisShardInfos.add(shard2);
jedisShardInfos.add(shard3);
pool = new ShardedJedisPool(config, jedisShardInfos);
template = new ShardedJedisTemplate();
template.setShardedJedisPool(pool);
}
public static <T> T execute(ShardedJedisPool jedisPool, ShardedJedisReturnedCallback<T> shardedJedisReturnedCallback) {
ShardedJedis shardedJedis = null;
boolean broken = false;
try {
shardedJedis = jedisPool.getResource();
T returnValue = shardedJedisReturnedCallback.doInShardedJedis(shardedJedis);
return returnValue;
} catch (JedisConnectionException e) {
if (shardedJedis != null) {
broken = true;
jedisPool.returnBrokenResource(shardedJedis);
}
throw new RedisException(e.getMessage(), e);
} finally {
if (shardedJedis != null && broken == false) {
jedisPool.returnResource(shardedJedis);
}
}
}
@Test
public void shardTest() {
for (int i = 0; i < 10; i++) {
String status = template.set("test:key" + i, "testvalue");
System.out.println(status);
}
}
위의 shardTest() 메소드가 실행되면 10번의 OK가 출력이 되고 3대의 redis 장비에 데이터가 샤딩되어진 것을 확인할 수 있다.
redis(6300):0>keys *
1) test:key1
2) test:key2
3) test:key8
4) test:key6
redis(6301):0>keys *
1) test:key9
2) test:key7
3) test:key4
redis(6302):0>keys *
1) test:key3
2) test:key5
3) test:key0
만약 위의 환경에서 6302 포트를 사용하는 레디스 서버를 셧다운 한 후 shardTest() 테스트 함수를 다시 실행하게 된다면 6302에 적재되는 데이터가 6300, 6301 서버로 데이터가 분배되어야 할 것으로 생각을 하였다.
이와 같이 생각한 이유는 jedis 라이브러리의 jedisPool.returnBrokenResource(shardedJedis); 가 하는 역할이 pool 안에서 connection timeout으로 인해 오류가 난 리소스를 제거하는 것으로 알고 있었기 때문이다.
허나 아무리 테스트를 해봐도 서비스되고 있는 레디스 장비에 적절하게 데이터가 샤딩되지 않았다.
결국 https://mail.google.com/mail/u/0/#inbox/153541b65559f797 에 질문을 올렸고, 그 답을 찾을 수 있었다.
먼저 jedisPool.getResource(); 호출 시 pool 객체 안에 idle 상태의 ShardedJedis 가 존재하면 해당 객체를 return 하고 없으면 ShardedJedis 생성한 후 이를 사용하게 된다.
ShardedJedis 객체를 생성할 때의 코드를 보면 생성자에 shards 변수를 넘겨주고 있는데 shards 변수에는 모든 서버의 정보가 저장되어 있다. (6300, 6301, 6302 레디스 서버 정보)
private List<JedisShardInfo> shards; <-- 해당 변수에 server1, server2, server3 정보가 저장되어 있음
@Override
public PooledObject<ShardedJedis> makeObject() throws Exception {
ShardedJedis jedis = new ShardedJedis(shards, algo, keyTagPattern);
return new DefaultPooledObject<ShardedJedis>(jedis);
}
특정 서버의 장비가 셧다운되어 connection timeout이 발생하여도 shards 변수에는 변동이 없기 때문에 위의 테스트 코드는 다음과 같은 시나리오대로 동작을 하게 되었던 것이다.
0에서 9까지의 값에 따른 데이터 분포는 다음과 같다고 가정하자.
redis(6300)
- 1,2,8,6
redis(6301)
- 9,7,4
redis(6302) <- 해당 장비 셧다운 상태
- 3,5,0
1번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis 객체가 존재하지 않아 새로 생성 후 값 저장. 이후 pool에 자원 반납
2번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis가 존재하여 해당 객체를 꺼내서 사용 후 반납
3번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis가 존재하여 재사용하는데 connection timeout 발생하여 ShardedJedis 객체를 pool에서 제거
4번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis 객체가 존재하지 않아 새로 생성 후 값 저장. 이후 pool에 자원 반납
5번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis가 존재하여 재사용하는데 connection timeout 발생하여 ShardedJedis 객체를 pool에서 제거
결과적으로 내가 jedis returnBrokenResource 메서드에 대해 오해하게 된 것은 jedis에서 제공하고 있는 샤딩 동작 방법을 제대로 이해하지 못한 것이였고, 풀에서 관리하는 ShardedJedis 객체가 최초 애플리케이션이 구동할 때 한번에 생성을 하는 줄 알았다는 것이다.
'프로그래밍' 카테고리의 다른 글
문제를 통해 이해해보는 Javascript 기본 원리 (0) | 2016.07.29 |
---|---|
Spring Controller 테스트 하기 (MockMvc 사용) (0) | 2016.05.18 |
Thread 생성 비용이 큰 이유 (0) | 2016.04.06 |
CronExpression Test 하기 (0) | 2016.03.17 |
Mybatis 쿼리 로그 출력 (0) | 2016.03.02 |
비트단위의 논리곱을 이용한 데이터 활용 방법 - 논리곱(and) & (1) | 2016.01.31 |
진수 변환 (0) | 2016.01.26 |
MyBatis 샾(#) 달러($) 차이 (4) | 2016.01.21 |