Загружаем каталог…
Загружаем каталог…
들어가며 회사에서 만들고 있는 서비스가 사내 테스트를 앞두고 있었다. 사용자가 직접 콘텐츠를 만들고, 여럿이 들어와서 실시간으로 같이 노는 서비스인데, 이번 테스트에는 기획자가 만든 게임 컨텐츠를 테스트 해보는 것이였다. 그런데 테스트 날짜가 다가올수록 계속 찜찜한 게 하나 있었다. "그래서 우리 서버, 몇 명까지 버티는데❓" 아무도 몰랐다. 나도 몰랐다. 백엔드가 나 혼자인데 내가 모르면 아무도 모르는 거다 😇 "대충 100명은 되지 않을까요?" 같은 말을 할 수는 없으니, 직접 재보기로 했다. 왜 도구를 직접 만들었나 🤔 처음엔 당연히 있는 도구를 쓰려고 했다. 그런데 우리 서비스는 요청 하나 보내고 끝나는 구조가 아니다. 로그인 토큰으로 WebSocket에 접속 해 있어야 하고 이동하면 REST API를 부르고, 그 결과가 WebSocket으로 같은 장소 사람들에게 뿌려지고 채팅은 WebSocket으로만 오간다 이동과 채팅만 하나? 게임 컨텐츠인데, 명령어도 쓰고, 전투도 하고, 퀘스트도 하고... URL 하나를 초당 몇 번 때리는 방식으로는 "사람이 들어와서 노는 상황"이 안 만들어졌다. 그래서 실제 사용자처럼 행동하는 가상 사용자 를 직접 만들기로 했다. 가상 사용자는 이렇게 생겼다 가상 사용자 한 명은 접속한 뒤에 "행동 하나 → 잠깐 쉬기"를 계속 반복한다. 어떤 행동을 할지는 비율로 정했다. // 글을 위해 단순하게 다시 쓴 코드입니다 const ACTIONS = { move: 35, chat: 35, look: 27, command: 3 }; function pickAction() { const total = Object.values(ACTIONS).reduce((a, b) => a + b, 0); let r = Math.random() * total; for (const [name, weight] of Object.entries(ACTIONS)) { if ((r -= weight) < 0) return name; } } async function run(bot) { while (bot.alive) { await bot[pickAction()](); // 이동, 채팅, 조회, 명령어 중 하나 await sleep(random(minThink, maxThink)); // 사람처럼 잠깐 쉰다 } } 이동 35%, 채팅 35%, 조회 27%, 명령어 3%. 그리고 인원을 50명 → 100명 → 150명 → 200명으로 올려가면서 60초씩 돌렸다. 평균만 보면 안 보인다 여기서 하나 신경 쓴 게 있다. 응답 시간을 요청 종류별로 따로 모았다. 전체 평균만 보면 "평균 200ms네, 괜찮네~" 하고 끝난다. 그런데 느린 요청이 전체의 3%라면 평균에는 티도 안 난다. 그 3%를 누른 사용자만 죽어나는 거다. 그래서 요청 종류마다 p99를 봤다. (p99 = 요청 100개 중 99개가 이 시간 안에 끝났다는 뜻. 제일 운 나쁜 사람이 겪는 시간이라고 보면 된다) 서버 상태도 같이 모았다. CPU, 메모리, DB 연결 수, 이벤트 루프 지연까지. 결과: 범인은 한 놈이었다 🕵️ 가상 사용자 초당 요청 일반 요청 p99 명령어 처리 p99 50명 7.3 약 0.15초 0.9초 100명 15.2 약 0.28초 0.3초 150명 22.6 약 0.33초 5.5초 200명 26.4 약 0.87초 11.8초 100명까지는 전부 1초 안에 끝났다. 그런데 150명부터 명령어 처리 하나만 5.5초, 200명에서는 11.8초가 걸렸다. 다른 요청은 여전히 1초 안쪽인데 말이다;;; 혹시 한 콘텐츠에 사람이 몰려서 그런가 싶어서, 같은 인원을 콘텐츠 2~3개에 나눠서도 돌려봤다. 방송(같은 장소 사람들에게 뿌리는 것)은 확실히 가벼워졌는데, 명령어 처리는 그대로 느렸다. 즉, 사람이 몰려서가 아니라 명령어 처리 자체가 무거운 거였다. 전체 평균만 봤으면 절대 못 찾았을 범인이다. 뭘 고쳤나 명령어 처리 코드를 열어보니 이유가 보였다. DB 조회를 하나씩 차례로 기다리고 있었다. 서로 상관없는 조회인데 줄 세워서 하나 끝나면 다음 걸 불렀다. 한꺼번에 보내도록 바꿨다. 한 요청 안에서 같은 걸 여러 번 조회했다. 한 번 조회한 결과를 재사용하도록 바꿨다. 채팅을 뿌릴 때 받는 사람 수만큼 DB를 갱신했다. 10명이 있는 방에서 채팅 한 번에 DB 갱신이 10번. 이걸 한 번으로 묶었다. 특히 3번은 명령어 처리랑 직접 상관은 없어 보이지만, 같은 서버에서 DB 연결을 나눠 쓰다 보니 얘가 연결을 잡고 있으면 다른 요청이 기다리게 된다. 다시 재봤다 (그리고 실수 하나 😅) 조치를 끝내고 한참 뒤에, 같은 도구로 다시 재봤다. 가상 사용자 조치 전 명령어 p99 조치 후 명령어 p99 150명 5.5초 2.6초 200명 11.8초 1.2초 오 좋아졌다!! 하고 끝내려는데 숫자가 이상했다. 같은 인원인데 초당 요청 수가 5배 나 많이 나온 거다. (200명 기준 26건 → 135건) "서버가 5배 빨라졌다고??" 잠깐 설렜는데 그럴 리가 없다. 확인해보니 가상 사용자가 쉬는 시간을 다르게 줬다. 처음 잴 때는 1 15초, 다시 잴 때는 0.1 3초. 쉬는 시간이 짧으니 요청을 5배 많이 보낸 거였다. 그러니까 이건 같은 부하의 비교가 아니다. 다만 5배 더 무거운 부하에서 쟀는데도 더 빨랐다 는 뜻이라, 의도치 않게 의외의 성과를 확인했다! 여기서 배운 것: 측정할 때 준 옵션을 결과 파일에 같이 남겨야 한다. 기억은 믿을 게 못 된다. "좋아졌다"는 숫자가 나와도, 조건이 같았는지부터 의심해야 한다. 아직 남은 것 명령어 처리가 빨라지니까 다음 범인이 나왔다. 이번엔 이동 요청 이다. 150명부터 이동이 제일 느렸다 (p99 3.0초, 200명에서 3.8초) 서버 오류도 나왔다. 200명에서 전체 요청의 약 1.1% 처음 잴 때는 실패가 0건이었는데, 부하가 5배 무거워지니 드러난 거다. 명령어 처리 때 했던 것처럼 이동 요청 안의 조회를 하나씩 재보면서 좁혀갈 차례다. 병목은 하나 잡으면 끝나는 게 아니라, 다음 병목이 차례를 기다리고 있더라. 마무리 부하 테스트를 하기 전에는 "느리면 서버를 늘리면 되지" 정도로 생각했다. 팀장님께서 나중에 런칭할때는 서버를 늘릴거니깐 신경쓰지 말라고 하셨다..만, 해보고 나니 생각이 좀 바뀌었다. 평균은 범인을 숨겨준다. 종류별로 쪼개 봐야 한다. 느린 건 서버가 아니라 코드 한 곳 일 때가 많다. 재는 조건을 기록하지 않으면 내 숫자를 나도 못 믿는다. "우리 서버 몇 명까지 버텨요?" 라는 질문에 이제는 숫자로 답할 수 있다. 그리고 "어디가 먼저 막혀요?" 라는 질문에도 답할 수 있다. 이게 제일 뿌듯하다 👍👍👍
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
아하❓ (8) - 서버가 몇 명까지 버틸까?! 부하 테스트 도구를 직접 만들었다. 들어가며 회사에서 만들고 있는 서비스가 사내 테스트를 앞두고 있었다. 사용자가 직접 콘텐츠를 만들고, 여럿이 들어와서 실시간으로 같이 노는 서비스인데, 이번 테스트에는 기획자가 만든 게임 컨텐츠를 테스트 해보는 것이였다. 그런데 테스트 날짜가 다가올수록 계속 찜찜한 게 하나 있었다. "그래서 우리 서버, 몇 명까지 버티는데❓" 아무도 몰랐다. 나도 몰랐다. 백엔드가 나 혼자인데 내가 모르면 아무도 모르는 거다 😇 "대충 100명은 되지 않을까요?" 같은 말을 할 수는 없으니, 직접 재보기로 했다. 왜 도구를 직접 만들었나 🤔 처음엔 당연히 있는 도구를 쓰려고 했다. 그런데 우리 서비스는 요청 하나 보내고 끝나는 구조가…
Открыть источник