[О блоге] [наверх] [пред] [2025-06-10 12:55:49+03:00] [9ba6bd3ee6f263042946a944de56efc47959610c]
Темы: [keks]

KEKS/RPC

http://www.keks.cypherpunks.su/RPC.html
Я, как и опытные коллеги, сторонник использования чего-нибудь типа
JSON-RPC, вместо RESTful. Я, как и опытные коллеги, тоже когда-то хотел
и пробовал RESTful :-). Но все мы пришли к тому, что он мало где красиво
вписывается. Очень простые (тривиальные) вещи на него "кладутся" хорошо.
Но абсолютно в каждом проекте рано или поздно возникнет case, когда не
понятно как с точки зрения REST правильно и лучше сделать API вызов. В
итоге в 100% случаев любой RESTful превращается в нечто не очень
приятное, чему ты не остаёшься удовлетворён, ведь хотел так красиво по
HTTP/REST ресурсам разбросать.

Как пример достаточно удовлетворительного протокола для вызова API, мы
сразу вспоминаем про JSON-RPC. Не нужно думать об endpoint-ах, ресурсах,
методах и прочем -- есть только строчка с названием метода и произвольно
передаваемые аргументы для её вызова. Это не значит что мы всюду и везде
пихали JSON-RPC, но идею его простоты заимствовали. JSON -- значит
будет очень медленная работа с кодеком. Можно прозрачно зачастую
заменить на BSON, MessagePack, Bencode (который нам нравился
детерминированностью кодирования, а значит дружелюбности к криптографии).
Я чаще всего применял JSON-RPC в чистом виде, поверх TCP, без HTTP. Если
нужно что-то касающееся кэширования, то ничто же не мешает мне добавить
какой-нибудь аналог ETag поля к аргументам запроса, а frontend уже
отработает как кэш. HTTP транспорт тут не много где даст пользы.

По аналогии с JSON-RPC я сделал KEKS/RPC предложение. Можно сказать, что
просто заменил кодек. Реализация совместимая с Go net/rpc тоже имеется.
Пока ещё это не сделал, но планирую применить с одним Си микросервисом.
Работать с KEKS-ом в Си -- вроде как вышло достаточно удобно, хотя опыта
пока ещё недостаточно накоплено.

Запрос KEKS/RPC: ["c", HEXLET, "method-name", {map of args}]
Оповещение:      ["n", HEXLET, "method-name", {map of args}]
Разница только в том, что запрос ("c"all) ожидает ответа.
Ответ: ["r", HEXLET, "error", {body map}]
Аргументы, как и ответ, ожидаются в виде MAP -- как минимум мне так
проще было в Go. Если строчка "error" пустая, то значит ошибки нет.
{body} это либо дополнительные данные об ошибке, либо тело ответа.

HEXLET является идентификатором запроса (и ответа на запрос). Если
применять UUIDv7, то даже визуально по журналу, будут видны
запросы/ответы в пределах одной сессии/соединения. Из-за timestamp они
будут sortable и возрастать. Если потеря производительности на запрос
времени недопустима, то можно же и просто делать инкремент timestamp.
Передача 16+1 байт на id запроса/ответа -- сущие мелочи, которыми можно
пренебречь, зато получаем удобство отладки/мониторинга.

Парсить этот список можно и потоково, понимая что за метод вызывается,
пришёл ли к нам запрос/ответ/оповещение. Ничто не мешает делать pipeline
вызовов. Асинхронно их обрабатывать.

    [оставить комментарий]