[스크랩]네트워크 서버 모델
출처 : http://blog.empas.com/stoneshim/1237
Network Server Models
뭐 확실하게 이런 저런 모델이 있다고 정해진건 없지만... 대표적인 모델들을 정리해본다.
- fork process per connection
- spawn thread per connection
- preforked process( process pool )
- prespawned thread( thread pool )
- preforked process + thread pool
- asynchronous I/O
1. fork process per connection
-. 가장 고전적인 UNIX Network Server Model.
-. Parent가 socket(), bind(), listen(), 후 connection 요청을 wait하다가
connection 요청이 들어오면 accept()후 fork() 하여(fork()후 아예 exec 하기도 한다)
parent는 connected FD를 close 하고 다시 connection요청을 wait하며,
child는 listening FD를 close 한 후 connection의 요청에 따라 job을 수행한다.
적절한 시점( client가 소켓 close 또는 서버가 request전송후 )child는 connection을 close 하고 죽는다.
-. 구현이 간단하고, resource의 미반환 등 어느정도의 실수가 용납된다.
-. 요청이 몰리면 곤란한다.
-. parent에서 SIGCHLD를 처리해 주어야 좀비 발생을 막을 수 있다.
2. spawn thread per connection
-. process를 fork() 하는 부담을 덜기 위한 방법.
-. parent(편의상 이렇게 부름)가 socket(), bind(), listen() 후 연결요청을 대기하다
연결요청이 들어오면 pthread_create()로 thread 생성 후 관심 fd에서 삭제후 연결대기.
child thread는 요청을 처리한 후 pthread_exit() 으로 thread 종료
-. 구현이 간단하며, fork() 모델에 비해 빠르고 resource의 부담이 적다.
-. connection 당 thread 하나이므로 connection이 아주 많아지면 곤란하다.
특히 kernel 2.4.x 까지의 thread모델은 clone() 을 이용한 방식이므로 더욱 곤란하며, kernel 2.6.x 에서부터 NPTL을 지원하여 조금 나아진 상태임.(사실상 2.4.20에서부터 지원 시작했음)
-. thread 가 죽어도 동적으로 할당한 메모리가 반환되지 않으므로 memory leak을 조심해야 함.
3. preforked process 모델
-. apache 1.x 까지의 모델.
-. parent는 socket(), bind(), listen() 후 일정 개수의 process를 fork() 한다.
-. fork()된 child들은 경쟁적으로 accept() 한다.
대부분의 accept()가 mutual exclusion을 지원하므로, 사실상 accept() 이전의 locking 처리는 불필요할 수 있지만, 혹시 모르므로 대부분 locking 처리를 한다.
apache의 경우는 OS의 종류에 따라 locking 방식을 달리하며, LINUX의 경우 record locking 을 사용한다.
-. accept()에 성공한 child는 요청을 처리하며, 적절한 시점( client가 연결 종료)에 사용한 resource를 반환하고 다시 accept()(혹은 accept()이전의 locking) 를 시도한다.
-. parent는 watch dog의 역할, 필요시 child를 더 fork()하는 역할 등을 수행한다.
-. accept() 이후에 fork()를 하는 모델에 비해 월등히 속도가 빠르다.
-. 1번 2번 모델에 비해 조금 복잡하나 구현이 간단한 편이다.
-. 일정한 job 수행 개수에 도달한 child는 스스로 죽기도 하여 memory leak이 있어도 어느정도 안심할 수 있다.
-. 프로세스 기반이므로 많은 수의 동시 connection은 견딜 수 없다.
4. prespawned thread ( thread pool )
-. preforked process 모델대신 thread를 사용.
-. parent(thread는 parent, child 개념이 없다고 봐야 하지만 편의상 이렇게 부르기로 한다.) 는 socket(), bind(), listen() 후 일정 개수의 threads를 spawn(pthread_create())한다.
-. spawn된 child thread들(혹은 parent까지 함께)은 경쟁적으로 accept()한다.
accept() 이전의 인위적인 critical section이 필요하다.( 대부분 mutex로 구현 )
-. parent thread는 child thread와 함께 accept() 경합을 벌이거나, watch dog, signal handling 등의 작업을 할 수 있다.
-. thread 기반이므로 preforked 모델 보다 더 많은 동시 connection을 견딜 수 있다.
-. stack size 등을 조심해야 한다.( 필요시 더 늘려야... )
-. thread 기반이므로 memory leak이 조금이라도 있으면 치명적이다.
-. 이 모델 역시 하나의 connection을 하나의 thread가 처리하므로 blocking socket을 사용해도 무방하다.
* 위의 방법은 worker가 여러개 있는 형태로, 일반적인 boss-worker 모델이 아님.
-. boss-worker 모델의 경우는 몇몇 혹은 하나의 boss가 I/O multiplexing(select, poll, epoll, kqueue등) 으로 network I/O(read만 혹은 read/write 모두)를 수행하며, client로부터의 reading이 complete된 packet을 worker queue에 쌓고, worker들은 queue에서 packet을 하나씩 뽑아 job을 수행하는 형태가 보통이다.
-. worker queue는 모든 worker가 공유하는 경우도 있고, worker끼리의 경합을 줄이기 위해 worker 별로 queue를 별도로 두기도 한다. worker별로 둔다면 boss가 어떤 worker를 선택할지를 결정하는 알고리듬이 필요하다.
-. boss가 read/write를 모두 수행하는 경우라면 conection list를 관리하기 위한 자료구조, block된 session의 나중처리를 위한 Statemachine이 필요하며, worker가 boss에게 response를 전달할 queue가 별도로 필요할 수 있다.
-. boss가 read만 하기도 하며, 이 경우 boss는 read만, worker는 job수행 + write 의 작업을 하게 되며, boss가 worker에게 job을 넘길때 일정한 context(connection정보)를 함께 건네 주어야 한다. 이 경우도 역시 block된 session은 나중에 처리해야 하므로 connection list와 Application 프로토콜 별 StateMachine이 필요하다.
-. 이 모델은 한정된 수의 boss가 여러 connection을 관리해야 하므로 nonblocking socket을 사용해야만 하며, 앞에서 말했듯이 block된 session을 위한 별도의 자료구조가 필요하다.
5. preforked process + thread pool
-. apache 2.x 버전에서 사용한다고 함( 확실하지 않음 )
-. preforked 된 child 들 각각이 thread pool을 가지고 있어서 accept() 후의 job은 thread pool에 존재하는 하나의 thread가 처리하며, child의 main thread는 곧바로 accept()로 들어가는 형태.
-. 일정 개수의 이상의 작업을 마친 child는 스스로 죽을 수 있으므로 memory leak이 어느정도 허용되는 장점이 있다.
-. 안써봤음.
6. asynchronous I/O
-. aio_xxxx() 계열의 POSIX aio 함수를 사용함.
-. LINUX kernel 2.6 에 포함된다는 말이 있으나, socket에 대해 지원할지는 확실하지 않음.
-. 가장 효율적인 방식으로 생각됨.
-. 사용해 보지 않아서 잘 모름.
** 대충 정해본 네트웍 서버 모델들에 대해 아는대로 써봤는데, 사실 네트웍 서버 모델이라는것은 이것 이외에도 많이 존재할 수 있다.
특히 여기서 다룬 부분은 TCP 에 치우친 경향이 있어서 UDP에 대해서는 나중에 보완할 예정이다.
** thread를 사용함에 있어서 가장 불안한 부분은 하나의 thread가 엄청난 짓을 저질러 버리면 전체가 다 맛이 가버린다는데 있다.
이를 보완하기 위해 I/O Multiplexing + Network I/O 만을 수행하는 thread군 과 실제 job을 수행하는 process군으로 분리하여 서로 pipe등으로 통신하도록 구성하는것도 하나의 방법이다.
물론 이때에는 Network I/O를 수행하는 thread 군은 수많은 테스트를 거쳐 거의 완벽한 모듈이 되어야 하며, job을 수행하는 process의 business Logic 부분만을 Application programmer에게 맞기면 되겠다.
이 글은 스프링노트에서 작성되었습니다.


