태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.
블로그 이미지
하늘을 헤엄치다. revoman

카테고리

분류 전체보기 (176)
Eye (13)
Programming (82)
Unix & Linux (52)
Android (3)
Tool and Tip (13)
시스템 관리 (7)
OPEN SOURCE (2)
XML (1)
WEB (0)
MY PROGRAM (1)
정보관리기술사 기출.. (1)
IT 동향 (1)
Total56,866
Today6
Yesterday34

출처 : 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에게 맞기면 되겠다.

이 글은 스프링노트에서 작성되었습니다.

Posted by revoman
TAG server

자동 셧다운/부팅 설정

  • 목표 : 새벽 2~6시에는 전원을 내려, 시스템 안정성 및 전기세 감면 하기 위함.

 

00 2   * * *   root /sbin/shutdown -h +5

이 글은 스프링노트에서 작성되었습니다.

Posted by revoman
TAG Linux, server

서버 설정하기

 

MYSQL 설정

 

INSERT INTO user(User, Host, Password) VALUES('testuser','localhost', PASSWORD('userpass'));

GRANT ALL on wikidb.* to wikiadmin;

FLUSH PRIVILEGES;

 

 

웹기반 무료 DNS 서버

전화나 케이블망으로 초고속인터넷을 이용하때, 매번 접속할 때마다, IP가 변경되는 유동IP환경에서도, 자신의 컴퓨터에서 인터넷 서버를 구축할 수 있습니다.DNSEver의 다이나믹DNS 서비스를 이용하면 자신의 컴퓨터에 다이나믹DNS클라이언트를 설치하여, 주기적으로, 자신의 도메인주소(예:www.myhome.com)을 매번 바뀌는 유동IP로 연결할 수 있습니다.

 

  • 호스트, IP 확인

nslookup www.myhome.com

 

 

 

  1. root@nudecode:/etc/init.d# cp /usr/local/bin/DNSEver DNSEver
    root@nudecode:/etc/init.d# chmod +x /etc/init.d/DNSEver
    root@nudecode:/etc/init.d# update-rc.d DNSEver defaults
     Adding system startup for /etc/init.d/DNSEver ...
       /etc/rc0.d/K20DNSEver -> ../init.d/DNSEver
       /etc/rc1.d/K20DNSEver -> ../init.d/DNSEver
       /etc/rc6.d/K20DNSEver -> ../init.d/DNSEver
       /etc/rc2.d/S20DNSEver -> ../init.d/DNSEver
       /etc/rc3.d/S20DNSEver -> ../init.d/DNSEver
       /etc/rc4.d/S20DNSEver -> ../init.d/DNSEver
       /etc/rc5.d/S20DNSEver -> ../init.d/DNSEver
    root@nudecode:/etc/init.d#

 

 

텍스트 큐브 설치

 

미디어 위키 설치

 

svn checkout http://svn.wikimedia.org/svnroot/mediawiki/trunk/phase3

서브버전 설치

sudo apt-get install subversion

sudo apt-get install libapache2-svn

하드웨어 시간 설정하기

이 글은 스프링노트에서 작성되었습니다.

Posted by revoman
TAG server

Linux HighPerformance Network Server
문서상태 작성: 이홍기(orinmir _at_ gmail.com)
작성일: 2001년 6월 10일
문서버전: 0.1.2
저작권: GPL

수정일:
     v0.1.0 ; 2001-06-10; 문서 초안 완성
     v0.1.1 ; 2001-06-12; RT signal concurrent echo server example
     v0.1.2 ; 2004-06-06; 일부 오자 수정

포기각서: 이 문서는 개인적인 자료 정리의 용도로 작성되었으며, 이 문서의 내용에 대한 모든 권리를 포기함. 또한 저작권에 대한 분쟁 발생시, 해당 내용은 삭제토록 한다.

