Тут использовано допущение, что код H можно вставить в P, но реальные программы гораздо сложнее, чем просто "код". Более того, код H может быть "написан" вообще на мета-архитектуре, которую невозможно принципиально встроить ни в какие P. Например, на квантовых эффектах. Или на принципиально отличающейся архитектуре, которую невозможно описать кодом. Формально это будет некий "чёрный ящик", на вход которого подаётся что-то и получается ответ и единственным способом получить ответ H - вызвать H, но это уже исполнение, что P делать не может (она содержит только код). Причём сам "вызов" H может происходить способом, совершенно произвольным и который к компьютерам вообще не имеет отношения. То есть Тьюринг был не прав, если говорить о задаче как "Можно ли создать некую программу, которая получит на входе набор код + входные данные и ответит гарантировано и верно зациклится или нет".
Более того, H может содержать реальный настоящий рандом, ей это по условию можно, а вот P нельзя. Но я пока не развивал эту теорию.
Так же H может возвращать разные данные, выполняется она сама по себе или в составе другой программы.
H может переписывать себя, может являться неким AGI с доступом к производственным мощностям и так далее.
То ли написано некорректно, то ли я тупой, но я не вижу тут никаких противоречий.
Задача поставлена корректно - некая программа (P), которая может делать анализ кода и входных данных другой программы (H) и отвечать на вопрос зациклится H или нет.
А дальше в мысленном эксперименте делается допущение, что программу H можно сделать такой, чтобы она отвечала обратное от P(H), но с чего все решили что это вообще возможно? Это нарушает причинно/следственные связи как минимум, такая программа не возможна в принципе, это похоже на парадокс брадобрея или парадокс классификаторов.
Более того, с точки зрения P выполняемость условий для H зависит только от её кода и входных данных и не требует выполнения H как таковой.
А вот по условиям «противоречия» нужно чтобы H ВЫПОЛНИЛО P(H), но в КОДЕ это невозможно задать динамически. Это всё-равно будут конкретные байты.
Что бы там H не решила по поводу P(H) ее код на момент анализа его программой P будет фиксирован и не может меняться во время анализа.
Так что как минимум это «противоречие» не верно.
Может кто-нибудь более умный что-ли пояснить где я тут не прав?
Да, вы писали "Время просчёта боя сократилось с двух с половиной секунд до одной", теперь я вижу, что у вас 500 RPS, что уже неплохо, значит не совсем всё ужасно, но всё же - очень мало для 10 раундов на 16 ядрах.
Вы так же пишете, что бой это все действия, в том числе "чтение состояния из БД". Работа с БД при правильной организации кэширования и загрузки игроков не должна появляться в таких событиях, как расчёт боя. Либо вы кэшируете не всё или не так.
И что вы тогда понимаете под микросервисами? То, что вы пописали это не микросервисная архитектура, о которой вы писали в самой статье. Это просто монолит + сервисы. Для того, чтобы сказать что у нас микросервисная архитектура недостаточно сделать 1 микросервис :) Иначе любой монолит + база можно назвать "микросервисной архитектурой".
Я там опечатался, имел ввиду "Хотя, зачем вы вообще отправляете ОТ серверА большой лог в JSON?". Бой довольно частая операция, я бы пожалел игроков и не стал бы их заставлять качать большие логи боёв в JSON, потом ещё распаковывать их (если там gzip/zstd) и парсить.
JSON сам по себе довольно медленный, особенно если у вас фиксированный формат протокола. У нас на одной игре вдруг оказалось что 50% всего CPU сервера с 32 ядрами занимает парсинг JSON-ответов соц. сетей, особенно Mail.ru, там они иногда очень большие. И хотя там и было больше 10k RPS, всё-равно исправили. У вас вряд-ли такая же картина, это чисто для понимания.
Свой протокол это не так страшно и поддержка нулевая, если всё сделать правильно. Но переходить к этому этапу нужно только после анализа где же конкретно проблемы с производительностью. А так никакое сжатие не даст тех же результатов по скорости и трафику.
Это очень круто, но это не самый быстрый алгоритм :(
Самый быстрый, не знаю, придумали ли еще, такой:
Делаем статический скомпилированный массив соответствий unix_timestamp -> конкретная дата
Далее просто возвращаем дату по нужному индексу в этом массиве.
Но пока что не хватает памяти у компьютеров на этот алгоритм. Но я предсказываю, что с оптимизациями и в будущем то, что я написал станет новым самым быстрым алгоритмом.
Возможно, что с оптимизациями уже будет помещаться в память, кстати.
Я не думаю, что вы когда либо достигнете тех порядков траффика и сложности архитектуры, при которой микросервисы оправдав. Многие сейчас их используют просто потому что это модно :)
«Чтение состояние из БД» - не должно быть на каждый бой. У вас 128GB RAM, сделайте нормальное кэширование без вытеснения.
Формирование JSON тоже не должно быть долгим, не знаю на чем написан сервер у вас, если на C++, то рекомендую rapidjson, хотя есть и быстрее. Хотя, зачем вы вообще отправляете серверу большой лог в JSON? К вашему архитектору всё больше вопросов. Ладно protobuf, но можно и свой протокол обмена данными создать. Бинарный и эффективный.
Я игровыми серверами занимаюсь уже больше 10 лет, какие только не делал и для шутеров с UDP и для пошаговых игр и для онлайн игр с комнатами и весь мой опыт говорит о том, что у вас что-то не так. Подозреваю, что микросервисная архитектура у вас применяется для галочки, без понимания какие проблемы она решает и как она работает, поэтому и вылезла, например, загрузка чего-то при каждой битве.
Как вы собираетесь это всё масштабировать дальше? 1 бой - 1 секунда что бы там внутри не было сейчас. 32 ядра - 32rps по боям. 10000 онлайна которые вы прогнозируете даст, ну пусть 1/10 боёв в секунду. Это 1000rps, вам нужно 30 серверов чтобы тянуть только бои…
Я бы не стал сейчас заморачиваться с микросервисами, вам монолита боёвка + кэширование данных игроков хватить на онлайн до 10k спокойно на текущем железе (если сделать все правильно).
Масштабирование дальше можно решить шардингом по серверам внутри игры (если смотреть на то, как обычно подобные игры делают сейчас, то игроков всё-равно надо как-то разделять по времени). 2х еще легко выжать с нормальным серверным железом.
В общем, там целый список решений которые делаются легче, быстрее и дешевле, чем любая возня с микросервисами.
У вас явно какая-то проблема с алгоритмами. В описанных вами задачах такой сервер как вы выбрали должен был считать бой за 0.01 секунду и меньше, а не за 1.
И зачем вам на таком небольшом трафике городить микросервисы - вопрос к вашему архитектору.
500 запросов в секунду на бой с временем ответа в 1 секунду… У вас там либо 500 ядер на сервер либо просчет боя сделан максимально криво либо вы что-то напутали или наврали в тексте :)
Подсветка нужна чтобы взглянув на 1 выражение быстро найти ВСЕ части выражения.
Отдельно для каждого выражения.
Функции в целом или даже файлы подсветкой не определяются. Для этого используют форматирование и отступы.
В рамках одного выражения до 7 цветов нормально, так как их смысл не в том, чтобы быстро по цвету находить что-то (например, строку), а чтобы цвета помогли мозгу быстро разбить выражение на части. Отделить разные части callchain, аргументы от имени функции, await от const и так далее.
И у вас там еще одна ошибка - слишком светлый фон.
Около 20 лет назад еще писал на PHP сайт (softodrom) и свою CMS для него, которая собирала страницу и кэшировала ее в memcached вместе с некоторыми метаданными, такие как параметры запроса и внутренние связи между данными.
Например, на странице мог быть блок с данными, которые обновляются по крону или через админку, страница знает какие данные на ней присутствуют и пересобирает себя если обновилось что-то из них, но не весь контент, а только эту часть. Там так же используется свой шаблонизатор, он строит что-то типа AST, кэширует его и пересобирает только ту часть, которая изменилась.
Профилировал весь движок так, чтобы он работал практически мгновенно, еще пришлось Linux + KDE установить, потому что Flamegraph красиво отображать тогда умело только что-то под KDE :)
Были планы еще переписать движок как расширение на PHP, но уже не стал.
В C++ нет полноценного “async/await” как законченной модели исполнения. Есть низкоуровневые корутины (co_await/co_yield) - синтаксический сахар без Future/Executor/IO-рантайма, которое нужно всё написать самому, там же и настраивается поведение в каком потоке проснется код после co_await, но тут даже по имени видно "co_await" != "await", о чём я и говорил. Я не утверждал, что в C++ нет ничего похожего по поведению на .await в Rust.
И в любом случае, даже в C++ корутины в продакшене плохо - они делают код некрасивым, сложным, медленным (без шаманств). И даже с этими всеми недостатками гибкость C++ выше на голову.
В С++ fetch выведет одинаковые thread_id если запустить вот так:
asio::io_context io;
auto ex = asio::make_strand(io);
co_spawn(ex, fetch(), detached);
io.run();
В C++ вообще мьютексы на общие данные внутри корутин использовать не очень хорошо, будут проблемы, но тем не менее это можно сделать корректно, например, организовав шардинг задач по id (если вся работа с локами только внутри корутин) или используя await-able мьютексы.
Выстрелить себе в ногу - это больше про интеллект, чем про ограничения и возможности языка. Тот же нож в руках у идиота тоже может делов натворить и что, из-за пары идиотов делать все ножи тупыми?
await на JS не меняет поток :) await в C++ нет. И да, я предпочитаю когда я точно знаю какой поток что выполняет, а не рандом.
Ради эксперимента можем посоревноваться, я пишу на C++, а вы на Rust и сравниваем скорость разработки, быстродействие программы, безопасность, архитектуру, простоту поддержки и красоту кода. C++ сделает Rust.
Хороший паттерн блокировок в C++ - scoped_lock, unique_lock, никто уже давно не лочит мьютексы сам. Приходится захватывать мьютекс в одном месте, а освобождать в другом = архитектура кусок говно. Раньше я и без них не делал так, чтобы лок / анлок был в разных функциях
Только взглянул на ваш код на TS сразу увидел проблему. С таким сталкивался еще в школе 25 лет назад…
Пример на Rust как раз показывает всю ущербность Rust. Сам себе создал проблему на ровном месте (await, отсутствие контроля за потоками), сам ее нашел еще и в другом месте и героически не дал собрать проект.
Напомнило C++ 2000-х годов, где сообщение об ошибке ссылалось на что угодно, только не на строку с проблемой.
На C++ за десятилетия программирования я ни разу не столкнулся с ошибкой что освободил мьютекс из другого потока…
Дедлоки - да, но они и в расте могут быть.
И за этим всем на самом деле скрывается некачественная архитектура проекта. Блокировать мьютекс надо только на составление запроса, но не на выполнение (с оговорками на случай больших транзакций с откатами).
На нашем сервере я сделал несколько перехватчиков scoped_lock, unique_lock и т.д. + некий measure_scope.
Все данные с номерами строк и именами файлов записываются в tbb::concurrent_queue и в отдельных потоках сохраняются в ClickHouse.
Оверхед ~0, точность идеальная, вплоть до отдельных строк кода, отдельно по блокировкам - видно сколько времени блокировка ожидалась, сколько держалась, количества вызовов и так далее.
Миллиарды записанных событий, данные в ClickHouse занимают до сотни гигабайт. Строятся гистограммы распределения времени, считаются экстремумы.
Визуализация в datalens, можно настраивать так же алерты, подключать ИИ к анализу.
А файлы… Если вы такое всерьез рассматривали в 2025 году, то забудьте про ИТ и идите пасти коров, что-ли…
Зачем? Как это делать написано уже 100 раз до меня, просто возьмите архитектуру любого http сервера, например, nginx. STL + pthreads + OpenSSL + libev + Intel TBB + libfmt + rapidjson - основа.
Для бинарного протокола используем ULEB-кодирование и свой формат описания пакетов.
Парсер HTTP свой, для API-сервера не нужна полная поддержка протокола.
SSL проксирование через CloudFlare или через nginx, он же может и корректность протокола проверять.
У нас на этом написано несколько бэкэндов онлайн-игр, API-сервера для аналитики, различные сервисы.
Базовая часть, эквивалентная тому, что описана в статье пишется за день, остальное все время уже уходит на бизнес-логику.
БД ClickHouse + MySQL, адаптер самописный.
Я так же написал свое расширение для ClickHouse для PHP (оно открытое), которое работает с ним по нативному протоколу и тоже базовая часть заняла около суток, остальное время уже реализация дополнительного функционала.
Тут использовано допущение, что код H можно вставить в P, но реальные программы гораздо сложнее, чем просто "код". Более того, код H может быть "написан" вообще на мета-архитектуре, которую невозможно принципиально встроить ни в какие P. Например, на квантовых эффектах. Или на принципиально отличающейся архитектуре, которую невозможно описать кодом. Формально это будет некий "чёрный ящик", на вход которого подаётся что-то и получается ответ и единственным способом получить ответ H - вызвать H, но это уже исполнение, что P делать не может (она содержит только код). Причём сам "вызов" H может происходить способом, совершенно произвольным и который к компьютерам вообще не имеет отношения.
То есть Тьюринг был не прав, если говорить о задаче как "Можно ли создать некую программу, которая получит на входе набор код + входные данные и ответит гарантировано и верно зациклится или нет".
Более того, H может содержать реальный настоящий рандом, ей это по условию можно, а вот P нельзя. Но я пока не развивал эту теорию.
Так же H может возвращать разные данные, выполняется она сама по себе или в составе другой программы.
H может переписывать себя, может являться неким AGI с доступом к производственным мощностям и так далее.
То ли написано некорректно, то ли я тупой, но я не вижу тут никаких противоречий.
Задача поставлена корректно - некая программа (P), которая может делать анализ кода и входных данных другой программы (H) и отвечать на вопрос зациклится H или нет.
А дальше в мысленном эксперименте делается допущение, что программу H можно сделать такой, чтобы она отвечала обратное от P(H), но с чего все решили что это вообще возможно? Это нарушает причинно/следственные связи как минимум, такая программа не возможна в принципе, это похоже на парадокс брадобрея или парадокс классификаторов.
Более того, с точки зрения P выполняемость условий для H зависит только от её кода и входных данных и не требует выполнения H как таковой.
А вот по условиям «противоречия» нужно чтобы H ВЫПОЛНИЛО P(H), но в КОДЕ это невозможно задать динамически. Это всё-равно будут конкретные байты.
Что бы там H не решила по поводу P(H) ее код на момент анализа его программой P будет фиксирован и не может меняться во время анализа.
Так что как минимум это «противоречие» не верно.
Может кто-нибудь более умный что-ли пояснить где я тут не прав?
Почему никто не заметил, что сервера возвращают разные по длине ответы?
Go-Server 9 байт
Python-Server 13 байт, на 44% больше!
Вы не путайте божий дар c яичницей. Хэширование это отображение N -> 1, по хэшу не восстанавливается файл однозначно. А тут всё работает однозначно.
Это уже технические моменты, пусть инженеры разбираются :) Главное то алгоритм есть и рабочий :))))
Да, вы писали "Время просчёта боя сократилось с двух с половиной секунд до одной", теперь я вижу, что у вас 500 RPS, что уже неплохо, значит не совсем всё ужасно, но всё же - очень мало для 10 раундов на 16 ядрах.
Вы так же пишете, что бой это все действия, в том числе "чтение состояния из БД". Работа с БД при правильной организации кэширования и загрузки игроков не должна появляться в таких событиях, как расчёт боя. Либо вы кэшируете не всё или не так.
И что вы тогда понимаете под микросервисами? То, что вы пописали это не микросервисная архитектура, о которой вы писали в самой статье. Это просто монолит + сервисы. Для того, чтобы сказать что у нас микросервисная архитектура недостаточно сделать 1 микросервис :) Иначе любой монолит + база можно назвать "микросервисной архитектурой".
Я там опечатался, имел ввиду "Хотя, зачем вы вообще отправляете ОТ серверА большой лог в JSON?". Бой довольно частая операция, я бы пожалел игроков и не стал бы их заставлять качать большие логи боёв в JSON, потом ещё распаковывать их (если там gzip/zstd) и парсить.
JSON сам по себе довольно медленный, особенно если у вас фиксированный формат протокола. У нас на одной игре вдруг оказалось что 50% всего CPU сервера с 32 ядрами занимает парсинг JSON-ответов соц. сетей, особенно Mail.ru, там они иногда очень большие. И хотя там и было больше 10k RPS, всё-равно исправили. У вас вряд-ли такая же картина, это чисто для понимания.
Свой протокол это не так страшно и поддержка нулевая, если всё сделать правильно. Но переходить к этому этапу нужно только после анализа где же конкретно проблемы с производительностью. А так никакое сжатие не даст тех же результатов по скорости и трафику.
Сервера у нас свои, на C++, для своих игр :)
Это очень круто, но это не самый быстрый алгоритм :(
Самый быстрый, не знаю, придумали ли еще, такой:
Делаем статический скомпилированный массив соответствий unix_timestamp -> конкретная дата
Далее просто возвращаем дату по нужному индексу в этом массиве.
Но пока что не хватает памяти у компьютеров на этот алгоритм. Но я предсказываю, что с оптимизациями и в будущем то, что я написал станет новым самым быстрым алгоритмом.
Возможно, что с оптимизациями уже будет помещаться в память, кстати.
Я не думаю, что вы когда либо достигнете тех порядков траффика и сложности архитектуры, при которой микросервисы оправдав. Многие сейчас их используют просто потому что это модно :)
«Чтение состояние из БД» - не должно быть на каждый бой. У вас 128GB RAM, сделайте нормальное кэширование без вытеснения.
Формирование JSON тоже не должно быть долгим, не знаю на чем написан сервер у вас, если на C++, то рекомендую rapidjson, хотя есть и быстрее. Хотя, зачем вы вообще отправляете серверу большой лог в JSON? К вашему архитектору всё больше вопросов. Ладно protobuf, но можно и свой протокол обмена данными создать. Бинарный и эффективный.
Я игровыми серверами занимаюсь уже больше 10 лет, какие только не делал и для шутеров с UDP и для пошаговых игр и для онлайн игр с комнатами и весь мой опыт говорит о том, что у вас что-то не так. Подозреваю, что микросервисная архитектура у вас применяется для галочки, без понимания какие проблемы она решает и как она работает, поэтому и вылезла, например, загрузка чего-то при каждой битве.
Как вы собираетесь это всё масштабировать дальше? 1 бой - 1 секунда что бы там внутри не было сейчас. 32 ядра - 32rps по боям. 10000 онлайна которые вы прогнозируете даст, ну пусть 1/10 боёв в секунду. Это 1000rps, вам нужно 30 серверов чтобы тянуть только бои…
Я бы не стал сейчас заморачиваться с микросервисами, вам монолита боёвка + кэширование данных игроков хватить на онлайн до 10k спокойно на текущем железе (если сделать все правильно).
Масштабирование дальше можно решить шардингом по серверам внутри игры (если смотреть на то, как обычно подобные игры делают сейчас, то игроков всё-равно надо как-то разделять по времени). 2х еще легко выжать с нормальным серверным железом.
В общем, там целый список решений которые делаются легче, быстрее и дешевле, чем любая возня с микросервисами.
У вас явно какая-то проблема с алгоритмами. В описанных вами задачах такой сервер как вы выбрали должен был считать бой за 0.01 секунду и меньше, а не за 1.
И зачем вам на таком небольшом трафике городить микросервисы - вопрос к вашему архитектору.
500 запросов в секунду на бой с временем ответа в 1 секунду… У вас там либо 500 ядер на сервер либо просчет боя сделан максимально криво либо вы что-то напутали или наврали в тексте :)
Я никогда не вставляю комментарии в середину if. Если мне надо что-то отключить, то я копирую оригинальный if, комментирую копию и меняю оригинал.
А проход по строке и сдвиг символов, если нашелся пробел почему не протестировали?
Аналогично вашему последнему варианту, только inplace
Напишите хотя-бы объемы данных и какая нагрузка :)
Вы неправильно используете подсветку кода.
Подсветка нужна чтобы взглянув на 1 выражение быстро найти ВСЕ части выражения.
Отдельно для каждого выражения.
Функции в целом или даже файлы подсветкой не определяются. Для этого используют форматирование и отступы.
В рамках одного выражения до 7 цветов нормально, так как их смысл не в том, чтобы быстро по цвету находить что-то (например, строку), а чтобы цвета помогли мозгу быстро разбить выражение на части. Отделить разные части callchain, аргументы от имени функции, await от const и так далее.
И у вас там еще одна ошибка - слишком светлый фон.
Около 20 лет назад еще писал на PHP сайт (softodrom) и свою CMS для него, которая собирала страницу и кэшировала ее в memcached вместе с некоторыми метаданными, такие как параметры запроса и внутренние связи между данными.
Например, на странице мог быть блок с данными, которые обновляются по крону или через админку, страница знает какие данные на ней присутствуют и пересобирает себя если обновилось что-то из них, но не весь контент, а только эту часть. Там так же используется свой шаблонизатор, он строит что-то типа AST, кэширует его и пересобирает только ту часть, которая изменилась.
Профилировал весь движок так, чтобы он работал практически мгновенно, еще пришлось Linux + KDE установить, потому что Flamegraph красиво отображать тогда умело только что-то под KDE :)
Были планы еще переписать движок как расширение на PHP, но уже не стал.
В C++ нет полноценного “async/await” как законченной модели исполнения. Есть низкоуровневые корутины (co_await/co_yield) - синтаксический сахар без Future/Executor/IO-рантайма, которое нужно всё написать самому, там же и настраивается поведение в каком потоке проснется код после co_await, но тут даже по имени видно "co_await" != "await", о чём я и говорил.
Я не утверждал, что в C++ нет ничего похожего по поведению на .await в Rust.
И в любом случае, даже в C++ корутины в продакшене плохо - они делают код некрасивым, сложным, медленным (без шаманств). И даже с этими всеми недостатками гибкость C++ выше на голову.
В С++ fetch выведет одинаковые thread_id если запустить вот так:
В C++ вообще мьютексы на общие данные внутри корутин использовать не очень хорошо, будут проблемы, но тем не менее это можно сделать корректно, например, организовав шардинг задач по id (если вся работа с локами только внутри корутин) или используя await-able мьютексы.
Выстрелить себе в ногу - это больше про интеллект, чем про ограничения и возможности языка. Тот же нож в руках у идиота тоже может делов натворить и что, из-за пары идиотов делать все ножи тупыми?
await на JS не меняет поток :) await в C++ нет. И да, я предпочитаю когда я точно знаю какой поток что выполняет, а не рандом.
Ради эксперимента можем посоревноваться, я пишу на C++, а вы на Rust и сравниваем скорость разработки, быстродействие программы, безопасность, архитектуру, простоту поддержки и красоту кода. C++ сделает Rust.
Хороший паттерн блокировок в C++ - scoped_lock, unique_lock, никто уже давно не лочит мьютексы сам. Приходится захватывать мьютекс в одном месте, а освобождать в другом = архитектура кусок говно. Раньше я и без них не делал так, чтобы лок / анлок был в разных функциях
Только взглянул на ваш код на TS сразу увидел проблему. С таким сталкивался еще в школе 25 лет назад…
Пример на Rust как раз показывает всю ущербность Rust. Сам себе создал проблему на ровном месте (await, отсутствие контроля за потоками), сам ее нашел еще и в другом месте и героически не дал собрать проект.
Напомнило C++ 2000-х годов, где сообщение об ошибке ссылалось на что угодно, только не на строку с проблемой.
На C++ за десятилетия программирования я ни разу не столкнулся с ошибкой что освободил мьютекс из другого потока…
Дедлоки - да, но они и в расте могут быть.
И за этим всем на самом деле скрывается некачественная архитектура проекта. Блокировать мьютекс надо только на составление запроса, но не на выполнение (с оговорками на случай больших транзакций с откатами).
Опять странные решения…
На нашем сервере я сделал несколько перехватчиков scoped_lock, unique_lock и т.д. + некий measure_scope.
Все данные с номерами строк и именами файлов записываются в tbb::concurrent_queue и в отдельных потоках сохраняются в ClickHouse.
Оверхед ~0, точность идеальная, вплоть до отдельных строк кода, отдельно по блокировкам - видно сколько времени блокировка ожидалась, сколько держалась, количества вызовов и так далее.
Миллиарды записанных событий, данные в ClickHouse занимают до сотни гигабайт. Строятся гистограммы распределения времени, считаются экстремумы.
Визуализация в datalens, можно настраивать так же алерты, подключать ИИ к анализу.
А файлы… Если вы такое всерьез рассматривали в 2025 году, то забудьте про ИТ и идите пасти коров, что-ли…
Слишком долго и сложно. Я обычно кидаю кусок кода и пишу что-то типа «найди проблему с ресайзом области текста после сокрытия бейджа и исправь»
Зачем? Как это делать написано уже 100 раз до меня, просто возьмите архитектуру любого http сервера, например, nginx. STL + pthreads + OpenSSL + libev + Intel TBB + libfmt + rapidjson - основа.
Для бинарного протокола используем ULEB-кодирование и свой формат описания пакетов.
Парсер HTTP свой, для API-сервера не нужна полная поддержка протокола.
SSL проксирование через CloudFlare или через nginx, он же может и корректность протокола проверять.
У нас на этом написано несколько бэкэндов онлайн-игр, API-сервера для аналитики, различные сервисы.
Базовая часть, эквивалентная тому, что описана в статье пишется за день, остальное все время уже уходит на бизнес-логику.
БД ClickHouse + MySQL, адаптер самописный.
Я так же написал свое расширение для ClickHouse для PHP (оно открытое), которое работает с ним по нативному протоколу и тоже базовая часть заняла около суток, остальное время уже реализация дополнительного функционала.
У вас что-то не так просто с эффективностью…