몇 달전에 redis client를 어떻게 사용하는게 더 잘 사용하는지에 대해서 여러가지 검토 및 테스트를 진행했었습니다.

시간이 지나서 많이 잊어버려서 몇가지만이라도 메모 해둡니다.

 

1. https://lettuce.io/core/release/reference/index.html#_pipelining_and_command_flushing 에 정리가 잘되어 있었습니다. (AWS 문서는 퀄리티가 높음)

 

2. lettuce를 사용했을때 Pipe 커맨드 내용

 - https://lettuce.io/core/release/reference/index.html#_pipelining_and_command_flushing

 

3. I/O 멀티플렉싱 향상된 redis 버전을 사용

 - 예) ElastiCache for Redis 버전 7.0(향상된 버전)

     https://aws.amazon.com/ko/blogs/database/enhanced-io-multiplexing-for-amazon-elasticache-for-redis/

AWS에서 Redis(Elastic cache)사용시 모니터링 등에 사용하는 커맨드
 - 예) 리얼 환경에서 사용하면 안되는 커맨드가 가끔 개발자 실수로 반영될 수 있음(예. keys)

-- redis에 keys 커맨드가 실행되고 있는지 모니터링하는 커맨드
 redis-cli -h 도메인혹은IP monitor | grep keys
  - 예) redis-cli -h 블라블라.apn2.cache.amazonaws.com monitor


-- redis에 접속되어있는 유니크한 클라이언 IP 리스트 확인 커맨드
redis-cli -h redis서버호스트 CLIENT LIST | awk '{print $2}' | sed s/:.*//g | sort -u

 


AWS ec2에 redis-cli설치하는 방법

# make 하기 위해서 gcc 다운
sudo yum install -y gcc

# redis-cli 설치 및 make
wget http://download.redis.io/redis-stable.tar.gz && tar xvzf redis-stable.tar.gz && cd redis-stable && make

#src/redis-cli 에서 사용 가능
예) ./redis-cli -h 'redis 호스트 도메인' -p '포트번호'

 

메모

 

어플리케이션을 개발하다보면 분산락 처리를 신경써야하는 과제들이 간혹 있는데 저는 보통 2가지를 사용합니다.

 

  1. mysql의 named lock 사용
    1. 보통 어플리케이션이 RDB는 사용중인 경우가 많음
    2. 트래픽이 아주 많아서 성능이 중요하지 않음
    3. 개인적으로 티어가 복잡해지지 않아서 선호
    4. 참고
      1. mysql lockking 링크 
  2. redis이용
    1. redis 가 추가로 필요
      1. 개인적으로는 atomic을 보장하는 setnxsetex 를 활용하여 심플하게 개발하기도 함
        1. 스핀락 형태라 주의할점이 있음(ex. ttl 없고, redis 부하 증가 등). 간단히 예상되는 트래픽에 많이 씀
    2. 참고
      1. 참고 글  링크
      2. redis redlock best practices 링크
  3. 기타
    1. 아주 예전에는 zookeeper를 사용하기도 했는데 번거로움

참고

 - https://aws.amazon.com/ko/blogs/database/optimize-redis-client-performance-for-amazon-elasticache/

 - https://aws.amazon.com/ko/blogs/database/best-practices-redis-clients-and-amazon-elasticache-for-redis/

 

 - https://github.com/lettuce-io/lettuce-core/wiki/Connection-Pooling

 - https://lettuce.io/core/release/reference/#connection-pooling.is-connection-pooling-necessary

  1. 필요 라이브러리
    1. maven dependency 추가
<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>5.3.3.RELEASE</version>
</dependency>


<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.8.1</version>
</dependency>

 

2. 샘플 소스 - java

 - async command 등 참고 링크

import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisConnectionException;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.support.ConnectionPoolSupport;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

/**
 * redis 커넥션풀 기반 연결 샘플
 *
 * @author 
 */
public class ConnectionSampleWithPool {

	private static final String REDIS_CON_URL = "redis://192.168.56.1:6379/0"; //로컬 redis 0번 사용
	public static GenericObjectPool<StatefulRedisConnection<String, String>> pool = null; //sync & 비 클러스터모드 커넥션풀

	public static void main(String[] args) throws InterruptedException {

		pool = nonClusterPoolUsage();
		//pool = useClusterPoolUsage(); //클러스터모드일때

		testSetValue("key-pool-test-1", "key-pool-test-1");
		testSetValue("key-pool-test-2", "key-pool-test-2");
		testSetValue("key-pool-test-3", "key-pool-test-3");

		Thread.sleep(10 * 1000); //커넥션풀 close 전에 대기 후, redis-cli(서버)에서 client list 명령어로 현재 연결된 클라이언트 리스트를 확인해보면 됨

		pool.close();
		System.out.println("finish");
	}

