웹 애플리케이션은 이제 모든 곳에서 사용되고 있으며, 모든 전송 프로토콜 중에서 HTTP가 가장 큰 비중을 차지하고 있습니다. 웹 애플리케이션 개발의 미묘한 차이를 연구할 때 대부분의 사람들은 이러한 애플리케이션이 실제로 실행되는 운영 체제에는 거의 관심을 기울이지 않습니다. 개발(Dev)과 운영(Ops)이 분리되면서 상황은 더욱 악화되었습니다. 그러나 DevOps 문화가 증가함에 따라 개발자는 클라우드에서 애플리케이션을 실행하는 책임을 갖게 되므로 운영 체제의 백엔드에 완전히 익숙해지는 것이 매우 유용합니다. 이는 수천 또는 수만 개의 동시 연결을 위해 시스템을 배포하려는 경우 특히 유용합니다.
웹 서비스의 제한 사항은 다른 응용 프로그램의 제한 사항과 매우 유사합니다. 로드 밸런서이든 데이터베이스 서버이든 이러한 모든 애플리케이션은 고성능 환경에서 비슷한 문제를 안고 있습니다. 이러한 근본적인 한계와 일반적인 극복 방법을 이해하면 웹 애플리케이션의 성능과 확장성을 평가하는 데 도움이 됩니다.
Я пишу эту серию статей в ответ на вопросы молодых разработчиков, которые хотят стать хорошо информированными системными архитекторами. Невозможно чётко понять методы оптимизации приложений Linux, не погрузившись в основы, как они работают на уровне операционной системы. Хотя есть много типов приложений, в этом цикле я хочу исследовать сетевые приложения, а не десктопные, такие как браузер или текстовый редактор. Этот материал рассчитан на разработчиков и архитекторов, которые хотят понять, как работают программы Linux или Unix и как их структурировать для высокой производительности.
Linux -가요 서버 룸 операционная система, и чаще всего ваши приложения работают именно на этой ОС. Хотя я говорю «Linux», большую часть времени вы можете с уверенностью предположить, что имеются в виду все Unix-подобные операционные системы в целом. Тем не менее, я не тестировал сопровождающий код на других системах. Итак, если вас интересует FreeBSD или OpenBSD, результат может отличаться. Когда я пробую что-то Linux-специфическое, то указываю на это.
이 지식을 사용하여 처음부터 앱을 구축하면 완벽하게 최적화될 수 있지만 그렇게 하지 않는 것이 가장 좋습니다. 조직의 비즈니스 애플리케이션을 위해 C 또는 C++로 새 웹 서버를 작성한다면 오늘이 작업의 마지막 날일 수 있습니다. 그러나 이러한 응용 프로그램의 구조를 아는 것은 기존 프로그램을 선택하는 데 도움이 됩니다. 프로세스 기반 시스템을 스레드 기반 시스템 및 이벤트 기반 시스템과 비교할 수 있습니다. Nginx가 Apache httpd보다 더 나은 성능을 발휘하는 이유와 Tornado 기반 Python 애플리케이션이 Django 기반 Python 애플리케이션에 비해 더 많은 사용자에게 서비스를 제공할 수 있는 이유를 이해하고 감사하게 될 것입니다.
ZeroHTTPd: 학습 도구
는 교육 도구로 처음부터 C로 작성한 웹 서버입니다. Redis에 대한 액세스를 포함하여 외부 종속성이 없습니다. 우리는 자체 Redis 절차를 실행합니다. 자세한 내용은 아래를 참조하세요.
이론을 길게 논의할 수는 있지만 코드를 작성하고 실행하고 모든 서버 아키텍처를 서로 비교하는 것보다 더 좋은 것은 없습니다. 이것이 가장 확실한 방법이다. 따라서 우리는 프로세스 기반, 스레드 기반, 이벤트 기반의 각 모델을 사용하여 간단한 ZeroHTTPd 웹 서버를 작성하겠습니다. 각 서버를 확인하고 서로 비교하여 성능이 어떤지 살펴보겠습니다. ZeroHTTPd는 단일 C 파일로 구현되며 이벤트 기반 서버에는 다음이 포함됩니다. , 단일 헤더 파일로 제공되는 훌륭한 해시 테이블 구현입니다. 다른 경우에는 프로젝트를 복잡하게 하지 않기 위해 종속성이 없습니다.
В коде очень много комментариев, чтобы помочь разобраться. Будучи простым веб-сервером в нескольких строчках кода, ZeroHTTPd также представляет собой минимальный фреймворк для веб-разработки. У него ограниченная функциональность, но он способен выдавать статические файлы и очень простые «динамические» страницы. Должен сказать, что ZeroHTTPd хорошо подходит для обучения, как создавать высокопроизводительные Linux-приложения. По большому счёту, большинство веб-сервисов ждут запросов, проверяют их и обрабатывают. Именно это будет делать ZeroHTTPd. Это инструмент для обучения, а не для продакшна. Он не силён в обработке ошибок и вряд ли похвастается лучшими практиками безопасности (о да, я использовал strcpy) 또는 C 언어의 영리한 트릭이지만 그것이 잘 작동하기를 바랍니다.

