컨테이너가 쓰는 자원 스펙을 얼마로 해야 적절한지 밝혀내는 거고,
이 적절한 스펙을 사용했을 때 몇명의 유저를 받을 수 있는 지
241209 TIl 에 관련 내용이 있었던 덕에 비교적 쉽게 해결할 수 있었다.
onData보다 onEnd가 우선처리 되고 있는 것으로 보인다는 동혁님 말씀.
제이미터 작동이 중지되어 쓰레드가 모두 꺼진 뒤에도 메모리는 어느정도 용량을 차지하면서 돌아가고 있었다. (아이들 시 60 언저리)
그래서 chat GPT에게 물어 JMeter의 테스트 스크립트를 다음과 같이 수정했다.
import java.io.OutputStream;
import java.io.InputStream;
import java.net.Socket;
import org.apache.jmeter.util.JMeterUtils;
String serverName = "127.0.0.1";
int port = 6666;
byte[] loginData = {
0x00, 0x03, 0x05, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x1d, 0x1a, 0x1b, 0x0a, 0x11, 0x6c, 0x69, 0x68, 0x61, 0x32, 0x37, 0x40, 0x6c, 0x69, 0x68, 0x61, 0x32,
0x37, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x06, 0x6c, 0x69, 0x68, 0x61, 0x32, 0x37
};
Socket socket = null;
OutputStream out = null;
InputStream in = null;
try {
// 소켓 초기화
socket = new Socket(serverName, port);
out = socket.getOutputStream();
in = socket.getInputStream();
// 데이터 전송
out.write(loginData);
out.flush();
// 서버 응답 수신
byte[] responseBuffer = new byte[1024]; // 서버 응답을 저장할 버퍼
int bytesRead = in.read(responseBuffer); // 서버로부터 데이터 읽기
if (bytesRead > 0) {
String response = new String(responseBuffer, 0, bytesRead, "UTF-8");
System.out.println("서버 응답: " + response); // 응답 출력
} else {
System.out.println("서버로부터 응답이 없습니다.");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 리소스 정리
try {
if (out != null)
out.close();
if (in != null)
in.close();
if (socket != null)
socket.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
수정된 스크립트로 테스트하니 이렇게 나왔다. 이게 현실적인 그래프 같긴 하다.
이렇게 했더니 응답시간 디스트리뷰션 그래프가 정말 화려하게 나왔다. 오호
초당 트랜잭션도 거의 일정한 느낌.
그렇게 나온 종합 그래프.
-> CPU 용량을 2배로 주니 거짓말처럼 처리속도가 절반이 되었다. 오호!
한개의 로비서버에 CPU 사용량을 10%으로 했을땐 21000ms까지 뛰었는데, 20%로 하니 11000ms까지 떨어졌다.
이를 이용해서 분산서버 테스트를 동혁님과 진행해보았다. 10% - 128MB로 고정한 두 개의 로비서버에 패킷 부하를 걸어보았다.
-> 분산서버에 대해 유의미한 결과가 나왔다. 한개의 로비서버가 CPU를 20% 쓰는 것과 비슷한 응답시간을 보였다.
-> 다만 Node.js 자체가 먹는 메모리가 꽤 있어서 이 부분을 손실로 감당해야 할듯.
도커의 상태
JMeter 응답시간의 상태
쓰레드 50 - 램프업 주기 60초 설정에 CPU 20%로 응답시간이 11000ms 정도 나왔으니, 쓰레드를 두배로 늘리면 응답시간도 최소 두배 이상 걸릴 것으로 보였다.
그래서 쓰레드 100 - 램프업 주기 100초 설정으로, 한개의 서버에 CPU를 40%로 주고 메모리는 128MB로 그대로 두니(그전에도 메모리를 거의 쓰지 않았음) 거의 비슷한 결과가 출력되었다.
로비가 지속적으로 사용하는 패킷. ( 몇명이나 로비에 대기상태로 있을 수 있는지)
1. 방 리스트 업데이트 (5초간격)
a. → 요청 빈도 확인하고 접속자 몇명 이상이 로비에 대기 중일 때 터지는지 확인해야함.
b. 100명이 대기할 수 있게 할 것이고, Response Time은 2초 이하로
c. 그것에 필요한 메모리, cpu 파워를 알아내야함.
d. 요청 → 응답 받음 → 5초 대기 → 다시 요청 하게 코드를 짤 것.
<Buffer 00 07 05 31 2e 30 2e 30 00 00 00 04 00 00 00 02 3a 00>
0x00, 0x07, 0x05, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x3a, 0x00
CPU 40%를 줬는데 5%도 쓰지 않았다 - 대신 응답시간 그래프가 꽤 시간을 잡아먹는 양상으로 나왔다.
-> 다만 응답시간의 경우 지연을 5초씩 줘서 5초에 한번씩만 작동하도록 설정했으니까, +5000ms가 된 값으로 보면 될듯.
-> 이걸 고려하면 응답시간은 거의 없는 셈이니 2초 내로 끊고싶다면 7000ms 안으로 들어오면 된다.
100/100 - CPU 40%, 128MB,
100/100, CPU 10%, 128MB 설정일 때 로그인 로직을 넣으면 이렇게 나온다.
import java.io.OutputStream;
import java.io.InputStream;
import java.net.Socket;
// 서버 정보
String serverName = "127.0.0.1";
int port = 6666;
// 송신 데이터
//byte[] loginData = { 0x00, 0x03, 0x05, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
// 0x1d, 0x1a, 0x1b, 0x0a, 0x11, 0x6c, 0x69, 0x68, 0x61, 0x32, 0x37, 0x40, 0x6c, 0x69, 0x68, 0x61, 0x32,
// 0x37, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x06, 0x6c, 0x69, 0x68, 0x61, 0x32, 0x37 };
byte[] roomListData = new byte[] {
0x00, 0x07, 0x05, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x02, 0x3a, 0x00
};
Socket socket = null;
OutputStream out = null;
InputStream in = null;
try {
// 소켓 초기화 (한 번만 연결)
log.info("Connecting to server: " + serverName + ":" + port);
socket = new Socket(serverName, port);
out = socket.getOutputStream();
in = socket.getInputStream();
// 100초 동안 실행 (5초 간격으로 20번 반복)
// for (int i = 0; i < 20; i++) {
try {
// 데이터 전송
log.info("Sending data: " + java.util.Arrays.toString(roomListData));
// out.write(loginData);
out.write(roomListData);
out.flush();
// 서버 응답 수신
byte[] responseBuffer = new byte[1024];
int bytesRead = in.read(responseBuffer);
if (bytesRead > 0) {
String response = new String(responseBuffer, 0, bytesRead, "UTF-8");
log.info("Server response: " + response);
} else {
log.warn("No response received from the server.");
}
} catch (Exception e) {
log.error("Error occurred during data transmission: ", e);
}
// 5초 대기
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
log.error("Thread interrupted during sleep.", e);
}
// }
} catch (Exception e) {
log.error("Error occurred during socket initialization: ", e);
}
// finally {
// // 리소스 정리 (최종적으로 소켓 닫기)
// try {
// if (out != null) out.close();
// if (in != null) in.close();
// if (socket != null) socket.close();
// log.info("Socket closed.");
// } catch (Exception ex) {
// log.error("Error occurred while closing resources: ", ex);
// }
//}
위와 같은 테스트 스크립트로 진행해서, socket.close()를 하지 않고 열어둔 채로 테스트를 진행했다.
-> 쓰레드 수명주기를 걸어줘서, 쓰레드가 꺼짐과 같이 테스트가 종료되도록 함. 이때 아마 소켓도 같이 닫혔을 것이다.