	/**
	 * set value 테스트
	 *
	 * @param key
	 * @param value
	 */
	private static void testSetValue(String key, String value) {

		try (StatefulRedisConnection<String, String> connection = pool.borrowObject()) { //pool을 이용해서 커맨드 실행
			connection.sync().set(key, value);
			value = connection.sync().get(key);
			System.out.println(value);
		} catch (RedisConnectionException e) {
			System.out.println(String.format("Failed to connect to Redis server: %s", e));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 비 클러스터 모드일때 커넥션풀 셋팅
	 *
	 * @return
	 */
	private static GenericObjectPool<StatefulRedisConnection<String, String>> nonClusterPoolUsage() {

		RedisClient client = RedisClient.create(REDIS_CON_URL);
		client.setOptions(ClientOptions.builder().autoReconnect(true).build());

		return ConnectionPoolSupport.createGenericObjectPool(() -> client.connect(), createPoolConfig());
	}

	/**
	 * 클러스터모드일때 커넥션 풀 셋팅
	 *  - 참고: https://github.com/Azure/azure-redis-cache-samples/blob/master/Java/ClientSamples/src/main/java/lettuce/PoolUsage.java
	 *
	 * @return
	 */
	private static GenericObjectPool<StatefulRedisClusterConnection<String, String>> useClusterPoolUsage() {

		RedisClusterClient clusterClient = RedisClusterClient.create(REDIS_CON_URL);
		clusterClient.setOptions(ClusterClientOptions.builder().autoReconnect(true).build());

		return ConnectionPoolSupport.createGenericObjectPool(() -> clusterClient.connect(), createPoolConfig());
	}

	/**
	 * 커넥션풀 설정 생성
	 *
	 * @return
	 */
	private static GenericObjectPoolConfig createPoolConfig() {

		GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();

		poolConfig.setMaxTotal(20);
		poolConfig.setMaxIdle(10);

		// "true" will result better behavior when unexpected load hits in production
		// "false" makes it easier to debug when your maxTotal/minIdle/etc settings need adjusting.
		poolConfig.setBlockWhenExhausted(true);
		poolConfig.setMaxWaitMillis(1000);
		poolConfig.setMinIdle(5);

		return poolConfig;
	}

}
  1. 목적
    1. 최신버전의 redis를 로컬 virtualbox의 centos에 설치해보고 프로그램으로도 저장해봄
       
  2. Install Redis
sudo yum -y install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
sudo yum --enablerepo=remi install redis
 
  1. Start Redis
sudo systemctl start redis
 
 
#버전확인
redis-cli -v
 
  1. 재 부팅시 자동으로 시작되도록 처리
sudo systemctl enable redis
 
 
#확인
systemctl list-unit-files | grep redis
 
  1. 추가로 redis conf 튜닝 예
Redis Conf 튜닝 예(sudo vim /etc/redis.conf)
 
 
#방화벽으로 관리할 경우라서 편의성 문제로 모든 IP에서 접근 허용
bind 0.0.0.0
 
 
#아래 file save옵션 부분 주석처리(메모리 캐쉬로만 사용하기 위해서)
#save 900 1
#save 300 10
#save 60 10000
 
 
859라인쯤에 아래 추가(최대 메모리 제한)
maxmemory 256m
 
적용 : sudo systemctl restart redis
 
 
  1. 참고 : 삭제 방법
yum erase $(rpm -qa | grep redis)
 
  1. 결과
    1. install
       
    2. redis-cli 접속 후 key-value 테스트
          
 
 

  1. EPEL 저장소 추가
sudo yum install epel-release
sudo yum update

  1. Install Redis
sudo yum install redis

  1. Start Redis
sudo systemctl start redis

#버전확인
redis-cli -v

  1. 재 부팅시 자동으로 시작되도록 처리
sudo systemctl enable redis

#확인
systemctl list-unit-files | grep redis

  1. 추가로 redis conf 튜닝 예
Redis Conf 튜닝 예(sudo vim /etc/redis.conf)

#방화벽으로 관리할 경우라서 편의성 문제로 모든 IP에서 접근 허용
bind 0.0.0.0

#주석처리(메모리 캐쉬로만 사용하기 위해서)
#save 900 1
#save 300 10
#save 60 10000


537라인쯤에 아래 추가
maxmemory 256m


적용 : sudo systemctl restart redis


+ Recent posts