TODO: 이 문서는 아직 draft상태에 있으며, 대부분의 저작자에 대한 정보를 link하려고 한다. 물론 빠진 부분은 보강해 나가고자한다. 좀더 유용한 예제들을 위주로 갱신할 예정이다.

개요   Linux에서의 TCP/IP Socket 기반 고성능 서버를 제작하는 방법에는 고전적인 multiplexing io(select, poll)를 가장 많이 사용하는데, 이는 매번 kernel로부터 전체 file descriptor 를 조사하여 set을 만들어 리턴하도록 하는 방식으로 high load 일수록 CPU점유율과 응답시간이 길어지는 단점이 있다. 물론 이를 사용하여 서버프로그램을 제작하여도 1000-3000명 많게는 5000개의 클라이언트 접속을 처리할 수는 있다고 보고되고 있다.
  오늘날에 와서는 여러가지 성능향상을 위한 시도가 이루어지고 있는데, 기존의 poll에 대한 성능향상을 꾀하여 /dev/poll와 같이 장치 인터페이스를 사용하는 방법(solaris 8에 도입되었다)과 기존의 SIGIO를 이용한 고전적인 방법이외에도 Realtime(RT) signal을 이용한 IO Completion signal 방식으로 high load에서의 낮은 CPU 점유율과 빠른 응답시간, inactive client 접속이 많아지더라도 처리에 문제가 없도록 하기 위한 방식들이 연구되고 구현되고 있다.
  타 운영체제에서는 FreeBSD의 kqueue/kevent와 Windows계열의 IOCP(IO Completion Port) 등이 있으며 OS kernel내에 관심있는 소켓들에 대해 이벤트를 등록해놓고 kernel로부터 signal을 기다리는 방법으로 그 내용은 거의 대동소이하다.

  본 문서에서는 리눅스에서의 Linux(kernel 2.2.x 이상)에서 사용할 수 있는 RT signal과 기존의 poll을 개선한 /dev/poll에 대해 이야기해보고 실제 고성능 서버의 소켓 모델에 대해서도 살펴보기로한다.

select vs. poll     고전적인 bsd socket의 multiplex io 처리 방식은 select()와 poll()에 대한 이야기로는   poll()방식이 select()에 비해 좀더 낮은 CPU점유율과 빠른 응답속도로 고성능의 서버프로그램에서   사용되고 있다는 이야기들이 일반적이다.
  linux의 경우 2.0전에는 poll()이 구현되어 있지 않아 select() system call을 매핑하여 poll()을   구현하였으며, 2.0이후에는 poll()이 구현되어 반대로 select()가 poll()의 매핑 system call로   바뀌었다.

    그렇다면 왜? poll()방식이 더 select()보다 효율적인가? 쉽게 생각하면 poll()이 좀더   low level의 처리로 system call의 호출이 select()보다 적기 때문이다. select의 경우는 처리하고자   하는 socket만을 감시하는 것이 아니라, max filedescriptor만큼을 항상 loop를 돌며 처리해야하고   poll은 이에 비해 원하는 만큼만 max로 지정해서 늘어나는 만큼 loop를 돈다. 하지만 select()가 poll()   보다 사용하기가 조금더 쉽고 poll()을 이용하여 select()를 구현하기도 한다.   poll이 select보다 효율적이지만 접속수가 늘어나면 오히려 fd당 체크 마스크의 크기가 select는 3bit인데   비해, poll은 6Byte(이부분 확실하진 않음)정도이므로 양이 많아지면 오히려 select보다 더 많은 overhead가   존재하여 성능이 더 떨어진다는 이에 대한 벤치마킹도 존재한다. 특히 리눅스에서는 그렇다는 얘긴데,   이로 인해 그리 좋은 소리는 듣고 있지 못하긴하다.

    고전적인 select/poll에서의 성능 향상 방법으로는 multiple accept가 있다. 이 방법은    process fork방식에서 예를 들자면 child를 spawning시켜(listen pool형성), accept를 받아 recv/send를 해주고   끊어주는 일반적인 request-response-close로 이어지는 webserver style의 구현 방식이며, 이를   이용한 벤치마킹에서 좋은 효율을 보이고 있다.
    또한 고전적인 select, poll 자체에 대한 성능 향상 패치도 물론 존재하고 있다.   

  examples: Unix Network Programming 2e의 pollselect 구현예제, multiple accept, multiple accept2
  articles: Select() vs. Poll(),

