У KPHP действительно есть отличия от оригинальной пыхи. KPHP не поддерживает все то, что не дружит со статической типизацией и не может быть скомпилено. Например, не поддерживается рефлексия, вызовы функций по динамическому имени, всякие eval'ы и прочее.
Еще KPHP не разрешает вещи, которую ломают систему типов - например нельзя смешивать классы с примитвами, т.е. mixed не может быть классом. Более подробно можно почитать об отличиях в доке. В целом, все эти ограничения заставляют писать более понятный (как для людей так и для IDE), статически типизизруемый код, который чаще всего еще и более эффективен.
Алгебраические типы сейчас действительно не совсем честные, так как string|int выводится как mixed, и получается может содержать array. Возможно в будущем мы с этим что-то сделаем, есть кое-какие идеи на этот счет. Но зато в случае со строго типизированным кодом, компилятор все четко проверяет, и если попытаться например передать в mixed объект класса, или в interface несовместимый класс, то получим ошибку на этапе компиляции, а не TypeError в рантайме как в пхп.
Под множественными имелось в виду просто несколько серверных сокетов, у каждого из которых свой backlog. Путем многочисленных экспериментов выяснили, что в случае с двумя сокетами, причем на разных портах, можем держать больший RPS без деградации, чем с одним. Пробовали разные варианты: и SO_REUSEPORT на 2 сокета + fork n раз, и отдельный listen сокет в каждом воркере с SO_REUSEPORT, и еще много всяких. Резльутат один: 2 слушающих сокета на разных портах выигрывают. Скорее всего причина тому -- contention в ядре на слушающий сокет на accept'е.
Напомню, что в KPHP prefork сервер, то есть много воркер процессов которые обрабатывают запросы. Под CPU affinity имелось в виду прибивание этих воркер процессов к ядрам. Более точно - к наборам ядер, соответвующих конкретной NUMA ноде. Еще из подобных оптимизаций решили сделать прибитие проксей к NUMA нодам, чтобы каждый воркер взаимодействовал с проксей со "своей" ноды. В совокупности это все позволило выиграть несколько процентов CPU.
Да, именно об этом. Все благодаря SCM_RIGHTS. По UNIX сокету просто передается массив чисел - открытых файловых дескрипторов. Дальше вся магия происходит в kernel space: происходит что-то типа dup файловых дескрипторов из таблицы открытых файлов одного процесса в таблицу открытых файлов другого. В итоге на принимающей стороне в user space просто отдаются полученные файловые дескрипторы. Сами числа файловых дескрипторов могут отличаться, но они будут указывать на те же записи в системной таблице файлов.
У kphp сервера есть специальный файлик в разделяемой памяти (см. shm_open). Когда новый сервер стартует, он через этот специальный файлик связывается со старым сервером, и начинается graceful restart. Сначала старый сервер через UNIX сокет с помощью SCM_RIGHTS посылает новому открые файловые дескрипторы нужных серверных коннектов. Затем старый сервер начинает плавно тушить своих воркеров (в kphp prefork сервер), в то время как новый поднимает своих. В итоге, когда воркеров больше не остается, старый сервер завершается, и получается бесшовный рестарт.
У KPHP действительно есть отличия от оригинальной пыхи. KPHP не поддерживает все то, что не дружит со статической типизацией и не может быть скомпилено. Например, не поддерживается рефлексия, вызовы функций по динамическому имени, всякие eval'ы и прочее.
Еще KPHP не разрешает вещи, которую ломают систему типов - например нельзя смешивать классы с примитвами, т.е. mixed не может быть классом.
Более подробно можно почитать об отличиях в доке.
В целом, все эти ограничения заставляют писать более понятный (как для людей так и для IDE), статически типизизруемый код, который чаще всего еще и более эффективен.
Алгебраические типы сейчас действительно не совсем честные, так как
string|int
выводится какmixed
, и получается может содержатьarray
. Возможно в будущем мы с этим что-то сделаем, есть кое-какие идеи на этот счет.Но зато в случае со строго типизированным кодом, компилятор все четко проверяет, и если попытаться например передать в
mixed
объект класса, или в interface несовместимый класс, то получим ошибку на этапе компиляции, а неTypeError
в рантайме как в пхп.Под множественными имелось в виду просто несколько серверных сокетов, у каждого из которых свой backlog. Путем многочисленных экспериментов выяснили, что в случае с двумя сокетами, причем на разных портах, можем держать больший RPS без деградации, чем с одним. Пробовали разные варианты: и
SO_REUSEPORT
на 2 сокета + fork n раз, и отдельный listen сокет в каждом воркере сSO_REUSEPORT
, и еще много всяких. Резльутат один: 2 слушающих сокета на разных портах выигрывают. Скорее всего причина тому -- contention в ядре на слушающий сокет на accept'е.Напомню, что в KPHP prefork сервер, то есть много воркер процессов которые обрабатывают запросы. Под CPU affinity имелось в виду прибивание этих воркер процессов к ядрам. Более точно - к наборам ядер, соответвующих конкретной NUMA ноде. Еще из подобных оптимизаций решили сделать прибитие проксей к NUMA нодам, чтобы каждый воркер взаимодействовал с проксей со "своей" ноды. В совокупности это все позволило выиграть несколько процентов CPU.
Да, именно об этом. Все благодаря
SCM_RIGHTS
.По UNIX сокету просто передается массив чисел - открытых файловых дескрипторов. Дальше вся магия происходит в kernel space: происходит что-то типа
dup
файловых дескрипторов из таблицы открытых файлов одного процесса в таблицу открытых файлов другого. В итоге на принимающей стороне в user space просто отдаются полученные файловые дескрипторы.Сами числа файловых дескрипторов могут отличаться, но они будут указывать на те же записи в системной таблице файлов.
Есть хорошая статья от cloudflare на эту тему.
Еще можно почитать
man unix
,man open
- там довольно хорошо описано.У kphp сервера есть специальный файлик в разделяемой памяти (см. shm_open).
Когда новый сервер стартует, он через этот специальный файлик связывается со старым сервером, и начинается graceful restart.
Сначала старый сервер через UNIX сокет с помощью SCM_RIGHTS посылает новому открые файловые дескрипторы нужных серверных коннектов.
Затем старый сервер начинает плавно тушить своих воркеров (в kphp prefork сервер), в то время как новый поднимает своих.
В итоге, когда воркеров больше не остается, старый сервер завершается, и получается бесшовный рестарт.