2009년 7월경 서버 프로그래머들간에 효과보다 복잡도 상승으로 인한 개고생이 심해서 기피한다는
'다수의 Logic Thread를 가진 서버'를 만들게 되었다.
현재 잘 돌아가고 있으며, 덕분에 성능과 컨텐츠 추가 속도 향상이라는 2마리를 토끼를 같이 잡을 수 있었다.
다만 이건 본인이 진행 중인 프로젝트 성격과 잘 맞아서 성공적으로 적용 가능했다고 생각한다.
현재 프로젝트에서도 유저 접속 및 DB관리 서버만 해당 모듈을 사용 중이고, 나머지 서버는 기존 모듈을
사용 중이다.
즉 모든 케이스에 아래와 같이 해서는 안 된다는 말이다.
다만 뒤에 나올 Singleton Locker와 DeadLock Checker는 타 프로젝트에서도 도움될 것 같다.
어쨌든 그 당시 푹 빠져서 재미있게 설계했었기 때문에 대략적인 상항과 구조에 대해 기록을 남긴다.
-------------------------------------------------------------------------------------------------
현재 많은 게임 서버들이 멀티 쓰레드기반으로 서버를 만든다.
본인이 진행 중인 프로젝트도 IOCP를 활용한 게임 서버를 만들었다.
위에 보이는 바와 같이 IOCP를 활용하여 다수의 IO Thread를 가지고 있었으며, 다수의 DB Thread를 가지고,
단 하나의 Logic Thread를 가지고 있다.
그리고 Thread간에는 메세지를 통해 통신을 하였다.
일단 위와 같은 구조로 2차 CBT까지 잘 운영하였다.
그러나 한가지 문제점이 생겼다.
바로 Logic과 DB Thread가 분리되어 있어서 하나의 메세지를 처리할 때 atomic하게 처리하기가 힘들다.
예를 들면 우편을 통해 아이템을 편지로 전송하는 경우 Logic Thread에서 해당 아이템을 임시 공간으로
이동시킨 후에 DB Thread에 전송을 요청한다. DB Thread에서 성공&실패 메세지가 도착하면 그에 맞는
처리를 해주어야된다. 즉 아래와 같이 처리한다.
이 때 Logic Thread에서 DB Thread로 메세지 전송 후 결과를 받기까지 타이밍이 참 애매하다.
만약 그 때 첨부한 아이템의 위치로 타 아이템을 이동시키는 메세지가 도착한다면?
만약 결과가 실패라면 첨부하려던 아이템을 원래 위치로 이동시켜 주어야되므로,
위와 같은 메세지는 허용하지 말아야된다.
그래서 보통 첨부된 아이템 슬롯에 Lock을 건다.
그렇게되면 인벤토리 Lock관리를 잘 해주어야 된다.
위와 같이 심플한 경우도 있지만, 경매장이나 개인 거래처럼 DB에 바로 처리해야 될 정도로 중요하면서
Logic Thread와 DB Thread간에 시간 갭을 매우기위해서 다수의 Lock이 존재하는 경우
그 관리가 쉽지만은 않다.
결국 복잡도 증가는 개발일정 연장으로 이어지고, 버그에 대한 위험성때문에 서버 프로그래머가 벌벌 떨어야되는 상황까지 되었다.
( 사실 이 때 내 부사수가 해당 문제때문에 일정이 계속 지연이 되었다. 그리고 당장 해당 문제를 해결한다고 하더라도
추후에 또 비슷한 작업을 해야될 때 또 다시 개고생해야된다는 생각에 아예 다시 이런 문제가 발생하지 않는 구조였으면 좋겠다...라는 생각에서 이 일을 시작하게 되었다. )
그래서 Logic Thread에서 DB처리를 동시에 할 수 있다면 어떨까라는 생각을 해보았다.
그렇다면 Logic Thread에서 하나의 요청( 아이템을 우편에 담아서 보낸다 )을 atomic하게 처리할 수 있으며,
거기다가 코드에서 Transaction을 걸면 더욱 복잡한 메세지도 atomic하게 처리할 수 있을 것 같았다.
대충 위와 같은 구조를 생각해보았다...
그런데 위에 구조는 문제가 많다. 이렇게 되면 거의 IO를 제외한 1 Thread서버가 되므로 성능이 매우 떨어질것이
불을 보듯 뻔했다.
그래서 아래와 같이 구조를 만들어 보았다.
기존에 Logic thread에 이름을 Main Thread로 변경하고, 다수의 유저와 하나의 DB Object를 가진 다수의 Logic
Thread를 만들었다.
일단 앞선 구조에 비하면 다수의 Logic Thread를 통해 성능 문제는 해결되었다.
그리고 User 메세지를 처리하는 부분에서 DB Object에 바로 접근이 가능하므로, 프로그래밍하기는
매우 쉬워졌다.
단! 그 모든걸 베이스로 하는 기반 코드에 복잡도가 상승하게 되었다.
베이스 라이브러리의 복잡도가 어느정도 상승하기는 했지만 어짜피 메세지기반으로 Thread간에 통신하므로
다수의 Thread간 통신은 크게 문제가 될만한 요소는 없었다.
만약 이대로 하부단이 잘 작동만 한다면, 앞으로 상부단 작업( 컨텐츠 추가 ) 속도는 휠씬 빨라질 것 같았다.
그러나 세상 일이 그렇게 호락호락하지만은 않듯...설계를 하다가 복병을 하나 만났다. 그 것은 바로 '싱글톤'이었다.
'다수의 Logic Thread를 가진 서버'를 만들게 되었다.
현재 잘 돌아가고 있으며, 덕분에 성능과 컨텐츠 추가 속도 향상이라는 2마리를 토끼를 같이 잡을 수 있었다.
다만 이건 본인이 진행 중인 프로젝트 성격과 잘 맞아서 성공적으로 적용 가능했다고 생각한다.
현재 프로젝트에서도 유저 접속 및 DB관리 서버만 해당 모듈을 사용 중이고, 나머지 서버는 기존 모듈을
사용 중이다.
즉 모든 케이스에 아래와 같이 해서는 안 된다는 말이다.
다만 뒤에 나올 Singleton Locker와 DeadLock Checker는 타 프로젝트에서도 도움될 것 같다.
어쨌든 그 당시 푹 빠져서 재미있게 설계했었기 때문에 대략적인 상항과 구조에 대해 기록을 남긴다.
-------------------------------------------------------------------------------------------------
현재 많은 게임 서버들이 멀티 쓰레드기반으로 서버를 만든다.
본인이 진행 중인 프로젝트도 IOCP를 활용한 게임 서버를 만들었다.
위에 보이는 바와 같이 IOCP를 활용하여 다수의 IO Thread를 가지고 있었으며, 다수의 DB Thread를 가지고,
단 하나의 Logic Thread를 가지고 있다.
그리고 Thread간에는 메세지를 통해 통신을 하였다.
일단 위와 같은 구조로 2차 CBT까지 잘 운영하였다.
그러나 한가지 문제점이 생겼다.
바로 Logic과 DB Thread가 분리되어 있어서 하나의 메세지를 처리할 때 atomic하게 처리하기가 힘들다.
예를 들면 우편을 통해 아이템을 편지로 전송하는 경우 Logic Thread에서 해당 아이템을 임시 공간으로
이동시킨 후에 DB Thread에 전송을 요청한다. DB Thread에서 성공&실패 메세지가 도착하면 그에 맞는
처리를 해주어야된다. 즉 아래와 같이 처리한다.
이 때 Logic Thread에서 DB Thread로 메세지 전송 후 결과를 받기까지 타이밍이 참 애매하다.
만약 그 때 첨부한 아이템의 위치로 타 아이템을 이동시키는 메세지가 도착한다면?
만약 결과가 실패라면 첨부하려던 아이템을 원래 위치로 이동시켜 주어야되므로,
위와 같은 메세지는 허용하지 말아야된다.
그래서 보통 첨부된 아이템 슬롯에 Lock을 건다.
그렇게되면 인벤토리 Lock관리를 잘 해주어야 된다.
위와 같이 심플한 경우도 있지만, 경매장이나 개인 거래처럼 DB에 바로 처리해야 될 정도로 중요하면서
Logic Thread와 DB Thread간에 시간 갭을 매우기위해서 다수의 Lock이 존재하는 경우
그 관리가 쉽지만은 않다.
결국 복잡도 증가는 개발일정 연장으로 이어지고, 버그에 대한 위험성때문에 서버 프로그래머가 벌벌 떨어야되는 상황까지 되었다.
( 사실 이 때 내 부사수가 해당 문제때문에 일정이 계속 지연이 되었다. 그리고 당장 해당 문제를 해결한다고 하더라도
추후에 또 비슷한 작업을 해야될 때 또 다시 개고생해야된다는 생각에 아예 다시 이런 문제가 발생하지 않는 구조였으면 좋겠다...라는 생각에서 이 일을 시작하게 되었다. )
그래서 Logic Thread에서 DB처리를 동시에 할 수 있다면 어떨까라는 생각을 해보았다.
그렇다면 Logic Thread에서 하나의 요청( 아이템을 우편에 담아서 보낸다 )을 atomic하게 처리할 수 있으며,
거기다가 코드에서 Transaction을 걸면 더욱 복잡한 메세지도 atomic하게 처리할 수 있을 것 같았다.
대충 위와 같은 구조를 생각해보았다...
그런데 위에 구조는 문제가 많다. 이렇게 되면 거의 IO를 제외한 1 Thread서버가 되므로 성능이 매우 떨어질것이
불을 보듯 뻔했다.
그래서 아래와 같이 구조를 만들어 보았다.
기존에 Logic thread에 이름을 Main Thread로 변경하고, 다수의 유저와 하나의 DB Object를 가진 다수의 Logic
Thread를 만들었다.
일단 앞선 구조에 비하면 다수의 Logic Thread를 통해 성능 문제는 해결되었다.
그리고 User 메세지를 처리하는 부분에서 DB Object에 바로 접근이 가능하므로, 프로그래밍하기는
매우 쉬워졌다.
단! 그 모든걸 베이스로 하는 기반 코드에 복잡도가 상승하게 되었다.
베이스 라이브러리의 복잡도가 어느정도 상승하기는 했지만 어짜피 메세지기반으로 Thread간에 통신하므로
다수의 Thread간 통신은 크게 문제가 될만한 요소는 없었다.
만약 이대로 하부단이 잘 작동만 한다면, 앞으로 상부단 작업( 컨텐츠 추가 ) 속도는 휠씬 빨라질 것 같았다.
그러나 세상 일이 그렇게 호락호락하지만은 않듯...설계를 하다가 복병을 하나 만났다. 그 것은 바로 '싱글톤'이었다.
'하루하루 > 개발' 카테고리의 다른 글
Multi-Logic-Threaded GameServer 3부 (0) | 2010.05.17 |
---|---|
Multi-Logic-Threaded GameServer 2부 (0) | 2010.04.12 |
LFH (Low Fragmentation Heap) (0) | 2009.04.12 |
Mock 객체 활용법 (1) | 2008.04.29 |