이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.
소켓을 한지도 오래되고, 깊게 파지도 않아서.. Blocking / NonBlocking 을 이제서야 찾아봤습니다. 그래서 강의를 써봅니다.
초보자들도 개념적으로 이해할 수 있게 쓰려고 했기 때문에 논블록킹에 대한 설명은 후반에 나옵니다.
이 강의는 TCP/IP의 간단한 채팅서버라도 만들어봤다는 기준으로 진행되게 됩니다.
Blocking : 블로킹 이란 무엇인가?
블록킹이란 말그대로 블록이 되는것을 말합니다. accept / read등의 함수는 결과가 올 때까지 그자리에서 멈춰있을때 블록킹 됬다고 합니다.
멈춰잇으면 무슨 문제가 생기는가?
블록상태에 들어섰다면, 다른일을 하지 못 한다는 겁니다. 즉 “홍길동”이 채팅방에 들어갔다면, 홍길동이 연결된 상태이기 때문에 추가로 제가 연결할 수 없게됩니다.
어떻게 해결하는가?
셀릭트나 쓰레드등 여러가지 멀티 처리를 할 수 있는 방법이 있습니다. 셀릭트는 블록킹인지 논블록킹인지 필자도 잘 모르겠습니다… 주변사람에게 물어봐도 각기 대답이 다르더군요. 그럼 쓰레드로 예를들어보겠습니다. 여러분도 아주 잘 알다시피…(너무기초강의를 하는 느낌이드네요.. 하하..;;) 엑셉을 만나면 쓰레드를 생성하게되고 생성된 쓰레드에 유저를 할당하게됩니다. 그렇게하여 한 유저당 1개의 쓰레드를 주게되고 채팅은 문제없이 돌아가게 됩니다.
해결?
단순한 학교과제였다면 여기서 해결되었겠지만.. 채팅을 사용하는 사용자가 천명이 넘어가고 만명이 넘어가게 되면 또하나의 문제가 생깁니다. 바로 쓰레드가 너무 많아져서 생기는 문제들이 발생하게 됩니다.
쓰레드가 과도하게 생성되서 나타나는 문제?
사실 얼마전까지도 어차피 메모리가 많은데.. 쓰레드 많아도 상관없지 않나… 라고 생각하고 또 남발하다가.. 면접때 블록킹과 논블록키에 대한 질문이 있어서 다시 찾아보게되었습니다. 그동안 너무 웹쪽에만 치중하고있던지라.. 소켓을 참 오랜만에 해봅니다…. 쓰레드가 많아지는건 메모리외에도 CPU 컨텍스트 스위칭과 메모리 할당등의 문제가 발생합니다. 예를들어 한사람당 하나의 쓰레드를 주었을때 동시에 만명이 접속하면 어떻게될까요? (프로그램이 죽지않는다고 가정) CPU는 천개의 쓰레드를 새로만들고 시분할하여 돌아가면서 컨텍스트 스위칭을 합니다. 즉 자신의 시간을 천개로 쪼개서 여기저기 본다는건데 대부분은 “그냥 연결중” 상태입니다.
쓰레드 문제 1 : 메모리 재할당
새로은 쓰레드가 만들어지면서 메모리는 재할당 되게 됩니다. 자바, C#를 배운 분이시라면 스트링빌더, 스트링버퍼에 대해서 들어보셨을 겁니다.
String a = "안녕";
a += "하세요.";
a += "반값습니다.";
코드상으로는 그냥 스트링문장뒤에 “하세요.” “반값습니다.” 를 더하는거 같지만. 두 메모리 공간을 합친 것 만큼 메모리를 재할당하여 새로운 메모리 주소를 a에 주도록 되어있습니다. (사실 저정도 코드야 자바나 C#의 빌드과정에서 자동 교정되지만… 그렇지 않다고 합시다.) 때문에 빌더나 버퍼의 경우 어느정도 메모리를 추가로 할당해두고 append 를 통해 메모리 뒷 공간에 쓰기를 시도합니다. 이제 조금 난이도를 올려서 웹의 DB접근 용어중 커넥션 풀이라는게 있습니다. 커넥션풀이란 DB에 접속하기위해 DB접속객체를 만들고 open하는대까지의 시간이 오래 걸리다보니 close시 닫지않고 거대한 풀(수영장 : 메모리공간)에 반환합니다. 또 새롭게 DB에 접속하기위해 요청한다면 먼저 풀을 살펴본뒤 반환된 커넥션이 있으면 해당 커넥션을 건내줌으로써 커넥션을 재활용하게 됩니다. 마찬가지로 쓰레드는 쓰레드 풀이 있습니다. 윈도우같은경우는 IOCP가 있습니다. (자바는 모르겠습니다. 쓰레드풀을 만들어쓰나…;;; 사실 쓰레드풀은 손쉽게 만들어 쓸수있으나 커널단에 있는게 더 빠름으로 사용합니다.) 결론적으로 쓰레드를 만들어놓고 재활용을 해서 쓰레드를 새로 생성하는 자원을 줄여나가는 방법입니다. 이때 만들 쓰레드의 갯수는 CPU의 코어 갯수에 맞추는 경우까지 있다고 하는데.. 실제 테스트를 돌려보면서 적당한 선에서 맞추시면될거 같습니다.
쓰레드 문제 2 : 블락킹 -> 논블락킹
예를들어 쓰레드를 8개 생성해서 돌려쓰고있는데 블락킹이 걸려있다면 더 이상 사용자가 들어올 수 없게됩니다. 또 생성된 8개같은경우 채팅서버기준으로 실제 데이터를 보내는 구간보단 블락킹되어있는 구간이 큽니다. 때문에 블락킹이 걸리지 않도록 받는데이터가 없으면 다음사용자에게 앙도하는 겁니다. 이렇게해서 위 문제들을 다 해결할 수 있습니다.
쓰레드 풀링 - 논블락킹은 무조건 좋은가?
대량의 커넥션을 처리하는 서버라고하면 무조건 예라고 대답할 수 있습니다. 하지만 이 구조에도 문제점은 있습니다.
- 프로그래밍의 가독성이 약간 떨어진다. -> 작성 및 유지보수 비용이 증가한다.
- 병목현상이 생길 위험이 늘어난다. -> 작성 및 유지보수 비용이 증가한다. 때문에 어떤상황에서도 쓰레드가 많이 늘어나지 않을 프로그램에는 블락킹 + 쓰레드를 사용하여 프로그래밍을 해도 좋다고 생각합니다.
참고자료
윈도우 : IOCP 리눅스 : epoll 공통 : 셀릭트 자바 : NIO
추신. IOCP, epoll, NIO에 대해서도 좀 써야할거같은데… 시간이 있을지 모르겠네요. 빨리 가리사니를 좀 보충하고 써보도록 하겠습니다.