티스토리 뷰

Web

NGINX와 Apache (NGINX and Apache)

DevES 2020. 4. 4. 22:49

NGINX와 Apache(NGINX and Apache)

잘못 정리된 내용이 있다면 댓글부탁드립니다.


관련해서 알아두면 이해하는데 도움되는 내용(추후에 딥하게 알아보자.)

  • socket
    • 네트웤 데이터 교환을 위한 IO 인터페이스
    • 또한 일종의 file이다.(리눅스)
    • blocking/non-blocking mode
  • IO(especially, Network I/O)
    • 외부와 데이터를 교환하기 위한 기능.
    • 소켓은 네트워크를 통해 외부와 데이터를 교환할 수 있는 IO 인터페이스.
  • system call
    • user/kernal space
    • 일단 프로세스(user space상의)는 시스템의 file을 직접 다룰 수 없다. 따라서 소켓에 대한 핸들링을 하기 위해선 커널에 요청해야함.
  • IO multiplexing(select(stateless API), epoll(stateful API), etc.)
    • event 감지 방식
  • C10K 문제
    • 네트워크 환경이 발전하면서 트래픽이 늘게되고 서버가 동시에 대량의 클라이언트 요청을 처리할 수 있느냐에 대한 문제.
    • 하드웨어 스펙과 상관없이, 동시에 만개 이상의 소켓을 열었을 때 옛날의 IO 모델(e.g. select)에서 빠르게 찾아 처리 후 응답을 보내는게 어려웠음.
    • 이를 해결하려고 epoll과 같은 새로운 방식의 IO multiplexing 모델이 등장.

nginx에 대해 알아보기 전에 httpd에 대해 간단히 알아보자.

Apache(httpd)

고전적으로 많이 사용된 웹서버.

  • mpm(Multi Processing Modules)이라는 요청 처리 모듈의 유형에 따라 요청을 분배받은 child 프로세스가 요청을 처리하는 방식이 달라짐.

prefork 방식

prefork

  • parent 프로세스에서 fork된 child 프로세스 단위로 요청을 처리.

    • 1 Request - 1 Child Process(single threaded)
  • 장점: 안정성. 싱글 스레드라 multi thread unsafe한 외부 개발 모듈또한 사용이 부담없음.

    • 안정성 때문에 사내에서도 오래 사용되어짐.

요청이 오기전에 spare child process들을 미리(pre) 복제(fork)해놓는다해서 이름이 prefork인듯.

worker 방식

worker

  • 요청을 스레드 단위로 처리

  • 1 Request - 1 worker thread of Child Process(프로세스 생성 시점에 고정된 수의 multi worker thread 생성)

  • 장점: prefork에 비해 좋은 메모리 효율

prefork/worker 방식 둘다 속도는 비슷.

event 방식

event