ZeroHTTPd 홈페이지. 이미지를 포함한 다양한 파일 형식을 출력할 수 있습니다.
방명록 신청
최신 웹 애플리케이션은 일반적으로 정적 파일에만 국한되지 않습니다. 그들은 다양한 데이터베이스, 캐시 등과 복잡한 상호 작용을 합니다. 따라서 우리는 방문자가 자신의 이름 아래에 항목을 남기는 "Guest Book"이라는 간단한 웹 애플리케이션을 만들 것입니다. 방명록에는 이전에 남겨진 항목이 저장됩니다. 페이지 하단에도 방문자 카운터가 있습니다.

웹 애플리케이션 "Guest Book" ZeroHTTPd
Счётчик посетителей и записи гостевой книги хранятся в Redis. Для коммуникаций с Redis реализованы собственные процедуры, они не зависят от внешней библиотеки. Я не большой поклонник выкатывать доморощенный код, когда есть общедоступные и хорошо протестированные решения. Но цель ZeroHTTPd — изучить производительность Linux и доступ к внешним службам, в то время как обслуживание HTTP-запросов серьёзно влияет на производительность. Мы должны полностью контролировать коммуникации с Redis в каждой из наших серверных архитектур. В одной архитектуре мы используем блокирующие вызовы, в других — процедуры на основе событий. Использование внешней клиентской библиотеки Redis не даст такой контроль. Кроме того, наш маленький клиент Redis выполняет только несколько функций (получение, настройка и увеличение ключа; получение и добавление к массиву). К тому же, протокол Redis исключительно элегантный и простой. Его даже учить специально не надо. Сам факт, что всю работу протокол выполняет примерно в ста строчках кода, говорит о том, насколько он хорошо продуман.
다음 그림은 클라이언트(브라우저)가 요청할 때 애플리케이션이 수행하는 작업을 보여줍니다. /guestbookURL.

방명록 애플리케이션 작동 방식
방명록 페이지를 발행해야 하는 경우 템플릿을 메모리로 읽어들이기 위해 파일 시스템을 한 번 호출하고 Redis를 세 번 네트워크 호출합니다. 템플릿 파일에는 위 스크린샷에 있는 페이지에 대한 대부분의 HTML 콘텐츠가 포함되어 있습니다. 콘텐츠의 동적 부분(게시물 및 방문자 카운터)을 위한 특별한 자리 표시자도 있습니다. Redis에서 이를 받아 페이지에 삽입하고 클라이언트에게 완전한 콘텐츠를 제공합니다. Redis가 증가하면 새 키 값을 반환하므로 Redis에 대한 세 번째 호출을 피할 수 있습니다. 그러나 비동기 이벤트 기반 아키텍처를 갖춘 우리 서버의 경우 많은 네트워크 호출은 학습 목적으로 좋은 테스트입니다. 그래서 우리는 방문자 수의 Redis 반환 값을 버리고 별도의 호출로 쿼리합니다.
서버 아키텍처 ZeroHTTPd
우리는 기능은 동일하지만 아키텍처가 다른 XNUMX가지 버전의 ZeroHTTPd를 구축하고 있습니다.
- 반복적 인
- 포크 서버(요청당 하나의 하위 프로세스)
- 프리포크 서버(프로세스 프리포크)
- 실행 스레드가 있는 서버(요청당 스레드 XNUMX개)
- 사전 스레드 생성 기능이 있는 서버
- 아키텍처 기반
poll() - 아키텍처 기반
epoll
우리는 HTTP 요청으로 서버를 로드하여 각 아키텍처의 성능을 측정합니다. 그러나 고도로 병렬화된 아키텍처를 비교할 때는 쿼리 수가 증가합니다. 세 번 테스트하고 평균을 계산합니다.
테스트 방법론

