300ms를 바꾸는 함수 호출 순서

| 2021-12-29

안녕하세요, 넷마블 QA실 성능QA팀 남태희입니다.

이번에 공유할 내용은 온프레미스(On-Premise)에서 클라우드로 인프라를 이전하면서 발생한 서버 성능 저하를 개선한 사례입니다.

성능 저하 현상 확인

서버에서 클라이언트 요청을 처리할 때 요청량에 따라 부하가 발생합니다. 부하가 발생하기 시작하는 초기에는 초당 200건까지 소화를 했으나, 부하가 꾸준히 유지되면서 초당 3건만 처리하는 정도로 성능이 저하되는 현상이 발생했습니다.

서버에 부하가 계속되면서 요청 처리량이 떨어지는 현상

와이어샤크(Wireshark)로 패킷이 클라이언트에서 서버로 전달된 상태까지는 확인할 수 있었습니다. 하지만, 서버 애플리케이션에서 ‘AcceptCallback()’에 로그를 남겨보니, 클라이언트가 요청한 양만큼 패킷을 세지(Counting) 못하고 있었습니다. 

기존에 사용 중이던 온프레미스 환경에서는 400TPS까지 특이사항이 발생하지 않던 기능이었기 때문에, 상세 원인 분석이 필요했습니다.

함수 호출 순서로 인한 성능 저하

서버가 패킷을 처리하는 모든 구간에 처리 시간을 남기는 로그를 추가했습니다.

로그와 Try-Catch 구문으로 구간별 상황을 확인하던 중, 두 함수가 눈에 들어왔습니다. ‘BeginAccept()’ 함수와 ‘ConnectionHandler()’ 함수였습니다. ‘BeginAccept()’ 함수는 클라이언트에서 들어오는 연결(Connect) 요청을 대기하는 함수이며, ‘ConnectionHandler()’ 함수는 유저 커넥션을 초기화하는 함수입니다.

ConnectionHandler() 함수에서 300ms를 소모 중

서버가 패킷을 받아 처리하는 과정에서 ‘BeginAccept()’ 함수보다 먼저 호출하는 ‘ConnectionHandler()’ 함수에서 300ms씩 시간을 소요하고 있었습니다. ‘ConnectionHandler()’ 함수 호출이 지연되면서, 그 뒤에 실행하는 ‘BeginAccept()’ 함수도 같이 지연됐고, 그대로 클라이언트 연결도 지연되고 있었습니다. 

결국 ‘ConnectionHandler()’ 함수가 소모한 300ms 이후에 ‘BeginAccept()’ 함수가 1회씩 호출되므로, ‘BeginAccept()’ 함수는 1초(1000ms)당 약 3건 정도만 연결 요청을 받을 수밖에 없었습니다.

함수 호출 순서 변경

‘EndAccept()’ 함수 직후에 바로 ‘BeginAccept()’ 함수를 호출해, 지연이 발생하는 ‘ConnectionHandler()’ 함수보다 앞서서 ‘BeginAccept()’ 함수를 실행하도록 순서를 변경했습니다.

함수 호출 순서 변경
함수 호출 순서 변경 예시 코드

위 예시 코드처럼, 40번째 줄에서 ‘EndAccept()’ 함수를 호출한 후에 46번째 줄에 있는 ‘listener.Start()’ 함수 내부에서 ‘BeginAccept()’ 함수(28~30번째 줄)를 호출하도록 수정했습니다. 47번째 줄에 있는 ‘ConnectionHandler()’ 함수 내부에는 ‘clientSocket’을 초기화하고 ‘BeginReceive()’ 함수를 호출하도록 처리했습니다. (그래서 46번째 줄과 47번째 줄 순서가 뒤바뀌면, 성능 저하 현상이 다시 생깁니다.)

아직 밝혀지지 않은 정확한 원인

예시 코드처럼 함수 호출 순서를 변경한 후에는 클라이언트 요청양 250TPS 정도로 부하가 장시간 유지되더라도 특이사항이 발생하지 않았습니다.

하지만 온프레미스 환경에서는 ‘ConnectionHandler()’ 함수 처리 시간에서 특별한 지연 현상이 없었음에도 불구하고, 클라우드 환경에서는 왜 300ms를 소요했는지에 대해 정확한 원인을 아직 밝히지 못했습니다. 

서버 성능 저하 현상 자체는 해소했지만, 혹시라도 이 글을 읽으시는 분 중에 이 원인을 아시는 분이 계신다면 가르침을 부탁드립니다.