지금 진행하는 팀 프로젝트는 운동을 같이할 운동메이트를 동네에서 찾는게 핵심입니다.
여기에 추가로 실시간 매칭기능을 넣으려고 하고 있습니다.
원래는 운동 종목별로, 가까운 사람들 기준으로 등등 여러가지 사항을 고려해야하지만
프로젝트 제출 시간이 얼마 없기에 가능한 만큼 실시간 매칭기능을 만들어보려고 합니다.
Redis는 싱글 스레드이기에 실시간 매칭을 할때 동시성문제가 발생하지 않을것이라 판단하여 Redis를 적용하기로 했습니다.
처음에는 List를 사용해서 대기열을 만들려고 했습니다.
하지만 List는 중복체크기능이 되지 않았습니다...
순서를 보장하기위해 Queue를 그리고 중복을 잡기위해 Set을 사용해야하는데 좋은게 없을까? 라고생각하다
Redis의 Sorted Set
을 알게되었습니다.
중복을 제거해주고, 순서도 보장할 수 있으니 실시간 매칭 대기열에 딱이였습니다!
순서를 보장하기위해 Sorted Set에 현재 시간을 넣었습니다.
<div class="boxTest btn-random gap-3 d-md-flex justify-content-md-end">
<p><div class="btn-random-text" id="randomCnt" sec:authorize="isAuthenticated()" th:text="${randomCnt}">현제 대기중인 인원 : </div></p>
<button class="btn btn-danger" style="display: none" type="button" id="randomMatchCancel_btn"
onclick="randomMatchCancel()">
+ 실시간 매칭 취소🌌
</button>
<button class="btn btn-outline-danger" sec:authorize="isAuthenticated()" type="button" id="randomMatch_btn" onclick="randomMatch()">
+ 실시간 매칭🔥
</button>
</div>
실시간 매칭 버튼을 누르면 randomMatch()
js의 함수가 실행되어 운동 카테고리를 선택하고 대기열로 입장합니다.
간단히 alert 창으로 카테고리를 입력받았습니다.
//실제 매칭을 잡는 로직
function startMatching(level, sport) {
let username = document.getElementById("myName").innerText;
$.ajax({
type: "POST",
url: '/api/v1/match/live' + "?username=" + username + "&sport=" + sport,
success: function (data) {
let listCnt = data;
if (listCnt > 0) {
Swal.fire({
icon: 'success',
title: '실시간 매칭이 시작되었습니다🔥\n\n Level : '+ level,
html: '매칭이 성사될때까지 대기해 주세요👍<br> 3명이 대기열에 들어오면 매칭됩니다',
});
let randomMatchCancelBtn = document.getElementById("randomMatchCancel_btn");
let randomMatchBtn = document.getElementById("randomMatch_btn");
randomMatchCancelBtn.style.display = 'block';
randomMatchBtn.style.display = 'none';
}
},
error: function (request, status, error) {
alert("로그인 후 랜덤매칭이 가능합니다.")
}
});
}
위에서 카테고리를 선택했으면 startMatching()
함수가 id와 입력받은 운동 카테고리를 갖고 ajax 통신을 하게됩니다.
url: '/api/v1/match/live' + "?username=" + username + "&sport=" + sport,
@PostMapping("/live")
@Transactional
public int randomMatch(@RequestParam String username, @RequestParam String sport) {
return liveMatchService.randomMatch(username, sport);
}
username과 sport를 파라미터로 받아 service딴으로 넘겨줍니다.
이해를 위해 서비스 로직을 나누어 설명하겠습니다!
public int randomMatch(String username, String sport) {
// redis에 대기열 순서대로 삽입, 현재 시간을 score로 잡음
redisTemplate.opsForZSet().add(sport, username, System.currentTimeMillis());
// 현재 대기열의 총 숫자 확인
Long randomMatchListInRedis = redisTemplate.opsForZSet().zCard(sport);
//대기인원을 sport 종목에 따라 UI에 뿌려주는 로직
sendSportListCntToUser(sport);
랜덤매칭을 누르고 대기열에 들어온 상태입니다.
이제 랜덤매칭 List에 3명이상이 되면 방을 생성하고 채팅방을 생성해서 채팅방으로 이동시키면 됩니다
if (randomMatchListInRedis >= 3) {
String[] UserListInRedis = new String[4];
Set<String> range = redisTemplate.opsForZSet().range(sport, 0, 3);
Iterator<String> iterator = range.iterator();
int listCnt = 0;
while (iterator.hasNext()) {
String next = iterator.next();
UserListInRedis[listCnt] = next;
listCnt++;
}
User fistUser = findUserFromRedis(UserListInRedis[0]);
User secondUser = findUserFromRedis(UserListInRedis[1]);
User thirdUser = findUserFromRedis(UserListInRedis[2]);
Crew savedLiveMatchCrew = makeCrew(fistUser, secondUser, thirdUser, sport);
makeParticipationsAndUpdateToCrew(fistUser, secondUser, thirdUser, savedLiveMatchCrew, sport);
deleteRedisLiveMatchLists(UserListInRedis, sport);
sendSseToUsers(fistUser, secondUser, thirdUser, savedLiveMatchCrew);
}
return 1;
}
UserListInRedis
배열을 생성합니다.UserListInRedis
배열에 넣습니다.UserListInRedis
에 있는 User명단을 통해 DB에서 User를 찾습니다.Redis란? 레디스의 기본적인 개념 (인메모리 데이터 구조 저장소)
파티매칭의 구현
[요청강좌]온라인게임제작강좌 part6. 랜덤매칭시스템 강좌
Redis Sorted Set
레디스 List, Sorted Set 자료형