Информация об изменениях

Сообщение Re[3]: fir: другая HTTP/WebSocket либа с zero-copy внутри бе от 20.10.2017 11:18

Изменено 20.10.2017 11:26 pkl

Re[3]: fir: другая HTTP/WebSocket либа с zero-copy внутри бе
Здравствуйте, MTD, Вы писали:

MTD>Здравствуйте, pkl, Вы писали:


pkl>>Входной HTTP/1.1 протокол парсится с минимальным кол-вом копирований данных.

pkl>>Распарсенный HTTP представляет собой просто набор указателей на разные места во входном буфере, куда данные залетели изначально.
pkl>>Нет привязки к boost или другой сетевой библиотеке, на нижнем уровне лежит класс TCP сервера, реализованный на epoll, но можно подменить что угодно своё с похожим API.
pkl>>Написано по мотивам внутренностей nginx. Минимум new/delete, всё переиспользуется.

MTD>Крайне интересно звучит. Какие планы по развитию или чисто just for fun? Обработчики запросов где и как обрабатываются?

Just For Fun конечно, я есть частное лицо, а не организация. Планы могут быть когда ты можешь их гарантировать, а что может гарантировать частное лицо без договора.
Примерные планы — юзать как основное сетевое двигло в своих fun-проектах, дорабатывать по мере надобности, рефакторить как накатит волна.

TCPServerEpoll — это шаблонный класс, куда нужно передать класс фабрики ресурсов. Когда приходит TCP-коннект, то TCPServerEpoll лезет в фабрику ресурсов и говорит "дай объект коннекта для типа 70", где 70 — это то что указал юзер при открытии порта на прослушивание. Объект может быть new или взят из списка свободных (кеш).

Фабрика понимает, что 70 — это HTTP коннект, например. Там их всего несколько типов из коробки, есть например примитивный "bin" протокол для общения демонов между собой. Все коннекты наследуют класс Connection, методы которого eventRead(), eventWrite(), eventClose() и дёргает TCPServerEpoll при соответствующих событиях.

Далее внутри каждого конкретного дочернего типа Connection есть свои eventRead() и т.п. Но общее одно — сам читай из дескриптора данные куда тебе надо.

Преследуя политику минимального копирования данных, TCPServerEpoll не пытается сам читать данные во временные буферы, которые потом отдаёт абстрактному коннекшну. Сделано как в nginx: пускай коннекшн сам решает надо ли ему вообще читать эти данные и пускай сам читает СРАЗУ в тот буфер где они будут нужны и столько сколько нужно, а не занимается копированием из нашего буфера. Поэтому в ConnectionHTTP например, в зависимости от состояния (что мы сейчас пытаемся прочитать — хидеры или BODY) данные читаются в разные буферы и т.п. Вот стартовая процедура чтения данных в ConnectionHTTP https://github.com/pavelkolodin/fir/blob/master/net/connection_http.h#L285 — она читает и разбирает headers.

Есть понятие Responder. Это тип "отвечальщик" — там "хендлеры" и живут. Responder передаётся как шаблонный в фабрику. Когда ConnectionHTTP допринимал запрос и понял что запрос нормальный, он говорит фабрике — дай Responder, я буду отвечать! Далее делается что-то типа responder->dataHTTP(this) и ответ отправляется обратно. У Responder должны быть методы dataHTTP, dataWS, dataBin, closeHTTP, closeWS, closeBin иначе не скомпилицца. Это и есть хендлеры. В них тебе дают голые headers, cookies, body и делай с ними что хочешь. Есть там какой-то ответный буфер, туда можно запилить ответ и вернуться, а ConnectionHTTP сам отправит ответ.

ConnectionHTTP на конечных автоматах — данные могут приходить любыми кусочками.

Некоторые штуки не реализованы — например нет поддержки gzip, нет HTTP Range_requests, нет ещё какой-то важной фигни. Зато из коробки есть miltipart/form-data -- можно запощенные HTTP-формочки парсить.

ConnectionWebSocket реализован тоже на конечных автоматах, работает стабильно. В нём есть пара лишних копирований данных (из-за непоняток с вопросами владения буферами). Также в нём отсутствует пара каких-то второстепенных моментов, типа отработки PING\PONG, хотя может и запилено — не помню. В продакшене на http://fintank.ru этот ConnectionWebSocket вроде стабильно работает часами, но я бы над ним ещё поработал.