2.4.x 버전부터 추가됨.

  • worker 방식 기반.

  • worker 방식에 추가로, 각각 자식 프로세스에 listener 스레드가 추가되었는데, 이 listener 스레드가 요청을 받아 커넥션을 worker 스레드로 던짐.

    • apache는 keepalive로 커넥션을 유지할 경우 해당 프로세스 전체가 wait하는 단점이 있어 이를 개선하고자 별도의 listener를 두었다고 함.
  • event 방식에서는 소켓도 논블로킹 모드로 열고 io처리도 멀티플렉싱 모델을 사용한다고는 하지만, 그래도 성능은 nginx에 뒤진다고 한다. :(


Nginx

  • 밀고있는 키워드
    • Event-driven(Asynchronous) architecture
    • small memory usage
    • solve C10K Problem

io 멀티플렉싱 모델(epoll, kqueue)이 핵심인듯 하다. -> 사용할 io 모델의 종류는 nginx 컴파일시점에 옵션으로 선택(select, poll, kqueue, epoll, etc.). 혹은 자동으로 지정.
일반적으로 우리가 사용하는 apache(prefork 방식)같이 한 요청이 끝날때까지 프로세스가 대기하며 다른 작업을 할 수없는 건 blocking io 인듯.

nginx-architecture

Apache와 같이 프로세스/스레드 단위로 커넥션을 처리하는 서버 모델은 한계가 있음.

  • 각 커넥션의 IO작업(e.g. 커넥션 맺을 때 네트웤 소켓열기, read버퍼에서 데이터 긁어오기/write버퍼로 보내기, etc.)중에 프로세스/스레드가 blocked되니 메모리/CPU 효율이 좋지 않음.
  • 메모리: 동시에 처리해야할 요청이 많을수록 복제되는 프로세스가 많아지니 메모리 차지.

  • CPU: 멀티 프로세스간 CPU 사용을 위한 컨텍스트 스위칭 비용

Nginx는

  • 1 Master Process - fixed N Worker Processes(single threaded)
    • master는 하는 일이 거의 없음.(초반에 listening socket 열고 worker 복제하는 정도.) 실질적으로 worker가 모든 작업 수행.

nginx 동작 흐름

a. Starting Server

process architecture

  • master process
    • 각종 초기화 작업 진행.
      • config 읽음, 포트 바인딩, child processes(cache loader/cache manager/worker processes) 생성
  • cache loader
    • 시작시에 생성되서 disk의 캐시 데이터를 memory로 copy하는 작업 이후 종료.
  • cache manager
    • 주기적으로 실행되면서 캐시 공간 정리.

b. Running

1. Event Loop

  • worker process(single thread)는 event queue 하나를 가지고 단일 event loop내에서 모든 커넥션에 대한 작업을 처리함.

a event loop

event queue processing cycle

여기서 말하는 이벤트란 커넥션과 관련된 이벤트(예를 들면 timeout, IO할 데이터가 발생했다거나, error)를 의미함.

  • 각 worker당 하나의 loop가 존재. 이 loop가 반복해 돌면서 아래와 같은 작업 진행
    • 커널로부터 신규로 발생한 이벤트를 받아 큐에 적재.
    • 큐에 쌓인 이벤트를 하나 꺼내 처리(건바이건으로 loop 1회당 하나의 event를 처리.)

2. 커넥션 분배 방식

2-1. 단일 listen socket

single listen socket

  • 기본 방식
  • NGINX 전체에 하나의 listen socket에서 요청을 accept해서 각 worker별로 돌아가면서 connection을 만들어 던져줌.
  • connection 배분을 위해 lock 리소스가 들긴함.

2-2. Socket sharding(~ 1.9.1)

socket sharding

  • SO_REUSEPORT 소켓 옵션(하나의 포트에 대해 복수개로 소켓을 binding 가능)을 통해서 각 워커당 listen socket을 열어둬서 병렬처리 향상
  • 애초에 커널단에서부터 요청을 어느 listen socket에 accept할지 round robin 형태로 분배함.
  • 특정 listen socket에 대해 커넥션을 받을 수 있는 worker가 1개다보니 해당 worker가 blocking되는 케이스에선 신규로 들어오는 요청을 처리하지 못한다는 단점이 있음.(첫번째에선 다른 worker가 받음.)
    • 사용하고 있는 곳은 많이는 보지 못했다. 대량 태스크 처리를 다루는 한 곳 정도?

기타

  • 모듈을 실행시점에 동적으로 로딩하는 것이 불가.(빌드 시점에 사용하고자 하는 모듈또한 같이 포함되어 컴파일되어야함.)

  • apache의 mod_jk와 같은 서블릿 컨테이너와의 공식적인 AJP 커넥터를 지원하지 않음.

    • http proxy 방식으로 처리 필요.
  • worker내의 thread pool: 한 worker당 하나의 스레드, 단일 loop에서 한 건씩 event를 처리하기 때문에 nginx 내부 기능중에 blocking이 길게 되야하는 작업(e.g. nginx 서버 내부에서 캐싱되어있지 않아 disk를 갔다와야한다던가)을 수행하는 중에는 loop가 중단되 event queue에 쌓인 작업들이 전체적으로 delay된다.(nginx 코드에도 blocking 방식으로 동작하는 코드들이 좀 있다고 한다.) 이로 인한 delay를 막기 위해서 각 worker process내에 thread pool을 만들어 이런 긴 blocking io를 수행하는 애들이 있을 경우 이 task는 별도로 thread pool에서 작업을 수행하도록 해서 block을 막고 event loop는 계속 돌아가도록 함.

참고

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함