From 9ba6bd3ee6f263042946a944de56efc47959610c Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Tue, 10 Jun 2025 12:55:49 +0300 Subject: [PATCH] KEKS/RPC MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 вызовов. Асинхронно их обрабатывать. -- 2.51.0