В целом fir надо немного отрефакторить, там часть написана когда я ещё малолетним дебилом был — под c++14 надо причёсывать, про кеш CPU подумать, lock-free внедрить, мьютексов дурацких повыкидывать если есть...
Re[3]: fir: другая HTTP/WebSocket либа с zero-copy внутри бе
Здравствуйте, MTD, Вы писали:

MTD>Здравствуйте, pkl, Вы писали:


pkl>>Входной HTTP/1.1 протокол парсится с минимальным кол-вом копирований данных.

pkl>>Распарсенный HTTP представляет собой просто набор указателей на разные места во входном буфере, куда данные залетели изначально.
pkl>>Нет привязки к boost или другой сетевой библиотеке, на нижнем уровне лежит класс TCP сервера, реализованный на epoll, но можно подменить что угодно своё с похожим API.
pkl>>Написано по мотивам внутренностей nginx. Минимум new/delete, всё переиспользуется.

MTD>Крайне интересно звучит. Какие планы по развитию или чисто just for fun? Обработчики запросов где и как обрабатываются?

Just For Fun конечно, я есть частное лицо, а не организация. Планы могут быть когда ты можешь их гарантировать, а что может гарантировать частное лицо без договора.
Примерные планы — юзать как основное сетевое двигло в своих fun-проектах, дорабатывать по мере надобности, рефакторить как накатит волна.

TCPServerEpoll — это шаблонный класс, куда нужно передать класс фабрики ресурсов. Когда приходит TCP-коннект, то TCPServerEpoll лезет в фабрику ресурсов и говорит "дай объект коннекта для типа 70", где 70 — это то что указал юзер при открытии порта на прослушивание. Объект может быть new или взят из списка свободных (кеш).

Фабрика понимает, что 70 — это HTTP коннект, например. Там их всего несколько типов из коробки, есть например примитивный "bin" протокол для общения демонов между собой. Все коннекты наследуют класс Connection, методы которого eventRead(), eventWrite(), eventClose() и дёргает TCPServerEpoll при соответствующих событиях.

Далее внутри каждого конкретного дочернего типа Connection есть свои eventRead() и т.п. Но общее одно — сам читай из дескриптора данные куда тебе надо.

Преследуя политику минимального копирования данных, TCPServerEpoll не пытается сам читать данные во временные буферы, которые потом отдаёт абстрактному коннекшну. Сделано как в nginx: пускай коннекшн сам решает надо ли ему вообще читать эти данные и пускай сам читает СРАЗУ в тот буфер где они будут нужны и столько сколько нужно, а не занимается копированием из нашего буфера. Поэтому в ConnectionHTTP например, в зависимости от состояния (что мы сейчас пытаемся прочитать — хидеры или BODY) данные читаются в разные буферы и т.п. Вот стартовая процедура чтения данных в ConnectionHTTP https://github.com/pavelkolodin/fir/blob/master/net/connection_http.h#L285 — она читает и разбирает headers.

Есть понятие Responder. Это тип "отвечальщик" — там "хендлеры" и живут ( пример Responder ) Responder передаётся как шаблонный в фабрику. Когда ConnectionHTTP допринимал запрос и понял что запрос нормальный, он говорит фабрике — дай Responder, я буду отвечать! Далее делается что-то типа responder->dataHTTP(this) и ответ отправляется обратно. У Responder должны быть методы dataHTTP, dataWS, dataBin, closeHTTP, closeWS, closeBin иначе не скомпилицца. Это и есть хендлеры. В них тебе дают голые headers, cookies, body и делай с ними что хочешь. Есть там какой-то ответный буфер, туда можно запилить ответ и вернуться, а ConnectionHTTP сам отправит ответ.

ConnectionHTTP на конечных автоматах — данные могут приходить любыми кусочками.

Некоторые штуки не реализованы — например нет поддержки gzip, нет HTTP Range_requests, нет ещё какой-то важной фигни. Зато из коробки есть miltipart/form-data -- можно запощенные HTTP-формочки парсить.

ConnectionWebSocket реализован тоже на конечных автоматах, работает стабильно. В нём есть пара лишних копирований данных (из-за непоняток с вопросами владения буферами). Также в нём отсутствует пара каких-то второстепенных моментов, типа отработки PING\PONG, хотя может и запилено — не помню. В продакшене на http://fintank.ru этот ConnectionWebSocket вроде стабильно работает часами, но я бы над ним ещё поработал.

В целом fir надо немного отрефакторить, там часть написана когда я ещё малолетним дебилом был — под c++14 надо причёсывать, про кеш CPU подумать, lock-free внедрить, мьютексов дурацких повыкидывать если есть...