Комментарии 57
Расскажи, что за магия с отложенным вызовом функции при логировании
#define LOG_DEBUG() if (GetCurrentLogLevel() >= debug_level) std::cout
Так что если уровень логирования недостаточный — в ветку if не заходим и никакие функции не выполняем.
* только вместо std::cout у нас пара наворотов, чтобы побыстрее формировать строчку (без динамических аллокаций, std::locale и прочего безобразия) и логировать её асинхронно. На C++ Piter я мельком показывал, как оно сделано под капотом.
log_debug("{} is not OK of {}", request.id, GetSomeInfoFromDb());
и не вычислять GetSomeInfoFromDb() если логировать не нужно.logger.debug([&](auto stream){ auto value = heavyCalculation(); stream << "Value is" << value; });
Как атомарно записать в конец файла в таком случае?
Ядро точно даёт гарантии без msync()?
У меня в файле 1000 байтов логов. Поток А хочет залоггировать 100 байтов. Поток Б — 200 байтов. Я хочу увидеть в итоге файл на 1300 байтов, а в нём в конце две записи, в каком угодно порядке, но отдельно. А не так, что файл будет на 1100 байтов и в нём — половина лога Б.
open() с O_APPEND и write() атомарны при небольших записях (страница памяти). Как это можно сделать через mmap() и запись в память напрямую?
По идее, держать длину лога рядом в атомарной переменной и когда отображаемый кусок подходит в концу, то придётся блокировать запись в мьютексе, пока там файлу увеличивают размер и меняют маппинг в памяти.
Впрочем, на случай недостатка места в отображаемом куске можно условный memcpy() делать в дополнительный буфер-очередь (просто в памяти), а расширение файла вынести в отдельный поток, который после завершения работы подберёт всё из буфера. Лишь бы буфера хватило.
Блокировка всё равно понадобится (в коде), пусть и в оптимистичном случае мы на неё никогда не натолкнёмся. У меня есть маппинг на 1 МБ, куда сейчас пишут. Есть уже готовый маппинг ещё на 1 МБ, куда мы переключимся, когда первый закончится. И за то время, пока заполняется новый маппинг, надо успеть подготовить следующий ему на смену. Но если мы не успеем, то придётся идти и ждать.
Плюс должны быть гарантии, что старый маппинг на 1 МБ не освободят, пока в него не дозаписали все, кто увидел его до переключения. Но это, мне кажется, тоже атомиками можно как-то разрулить.
А насчёт падений, кстати, классно. Я почитал, что Линкус действительно даёт гарантию, что отображаемые страницы останутся в страничном кеше и — если только система не упадёт или там питание не отключится — то если поток записал что-то в эту память, то оно (со временем) дойдёт до диска. Даже если процесс пристрелит SIGKILL посреди memcpy().
Это всё предполагает, что страницы реально в физической памяти, для этого и всякие mlock() нужны.
* там только http сервер (нет баз данных, логирования и прочего)
* нет асинхронности, точнее её можно реализовать через цепочки фьючеров
response
.send(Http::Code::Request_Timeout, "Timeout")
.then([=](ssize_t) { }, PrintException());
Другими словами, pistache.io скорее просто библиотека, а не фреймворк. При этом с ней необходимо использовать callbacks. Вы не сможете писать высокопроизводительный код, который будет выглядеть как обычный синхронный код.
Сколько памяти выделяется на стек корутины?
Так всё-таки, сколько?
Поддерживаю вопрос, очень интересно.
Непонятно, что делать со всей информацией, как это всё использовать, если нет сорцов?
Всё же сильно больше чем начальные 3к в горутинах...
За это потом придется расплачиваться разделением стека
Какие получились накладные расходы по ЦПУ и памяти? Какой прирост с существующим опенсорсом?
Для нормального фреймворка вам мало собрать популярные библиотеки в один проект. Вам важно подружить их, предоставить консистентные интерфейсы, наладить системы сборки, реализовать возможности которых нет у других. Корутиновый движок — малая часть фреймворка.
Если говорить только про корутиновый движок, то например в Boost.Fibers крайне простой шедулер. Нам он не подходит, а компоненты для построения более сложных шедулеров отсутствуют в Boost. И принести их в Boost нельзя, а значит и заапстримить изменения. В Boost так же нет части примитивов синхронизации, wait list примитивов плохо кастомизируются, что мешает их эффективной реализации и т.д. и т.п… В итоге, от изначального кода практически ничего не остаётся.
Пример маловат, не ясно что под капотом.
На чём реализована работа с file handles (sockets) в плане мултиплексирования (epoll/poll/select)?
Можно-ли добавлять собственные классы (от чего наследоваться), для мултиплексирования?
EDIT:
HTTP/2 поддерживается?
Кто слышал про подобное на питоне?
asyncio же?
asyncio — это совсем не то же самое, о чём идет речь в этой статье и о чём вопрос выше, как мне кажется. Это просто низоуровневая библиотека, механизм если хотите, для поддержки асинхронности и кооперативной многозадачности на уровне языка. В нём нет ни http-сервера/клиента, асинхронных драйверов к БД, быстрого event loop и всего остального о чём тут написано. Чтобы собрать подобный стек понадобится кучка сторонних библиотек поверх asyncio, вроде aiohttp и т.д. И надо заметить, asyncio далеко не идеален. Посмотрите в сторону trio. Это асинхронность с человеческим лицом. :)
И...? Я знаю про uvloop и асинхронные драйверы для БД. :) И это никак не противоречит тому, что я написал выше. Всё это отдельные библиотеки, которые надо собирать в стек. Тут же представлен фреймворк (не библиотека), который может сразу всё это из коробки.
Они предназначены для asyncio и отлично собираются в быстрый стек) Проблема асинхронности и кооперативной многозадачности для Питона в принципе уже решена — asyncio, gevent, stackless в PyPy, так что нужды в очередном асинхронном монофрейморке нет.
А моя реплика не в укор, а просто так. Эта ветка уже пустилась в пространные рассуждения)
Под такое описание может попасть Starlette вместе с FastAPI и databases. Их делают одни и те же люди, оно всё внутри хорошо друг с другом интегрировано.
- Очень интересно про распределенных блокировки.
- Pocoproject содержит ту же функциональность (кроме распределенных блокировок), но там c++ не современный, так что, если уж в комментариях спрашивали про сравнение с другими фреймворками, то спрошу и я, чем лучше userver по сравнению с poco?
А как корутина понимает, что данный ответ на тот самый tcp запрос?
Получается, что если мы хотим работать с короутинами в одном соединении с множеством запросов — нам придется городить некий дополнительный функционал в протоколе?
Какой менеджер пакетов используете и используете ли? (Conan, vcpkg, qpm, что-то свое)
Корутины свои запилили или взяли из Boost?
Очень интересная для меня тема. Сейчас используем Kotlin/JVM в проде для микросервисов, но из-за JVM они не совсем «микро». Go идеален в плане минимальных системных требований, но как язык я его не приемлю. Посматриваю на плюсы в качестве базы для микросервисов, но видны следующие недостатки:
— нет интроспекции
— пакетные менеджеры не распространены и в тех что есть мало библиотек
— нет некоторых нужных библиотек (GraphQL например)
Используем скрипты CMake, которые при отсутствии нужных библиотек говорят какие системные команды надо выполнить, чтобы их поставить (например 'No compiler found. Please run `sudo apt install clang++`').
Корутины базируются на библиотеке Boost.Context + lock-free библиотеки + Boost библиотеки для умных указателей и интрузивных контейнеров.
С интроспекцией в C++17 и правда плохо, но для некоторых вещей хорошо подходит библиотека magic_get или constexpr функции с «рукописной помощью» для интроспекции.
Лучше придерживаться стиля snake_case из std::
Кодстайл это, кажется, одна из основных болей всех проектов, которые хотят заопенсорситься.
А какое у вас отношение к stackless coroutines? Не получится ли так что вот у вас есть фреймворк для stackfull, который надо будет переписать на stackless?
Это наверное старые новости, но недавно видел упоминание на stackless vs stackfull у Raymond Chen https://devblogs.microsoft.com/oldnewthing/20191011-00/?p=102989 в котором была ссылка на статью Gor Nishanov http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p1364r0.pdf где вкратце он топит за то что stackless считается более перспективным чем stackfull. В частности он там пишет что Facebook пытается уйти от stackfull по ряду причин и такую же штуке сделали в Rust.
* stackfull позволяют пользователям фреймворка не задумываться о внутренней реализации, о co_await, co_return
* stackless быстрее отменять и они расходуют меньше оперативной памяти
Возможно мы когда-нибудь сделаем возможность пользоваться во фреймворке сразу обоими разновидностями корутин. Но это не приоритетная задача.
Хм, а есть какая-то связь с https://userver.uwaterloo.ca/? А то это первая ссылка, что даёт гугл по запросу "userver".
Просто и на C++. Основы userver — фреймворка для написания асинхронных микросервисов