ZeroHTTPd 로드 테스트 설정
테스트를 실행할 때 모든 구성 요소가 동일한 시스템에서 실행되지 않는 것이 중요합니다. 이 경우 구성 요소가 CPU를 놓고 경쟁하므로 OS에 추가 스케줄링 오버헤드가 발생합니다. 선택한 각 서버 아키텍처의 운영 체제 오버헤드를 측정하는 것은 이 연습의 가장 중요한 목표 중 하나입니다. 더 많은 변수를 추가하면 프로세스에 해로울 수 있습니다. 따라서 위 그림의 설정이 가장 잘 작동합니다.
각 서버는 어떤 역할을 합니까?
- load.unixism.net: 여기가 우리가 실행하는 곳입니다.
ab, Apache 벤치마크 유틸리티. 서버 아키텍처를 테스트하는 데 필요한 로드를 생성합니다. - nginx.unixism.net: 때로는 서버 프로그램의 인스턴스를 두 개 이상 실행하고 싶을 때가 있습니다. 이를 위해 적절한 설정을 갖춘 Nginx 서버는 다음에서 오는 로드 밸런서로 작동합니다. ab 우리 서버 프로세스에.
- zerohttpd.unixism.net: 여기에서는 한 번에 하나씩 XNUMX개의 서로 다른 아키텍처에서 서버 프로그램을 실행합니다.
- redis.unixism.net: 이 서버는 방명록 항목과 방문자 카운터가 저장되는 Redis 데몬을 실행합니다.
모든 서버는 동일한 프로세서 코어에서 실행됩니다. 아이디어는 각 아키텍처의 최대 성능을 평가하는 것입니다. 모든 서버 프로그램은 동일한 하드웨어에서 테스트되므로 이는 비교를 위한 기준이 됩니다. 내 테스트 설정은 Digital Ocean에서 임대한 가상 서버로 구성되어 있습니다.
우리는 무엇을 측정하고 있나요?
다양한 지표를 측정할 수 있습니다. 우리는 다양한 병렬 처리 수준의 요청으로 서버를 로드하여 특정 구성에서 각 아키텍처의 성능을 평가합니다. 로드는 동시 사용자 20명에서 15명으로 증가합니다.
시험 결과
다음 차트는 다양한 수준의 병렬 처리에서 다양한 아키텍처의 서버 성능을 보여줍니다. y축은 초당 요청 수이고 x축은 병렬 연결입니다.



