한줄요약
웹서버는 상태가 없이 개발되기 때문입니다.
자세한 내용
정확히는 웹 통신방식과, 소켓(웹도 TCP지만 일단은 이렇게 구분하겠습니다)통신방식의 차이 때문이라고 생각해요.
TCP소켓통신을 하는 서버는 상대적으로 빠른 응답성이 중요한 서버이고, 이러한 서버에서는 DB에 다녀오는 시간을 줄이고자 메모리에 유저의 상태를 들고있도록 개발됩니다. 만약 이러한 서버를 무지성으로 수평확장 하게 된다면 어떻게 될까요?
1. 로드벨런서가 서버A로 유저의 돈 10원을 증가시키는 요청을 보낸다
2. 서버A에 유저데이터가 없다면 DB에서 읽어와 메모리에 들고있고, 유저의 돈을 10원 증가시킨다.
3. 로드벨런서가 서버B로 유저의 돈 10원을 증가시키는 요청을 보낸다
4. 서버B에 유저데이터가 없다면 DB에서 읽어와 메모리에 들고있고, 유저의 돈을 10원 증가시킨다.
4의 시점에서 서버B가 유저 데이터를 디비에서 읽어갈 때, 서버A는 10원 증가한 값을 DB에 기록했을까요?
만약 하도록 만들어져 있다면 TCP서버의 장점인 빠른 응답성을 많이 손해보고 있는 서버이며, 즉시 기록하지 않았다면 데이터 레이스가 발생합니다. 물론 즉시 기록하도록 만들어졌다고 해도 데이터 레이스가 발생합니다.
방법이 없진 않습니다. 서버A와 서버B가 서로 통신으 하면서 유저데이터에 소유권을 부여하거나, 카피해 들고있고 변경이 일어날때마다 알려주면 되거든요.
이렇게 개발하면 TCP서버의 빠른 으답성(DB에 즉시 다녀오지 않음)과 수평확장의 장점을 누릴수 있는것 같습니다.
그러나 서버 2대로도 과부하가 걸린다면? 3대여야 할 거고, 이 메모리에 있는 값을 완벽하게 같은 타이밍에 조작하는것은 대단히 어려운 이슈입니다. (CAP - 일관성, 가용성, 분할내성 참조)
웹서버로 개발되는 게임서버는 이러한 일관성 문제를 DB에 맡깁니다.
1. 로드벨런서가 서버A로 유저의 돈 10원을 증가시키는 요청을 보낸다
2. 서버A에는 유저데이터(상태)가 없으니 당연히 DB에서 값을 읽어와 처리하고 즉시 DB에 기록한다.
3. 로드벨런서가 서버B로 유저의 돈 10원을 증가시키는 요청을 보낸다
4. 서버B에는 유저데이터(상태)가 없으니 당연히 DB에서 값을 읽어와 처리하고 즉시 DB에 기록한다.
한번의 요청이 올 때마다 DB에서 값을 읽어와 처리한 뒤, Db에 값을 기록하고, 값을 메모리에 들고있지 않습니다.
이것을 상태 없는 서버 (스테이트리스)라고 표현합니다.
이러한 방식은 TCP에 비해 낭비가 심하지만 서버가 상태를 들고있지 않게되면서 수평확장에 유리해집니다.
이러한 특성 덕분에 유저들간에 상호작용이 적고, 즉각적인 응답이 덜 중요한 게임서버에서 웹서버를 선택하는 경우가 많습니다.
FPS에서 1초는 못참지만, 모바일 웹서버 게임에서의 1초는 그냥 와이파이가 좀 안터지나보다 하고 기다릴만 하니까요.