/dev/poll     /dev/poll은 poll()의 처리 방식이 kernel내의 file descriptor 를 모두 뒤져서 응답 set을   구성하는 방식의 비효율성을 극복하기 위해 나온 방식으로 기본적으로 event driven방식으로   이루어진 poll의 성능 개선방식이다. poll을 장치 입출력 방식으로 바꾸어놓은 것이라고 볼   수 있다. 벤치마킹에서 linux /dev/poll이 성능이 제대로 나오지 않는 이유는, /dev/poll의   구현에 버그가 있다는 이야기도 있다.   

  patches: dev_poll kernel patch(kernel 2.2.14,/ kernel 2.4.3) from here

RT Signal   커널 2.2에서부터 RealTime Signal(Posix RealTime Signal)을 지원하기 시작했다. 기존에 signal을 이용한 내용으로 sigio를 이용한 async io(타 운영체제에서는 aio_* function calls로 된 것도 있음) 에서 쓰였고 2.2가 나오기전까지는 패치를 통해 비동기 io를 사용할 수는 있었다. sigio를 이용한 signal 기반 소켓 서버의 예제도 stevens의 Unix Network Programming같은 서적을 통해서도 소개된 바있다. Linux의 RT signal방식은 기존 signal 기반 프로그램들보다는 좀더 접근하기 쉬운 방법으로 Poll을 대체할 수 있는 방법이다. (poll()에서 사용하는 POLL_IN, POLL_OUT, POLL_ERR 등을 그대로 사용한다.) RT Signal의 장점(혹은 sigio로 만들어진 것들의 장점일수도 있다.)이라면, signal을 등록해놓고 해당하는 이벤트가 발생하기 전까지는 되고 sigwaitinfo system call을 통해 대기하고 있다가, 이벤트가 발생하면, 정해둔 signal handler를 통해 처리를 하는 방식이다.(또한 timeout도 지원한다.) 물론 기존의 구현 방식에 비해 낮은 CPU점유율과 빠른 응답 속도, high load에서도 안정되고 일관된 성능을 유지하고 있다.
 5000개의 소켓 접속과 high load에서도 굉장히 좋은 성능을 내고 있다는 보고가 있다. 하지만 RT Signal의 단점은 제한된 signal resource로 인해, signal overflow가 되면 더이상 새로운 접속을 처리할 수 없게 된다. 이 때에는 기존의 poll()쪽으로 처리를 돌려 해결하는 혼합된 구현 방법을 쓰기도 한다.

 이를 해결하기위해 Vitaly라는 사람이 Signal-per-fd라는 kernel patch를 내놓았으며 기존의 kernel의 signal queue가 fd별로 multiple event를 받아내어야 하므로, high load시에 overflow가 일어날 수 있다는 단점에 착안, fd별로 단 하나의 signal event만을 가질 수 있도록 고쳤다고 한다. 이를 이용하여 기존의 방식들과의 성능 분석을 한 paper는 아래에 있다.
 한편, Linux RT Signal로 구현된 것으로는 phhttpd가 있다.

 최근 필자가 Linux RT Signal(signal-per-fd는 아직 추가 구현중)로 만든, 완벽한(???) Concurrent Echo Server 예제를 아래(rtsig_echoserver.cpp)에 링크해놓았다.

