После столь долгого перерыва я решил написать статью про то как я писал веб-сервер на С++ и системных вызовах не используя никаких сторонних библиотек.
Фактически это был просто эксперимент, поэтому сразу предупреждаю что код ещё далёк от идеала по производительности и структуре.
Для начала в качестве предисловия
Идея написать свой веб-сервер на С++, пришла не просто так в мою голову, изначально это было просто тестовое задание на разработку веб-сервера для раздачи статического контента на С++ с поддержкой многопоточности. Там же мне пришла идея использовать наработки из С++11 (поэтому данный код собирается уверенно на компиляторах GCC 4.8.4 и старше)
В текущем состоянии кодовой базы на моей машине (Core i5-4210U 1.7 GHz) для тестов на запрос вкомпилированной страницы выдаются следующие результаты (фреймворк для тестирования Apache Benchmark):
Server Software: simple
Server Hostname: 127.0.0.1
Server Port: 8080
Document Path: /
Document Length: 103 bytes
Concurrency Level: 100
Time taken for tests: 4.470 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 18600000 bytes
HTML transferred: 10300000 bytes
Requests per second: 22371.59 [#/sec] (mean)
Time per request: 4.470 [ms] (mean)
Time per request: 0.045 [ms] (mean, across all concurrent requests)
Transfer rate: 4063.59 [Kbytes/sec] received
Для страницы расположенной на диске устройства:
Server Software: simple
Server Hostname: 127.0.0.1
Server Port: 8080
Document Path: /index.html
Document Length: 28914 bytes
Concurrency Level: 100
Time taken for tests: 6.048 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 2900100000 bytes
HTML transferred: 2891400000 bytes
Requests per second: 16535.44 [#/sec] (mean)
Time per request: 6.048 [ms] (mean)
Time per request: 0.060 [ms] (mean, across all concurrent requests)
Transfer rate: 468305.09 [Kbytes/sec] received
Что весьма не плохо с учётом того что никаких модулей кэширования не было применено в процессе разработки. Забор файлов идёт используя модуль fstream стандартной библиотеки С++
Общее устройство
Код сервера полностью открыт, лицензия BSD-v3 (поэтому можете копировать и использовать код под свои нужды). Ссылка на репозиторий гитхаб: https://github.com/no111u3/simple_web
Рабочий код разбит на модули:
simple_web.h(.cpp) - запуск веб-сервера, выполнение конфигурации
core.h(.cpp) - запуск ядра сервера
daemon.h - переключение в режим демона ОС Linux, в соответствии с рекомендациями
config.h(.cpp) - разбор настроек сервера и их хранение в течении всей работы
polling.h(.cpp) - ядро обслуживания мастер-сокета.
handle_message.h(.cpp) - ядро обслуживания клиентских сокетов
Данный код может работать как однопоточном так и в многопоточном режиме. За создание потоков, планирование задач и создание новых отвечает следующие модули:
thread_pool.h(.cpp) - рабочий пул потоков с возможностью добавления задач и разделения их по очередям (локальная очередь потока и общая очередь).
function_wrapper.h - контейнер для корректного запуска/завершения задачи передаваемой в пул.
thread_safe_queue.h - общая очередь для разделения задач по потокам.
work_stealing_queue.h - очередь потока, с поддержкой заимствования
Также существует ряд сервисных модулей, без которых устройство кода было бы не таким компактным:
content_type_identify.h - статическая карта соотношений расширений файлов и их типа
join_threads.h - RAII контейнер потоков, для безопасного их завершения.
splinlock_mutex.h - мутекс основанный на атомарных переменных, по сути спинлок, так как нет приостановки потока и обращения к пространству ядра.
uint_to_str.h(.cpp) - быстрая конвертация беззнакового числа в строку (работает гораздо быстрей тех функций которые предоставляет система).
Код написан целиком с поддержкой C++11 как уже отмечалось выше, поэтому не все конструкции будут понятны человеку не знакомому с этим синтаксисом. В рабочем ядре используется системный модуль epoll и асинхронные сокеты. Для пула потоков использован пример из книги Энтони Уильямса - Concurrency in Action, конвертацию из беззнакового в строку взял на просторах интернета (приписывается разработчикам Facebook).
На этом всё
Фактически это был просто эксперимент, поэтому сразу предупреждаю что код ещё далёк от идеала по производительности и структуре.
Для начала в качестве предисловия
Идея написать свой веб-сервер на С++, пришла не просто так в мою голову, изначально это было просто тестовое задание на разработку веб-сервера для раздачи статического контента на С++ с поддержкой многопоточности. Там же мне пришла идея использовать наработки из С++11 (поэтому данный код собирается уверенно на компиляторах GCC 4.8.4 и старше)
В текущем состоянии кодовой базы на моей машине (Core i5-4210U 1.7 GHz) для тестов на запрос вкомпилированной страницы выдаются следующие результаты (фреймворк для тестирования Apache Benchmark):
Server Software: simple
Server Hostname: 127.0.0.1
Server Port: 8080
Document Path: /
Document Length: 103 bytes
Concurrency Level: 100
Time taken for tests: 4.470 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 18600000 bytes
HTML transferred: 10300000 bytes
Requests per second: 22371.59 [#/sec] (mean)
Time per request: 4.470 [ms] (mean)
Time per request: 0.045 [ms] (mean, across all concurrent requests)
Transfer rate: 4063.59 [Kbytes/sec] received
Для страницы расположенной на диске устройства:
Server Software: simple
Server Hostname: 127.0.0.1
Server Port: 8080
Document Path: /index.html
Document Length: 28914 bytes
Concurrency Level: 100
Time taken for tests: 6.048 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 2900100000 bytes
HTML transferred: 2891400000 bytes
Requests per second: 16535.44 [#/sec] (mean)
Time per request: 6.048 [ms] (mean)
Time per request: 0.060 [ms] (mean, across all concurrent requests)
Transfer rate: 468305.09 [Kbytes/sec] received
Что весьма не плохо с учётом того что никаких модулей кэширования не было применено в процессе разработки. Забор файлов идёт используя модуль fstream стандартной библиотеки С++
Общее устройство
Код сервера полностью открыт, лицензия BSD-v3 (поэтому можете копировать и использовать код под свои нужды). Ссылка на репозиторий гитхаб: https://github.com/no111u3/simple_web
Рабочий код разбит на модули:
simple_web.h(.cpp) - запуск веб-сервера, выполнение конфигурации
core.h(.cpp) - запуск ядра сервера
daemon.h - переключение в режим демона ОС Linux, в соответствии с рекомендациями
config.h(.cpp) - разбор настроек сервера и их хранение в течении всей работы
polling.h(.cpp) - ядро обслуживания мастер-сокета.
handle_message.h(.cpp) - ядро обслуживания клиентских сокетов
Данный код может работать как однопоточном так и в многопоточном режиме. За создание потоков, планирование задач и создание новых отвечает следующие модули:
thread_pool.h(.cpp) - рабочий пул потоков с возможностью добавления задач и разделения их по очередям (локальная очередь потока и общая очередь).
function_wrapper.h - контейнер для корректного запуска/завершения задачи передаваемой в пул.
thread_safe_queue.h - общая очередь для разделения задач по потокам.
work_stealing_queue.h - очередь потока, с поддержкой заимствования
Также существует ряд сервисных модулей, без которых устройство кода было бы не таким компактным:
content_type_identify.h - статическая карта соотношений расширений файлов и их типа
join_threads.h - RAII контейнер потоков, для безопасного их завершения.
splinlock_mutex.h - мутекс основанный на атомарных переменных, по сути спинлок, так как нет приостановки потока и обращения к пространству ядра.
uint_to_str.h(.cpp) - быстрая конвертация беззнакового числа в строку (работает гораздо быстрей тех функций которые предоставляет система).
Код написан целиком с поддержкой C++11 как уже отмечалось выше, поэтому не все конструкции будут понятны человеку не знакомому с этим синтаксисом. В рабочем ядре используется системный модуль epoll и асинхронные сокеты. Для пула потоков использован пример из книги Энтони Уильямса - Concurrency in Action, конвертацию из беззнакового в строку взял на просторах интернета (приписывается разработчикам Facebook).
На этом всё
Комментариев нет:
Отправить комментарий