목적
- 로스트 아크 게임 유저들을 위한 깐부 찾기 랜덤채팅 사이트
구성
- 프론트 : React, Shadcn, Javascript, Socket, STOMP
- 백 : SpringBoot, Java, MySQL, Redis, WebSocket, STOMP
이번에는 Redis의 사용에 대해 알아보자.
서버 배포는 AWS EC2에 진행 할건데, 설치 방법 및 내용은 별도 포스팅했다.
여기서는 로컬에서 Redis 사용법 및 체크하는 법에 대해서 알아보도록 하자.
목차
Redis 설치
맥 터미널
// redis설치
brew install redis
// redis설치 버전 확인
redis-server --version
// redis 백그라운드로 실행
brew services start redis
// redis 백그라운드로 재실행
brew services restart redis
// redis 백그라운드 실행프로세스 중지
brew services stop redis
redis를 실행할 수 있는 기본 명령어가 존재하긴 하나,
그렇게 실행했을 경우 I/O가 불가능해져서 (물론 새 터미널 켜도 됨) 백그라운드로 키는 것을 추천한다.
// redis 실행
redis-server
EC2 (Amazon Linux2023)
sudo yum update
sudo yum install gcc make
wget http://download.redis.io/releases/redis-6.2.5.tar.gz
tar xzf redis-6.2.5.tar.gz
cd redis-6.2.5
make
sudo mkdir /etc/redis
sudo mkdir /var/lib/redis
sudo cp src/redis-server src/redis-cli /usr/local/bin/
sudo cp redis.conf /etc/redis/
sudo vi /etc/redis/redis.conf
Redis CLI
redis-cli
실행하면 ip주소:포트 > 와 함께 명령어를 칠 수 있는 공간이 나온다.
많이 쓰게 될 Redis-CLI 명령어만 정리해보았다.
// redis에 저장된 모든 키 값 보기
keys *
// *는 sql의 %와 같은 조건이며 keys *opt* 를 하면 key 값 중 opt를 포함하고 있는 key들을 조회함
keys *{검색할단어}*
// key값에 해당하는 value보기
get {key}
// sorted-set의 value보기
ZRANGEBYSCORE {key} -inf +inf
// sorted-set의 value, 가중치 같이 보기
ZRANGE {key} 0 -1 withscores
// redis 데이터(key-value) 생성
set {key} {value}
// 모든 데이터(key-value) 삭제
flushall
SpringBoot 설정
build.gradle
// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
application.yml
spring:
redis:
host: localhost
port: 6379
RedisConfig
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(redisHost, redisPort);
}
@Bean
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
Redis를 사용한다면 위의 설정은 다들 똑같이 진행되어야 할 것이다.
별다른 설명은 할 것이 없고 RedisConfig에서 RedisTemplate<String, String> 의 경우 Redis의 여러가지 데이터타입이 존재하는데
key-value를 <String, String>으로 쓸 것이냐 혹은 <String, Object> <String, Long> 등 본인이 사용할 데이터 셋으로 설정하면 된다.
RedisTemplate은 Spring <-> Redis 간 데이터 전송 형태를 정해주는 것이라고 보면 되겠다.
RedisServiceImpl
매칭 서비스에 대한 설명은 이전 포스팅에서 설명했었다.
다시 한 번 요점만 얘기해보자면
1. 매칭 시작을 하면 우선적으로 Redis 대기 리스트에 동일 매칭 옵션을 가진 사람을 조회한다.
2. 동일 옵션이 있다면 등록된 사람 기준으로 채팅방이 생성된다. 이 때 Redis 대기 리스트에 등록된 채로 기다리던 사람은 STOMP /sub를 통해 매칭이 됨을 확인 후 채팅방에 입장하게 된다.
3. 동일 옵션이 없다면 본인의 정보가 Redis 대기 리스트에 등록된다.
4. 등록 순을 우선순위로 가져가기 위해 Sorted-Set 을 이용하였으며 등록시간이 가중치다.
5. 다른 사람이 매칭을 돌릴 때 Redis 대기 리스트를 조회하다가 일치한다면 본인 기준으로 방이 생성된다.
6. 20초가 지나도록 매칭이 되지 않는다면 '등록한 옵션개수 -1'만큼 일치하는 사람을 다시 리스트에서 찾는다. 단, 상대방도 옵션이 2개만 매칭되도 상관없어야 한다.
7. 리스트에 없다면 Redis 대기 리스트에 재등록한다. 이 때 우선순위는 최초 등록시간을 그대로 가져가기 때문에 변동되진 않으며 key값만 변동 된다.
*Redis에서 Sorted-Set을 사용하였으며 key(옵션수:옵션내용) value(id,roomKey) 데이터로 구성함.
*매칭용 STOMP endpoint를 따로 뒀다.
Redis에 접근해서 처리해야하는 경우는 총 3가지 밖에 없다.
1. Redis 대기리스트를 조회한다.
2. Redis 대기리스트에 새로운 key-value를 추가한다.
3. Redis 대기리스트에서 데이터를 제거한다.
@Service
@RequiredArgsConstructor
public class RedisServiceImpl implements RedisService{
private final RedisTemplate<String, String> redisTemplate;
@Transactional
public ChatRoom addUserOptions(long timeMillis, MatchReq req) {
ChatRoom room = setRoomInfo(req);
redisTemplate.opsForZSet().add(Integer.toString(req.getOptionCount())+":"+ req.getPrefer(),
req.getUuId() + ":" + req.getRoomKey(), timeMillis);
return room;
}
@Transactional
public ChatRoom modifyUserOptions(MatchReq req) {
ChatRoom room = chatRoomRepository.findByRoomKey(req.getRoomKey());
redisTemplate.opsForZSet().remove(Integer.toString(req.getOptionCount()+1)+":"+ req.getPrefer(), req.getUuId()+ ":" + req.getRoomKey());
redisTemplate.opsForZSet().add(Integer.toString(req.getOptionCount())+":"+ req.getPrefer(),
req.getUuId() + ":" + req.getRoomKey(), req.getTime());
return room;
}
.. 이하 다른 service 구현..
}
Sorted-Set 데이터형을 사용하였기 때문에 timeMillis는 프론트에서 '매칭시작'을 누른 시간을 보내게 된다.
MatchReq는 매칭 옵션에 대한 정보가 담아져서 넘어온다.
public ChatRoom addUserOptions(long timeMillis, MatchReq req)
내가 설정한 key-value형태로 RedisTemplate을 이용하여 저장한다. + 매칭시간을 가중치로 넘긴다.
redisTemplate.opsForZSet().add(Integer.toString(req.getOptionCount())+":"+ req.getPrefer(),
req.getUuId() + ":" + req.getRoomKey(), timeMillis);
예시) key::"3:11,13,15-100" value::"asd123:aaaa-1111-bbbb" 가중치::timeMillis
옵션 변동으로 인해 Redis대기리스트의 key값을 바꿔야 한다. (취소 후 재매칭x) 수정을 하는 경우, 기존 Sortes-Set을 삭제 후 다시 add해주는데 우선순위를 유지하고 싶다면 최초 등록 시의 가중치 값을 그대로 가져오면 된다.
redisTemplate.opsForZSet().remove(Integer.toString(req.getOptionCount()+1)+":"+
req.getPrefer(), req.getUuId()+ ":" + req.getRoomKey());
redisTemplate.opsForZSet().add(Integer.toString(req.getOptionCount())+":"+ req.getPrefer(),
req.getUuId() + ":" + req.getRoomKey(), req.getTime());
매칭 취소하는 경우 Redis 대기리스트에서 삭제한다.
redisTemplate.opsForZSet().remove(Integer.toString(req.getOptionCount())+":"+
req.getPrefer(), req.getUuId()+ ":" + req.getRoomKey());
redis-cli를 통해 조회한 내역이며 처음에 대기리스트에 2명이 존재하고
1) 옵션을 (3:13,15,12-100 에서 2:13,15,12-100) 변경 한 내역을 볼 수 있을 것이다.
결국 Redis도 데이터베이스이기에 단순히 조회, 추가, 삭제, 수정 등등이 다 가능하며
그 속도가 더 빠르며 메모리에 저장된다가 다른 점이기에 크게 어려울 것은 없다.
다만 RDB에 익숙한 사람들의 경우에는 어떻게 Insert하고 Select를 하는지 문법이나 방식이 많이 달라 초반에 헤맬뿐...
다음 포스팅은 매칭 시스템 로직에 대한 부분이다.
Redis와 같이 묶어서 쓰려다가 로직 부분이 생각보다 길어져서 따로 다룰 예정이다.
참고 - EC2 Redis 설치 https://velog.io/@ssoop/AWS-EC2%EC%97%90-Redis-%EC%84%A4%EC%B9%98
'IT이야기 > PROJECT' 카테고리의 다른 글
[AWS] S3 + CloundFront를 이용한 React 배포 (3) | 2024.10.21 |
---|---|
[React + SpringBoot] 1대1 랜덤채팅 프로젝트 - Redis를 이용한 매칭 로직 (0) | 2024.03.11 |
[React + SpringBoot] 1대1 랜덤채팅 프로젝트 - Spring&웹소켓&STOMP 설정 및 구현 (0) | 2024.03.09 |
[React + SpringBoot] 1대1 랜덤채팅 프로젝트 - 화면 및 기능 설계 (실시간 통신, 매칭) (1) | 2024.03.08 |
[React + SpringBoot] 1대1 랜덤채팅 프로젝트 - 환경설정 및 구성 (0) | 2024.03.08 |