아래는 결과를 담은 표입니다.
초당 요청
동시성
반복적 인
포크
프리포크
스트리밍
사전 스트리밍
투표
Epoll
20
7
112
2100
1800
2250
1900
2050
50
7
190
2200
1700
2200
2000
2000
100
7
245
2200
1700
2200
2150
2100
200
7
330
2300
1750
2300
2200
2100
300
-
380
2200
1800
2400
2250
2150
400
-
410
2200
1750
2600
2000
2000
500
-
440
2300
1850
2700
1900
2212
600
-
460
2400
1800
2500
1700
2519
700
-
460
2400
1600
2490
1550
2607
800
-
460
2400
1600
2540
1400
2553
900
-
460
2300
1600
2472
1200
2567
1000
-
475
2300
1700
2485
1150
2439
1500
-
490
2400
1550
2620
900
2479
2000
-
350
2400
1400
2396
550
2200
2500
-
280
2100
1300
2453
490
2262
3000
-
280
1900
1250
2502
큰 확산
2138
5000
-
큰 확산
1600
1100
2519
-
2235
8000
-
-
1200
큰 확산
2451
-
2100
10
-
-
큰 확산
-
2200
-
2200
11
-
-
-
-
2200
-
2122
12
-
-
-
-
970
-
1958
13
-
-
-
-
730
-
1897
14
-
-
-
-
590
-
1466
15
-
-
-
-
532
-
1281
Из графика и таблицы видно, что выше 8000 одновременных запросов у нас остаётся только две игрока: пре-форк и epoll. С ростом нагрузки сервер на базе poll работает хуже, чем потоковый. Архитектура с предварительным созданием потоков составляет достойную конкуренцию epoll: это свидетельство, насколько хорошо ядро Linux планирует большое количество потоков.
ZeroHTTPd 소스 코드
ZeroHTTPd 소스 코드 . 각 아키텍처마다 별도의 디렉터리가 있습니다.
ZeroHTTPd │ ├── 01_iterative │ ├── main.c ├── 02_forking │ ├── main.c ├── 03_preforking │ ├── main.c ├── 04_ 스레딩 │ ├── main.c ├── 05_prethreading │ ├── main.c ├── 06_poll │ ├── main.c ├── 07_epoll │ └── main.c ├── Makefile ├── public │ ├── index .html │ └── tux .png └── 템플릿 └── 방명록 └── index.html
모든 아키텍처에 대한 XNUMX개의 디렉터리 외에도 최상위 디렉터리에는 public과 template이라는 두 개의 디렉터리가 더 있습니다. 첫 번째에는 index.html 파일과 첫 번째 스크린샷의 이미지가 포함되어 있습니다. 여기에 다른 파일과 폴더를 넣을 수 있으며 ZeroHTTPd는 문제 없이 이러한 정적 파일을 제공해야 합니다. 브라우저의 경로가 공용 폴더의 경로와 일치하면 ZeroHTTPd는 이 디렉터리에서 index.html 파일을 찾습니다. 방명록의 콘텐츠는 동적으로 생성됩니다. 여기에는 홈 페이지만 있고 해당 콘텐츠는 'templates/guestbook/index.html' 파일을 기반으로 합니다. ZeroHTTPd는 확장을 위한 동적 페이지를 쉽게 추가합니다. 아이디어는 사용자가 이 디렉터리에 템플릿을 추가하고 필요에 따라 ZeroHTTPd를 확장할 수 있다는 것입니다.
XNUMX개의 서버를 모두 구축하려면 다음을 실행하세요. make all 최상위 디렉터리에서 - 모든 빌드가 이 디렉터리에 나타납니다. 실행 파일은 실행된 디렉터리에서 공용 및 템플릿 디렉터리를 찾습니다.
Linux API
Чтобы понять информацию в этом цикле статей, не обязательно хорошо разбираться в Linux API. Однако рекомендую прочитать больше на эту тему, в Сети много справочных ресурсов. Хотя мы коснёмся нескольких категорий Linux API, наше внимание будет сосредоточено в основном на процессах, потоках, событиях и сетевом стеке. Кроме книг и статей про Linux API, рекомендую также почитать маны для системных вызовов и используемых библиотечных функций.
성능 및 확장성
성능 및 확장성에 대한 참고 사항입니다. 이론적으로 그들 사이에는 연관성이 없습니다. 몇 밀리초의 응답 시간으로 매우 잘 작동하지만 전혀 확장되지 않는 웹 서비스를 가질 수 있습니다. 마찬가지로, 응답하는 데 몇 초가 걸리지만 수만 명의 동시 사용자를 처리하기 위해 수만 배로 확장되는 성능이 낮은 웹 애플리케이션이 있을 수 있습니다. 그러나 고성능과 확장성의 조합은 매우 강력한 조합입니다. 고성능 응용 프로그램은 일반적으로 리소스를 적게 사용하므로 서버에서 더 많은 동시 사용자에게 효율적으로 서비스를 제공하여 비용을 절감합니다.
CPU 및 I/O 작업
마지막으로, 컴퓨팅에는 I/O와 CPU라는 두 가지 가능한 작업 유형이 있습니다. 인터넷을 통한 요청 수신(네트워크 I/O), 파일 제공(네트워크 및 디스크 I/O), 데이터베이스와의 통신(네트워크 및 디스크 I/O)은 모두 I/O 활동입니다. 일부 데이터베이스 쿼리는 약간 CPU 집약적일 수 있습니다(정렬, 백만 개의 결과 평균화 등). 대부분의 웹 애플리케이션은 가능한 최대 I/O로 제한되며 프로세서가 전체 용량으로 사용되는 경우는 거의 없습니다. 일부 I/O 작업이 많은 CPU를 사용하고 있다는 것을 알게 되면 이는 애플리케이션 아키텍처가 좋지 않다는 신호일 가능성이 높습니다. 이는 프로세스 관리 및 컨텍스트 전환에 CPU 리소스가 낭비된다는 의미일 수 있으며 이는 완전히 유용하지는 않습니다. 이미지 처리, 오디오 파일 변환 또는 기계 학습과 같은 작업을 수행하는 경우 해당 애플리케이션에는 강력한 CPU 리소스가 필요합니다. 그러나 대부분의 응용 프로그램에서는 그렇지 않습니다.
서버 아키텍처에 대해 자세히 알아보기
출처 : habr.com