examples: rtsig_echoserver.cpp, sigio_example
articles: Signal-per-fd release document,
patches: Signal-per-fd kernel 2.4.4 patch,    dynhash kernel patch
papers:
    A scalable and explicit event delivery mechanism for UNIX [usenix99],
    Scalability of Linux Event-Dispatch Mecanism [Signal-per-fd],
    Linux Kernel Hash Table Behavior [ring buffer],
    Scalable Network IO in Linux.pdf [RTsig]
    Analyzing_the_Overload_Behavior_of_a_Simple_Web_Server[sigwaitinfo4]

Server Frameworks    실제로 Socket 서버를 제작할 때에는 UNIX Network Programming책을 보면, 여러가지 서버 모델이   나오는데, 그중에서도 가장 많이 참고하는 쪽은 concurrent socket server일 것이다. 이에 대한   구현 방식은 서버의 성격과 패킷의 송수신 형태 등에 따라, 그 구조를 어떻게 가져가느냐에   많은 관심이 있을 것이다.   
  usenet newsgroup의 comp.programing.threads나 comp.unix.programmer 등을 검색을 해보거나 질문을   올려보면, 대부분의 답변은 다음과 같이 정리될 수 있다고 본다.   

   1. linux에서는 1000+의 클라이언트 처리에도 문제가 없으며, 단지 문제가 된다면, RAM의 크기이다.   
   2. linux의 소켓 모델은 일반적으로 multiplexing io를 사용하여, select()보다는 poll()이 다소 처리가      매끄럽고 빠르다. 3000개 동시 접속 클라이언트이내라면      이 구조를 사용하는 것이 좋다. 만약 8000+정도를 처리해야한다면 sigio를 고려해볼만 하다.   
   3. 처리 방식에 있어, read전용 multiplexing io 1thread와 다수개의 write전용 thread가 좀더 효율이      좋다.   
   4. sigio나 성능 개선된 poll()을 쓰는 것이 CPU 사용율이라던지 효율적인 리소스 사용을     위해서는 좋을 수도 있다. 하지만 linux의 /dev/poll 구현에서 버그가 있다.   

   articles: 1000+ Client에서 고려할점,       Which_is_more_faster_socket_model,       How can I multiple read() with threads
   papers: Design Framework For highly Concurrent Systems, ACE frameworks

Other Tuning Points     Linux에서는 Beowulf Super Clustering Server 에서 사용된 channel bonding을 kernel 2.4부터는    정식으로 포함시켰다. channel bonding은 한마디로 2개의 NIC을 하나의 NIC처럼 사용하도록 하는    것이다. 그리고 2개이상의 NIC를 사용하는 경우, 강제로 IRQ를 sharing을 하도록 하면 성능이    나아진다는 설이 있는데, 이에 대한 문헌은 좀더 찾아볼 예정이다. 아래는 이 문서내에서 다루지    않은 또다른 튜닝에 관련된 사항을 다루고자한다.   

   patches: fast socket close kernel 2.2.x patch,        improved malloc patch
   tunning: zeus server os tunning guide
   articles: How many thread can I use with Linux

Patched Packages   KERNEL 2.4.5 with /dev/poll and signal-per-fd patched
  : 청아에서 사용된 kernel 2.4.5 tarball이다.
  

    tarball: linux-2.4.5-chonga1.tar.gz   

  GLIBC v2.2.2 with pthread mysql tunning
  : MAX_PTHREAD_NUM=4096, rpm4.0
  : mysql site의 linux glibc-2.2.2 patch 적용해서 다시 패키징했다.   

    srpms: glibc-2.2.2-10.src.rpm
    rpms: glibc-2.2.2-10.i386.rpm,       glibc-common-2.2.2-10.i386.rpm,       glibc-profile-2.2.2-10.i386.rpm,       glibc-devel-2.2.2-10.i386.rpm,       nscd-2.2.2-10.i386.rpm   

Other Links    Chuck Lever's Linux Scalability March/Aprial 2000 Status Report
  Usenix symposium
  Client 10000 project
  linux에서의 dummy client socket 접속 테스트
Posted by revoman
TAG C, Linux, server

최근에 달린 댓글

최근에 받은 트랙백

글 보관함