Comments 11
В любой сети самое интересное это что делать когда что-то пошло не так. Пойти не так может вообще все. Хотя бы самое тривиальное. Где таймауты и их обработка в продакшен коде? Удаленные узлы обожают не отвечать.
Сериализация джейсона везде сделана приемлимо. Она не интересна. Когда становится интересно пора переходить на протобаф, а не думать как сделать джейсон быстрее.
Всегда стараюсь отвечать максимально деликатно на подобные вашему комментарии. Или просто игнорирую их. Но, знаете, сегодня не буду… надоело, пусть и полетят в меня минусы роем. Для себя я называю такие вот комментарии - «булькающая бабушка».
Суть проста - ты сделаешь что-то, поэкперементируешь, поиграешь с идеями и опишешь это и вот на горизонте появляется она, «булькающая бабушка»… всегда с единственно верным суждением и решимостью его предъявить миру - «это всё г$_&о, не нужно делать х*!?ю»
Она не интересна. Когда становится интересно пора переходить на протобаф, а не думать как сделать джейсон быстрее.
Я учту о чём мне не думать, но все равно поясню: JSON в данной статье упоминается в качестве наиболее распространённого варианта формата данных, что характерно особенно для небольших web проектов. Никто не оспаривает существование прекрасных парсеров для него (взять хотя бы тот же serde на rust). А clibri так и вовсе не использует JSON вообще и раз так, то и не оптимизирует его. Статья вообще не про это.
На счёт таймаутов вы правы, они отлавливаются на всех болевых точках в rust имплементации и будут добавлены также в typescript совсем скоро. Собственно поэтому и есть плашка alpha, что бы подсветить - не все детали ещё учтены, есть базовое решение, но работа ещё ведётся.
Спасибо. Добра вам и терпимости к инакомыслию.
А чем вас не устроил gRPC с protobuf? Насколько ваше решение надёжнее, экономичней и быстрее?
Большое спасибо за хороший вопрос.
Конечно, protobuf - это первое что приходит на ум, и я ждал подобного вопроса. Но не упоминал о protobuf в статье лишь потому, что clibri не является ни конкурентом (куда уж мне в одиночку), ни тем более альтернативой; не в чистом виде, не в связке с gRPC. Это немного о другом.
Я по большей части работаю с web проектами, поэтому и на протокол смотрю именно с этой стороны. Часто нужно сделать какой-то несложный front-end для какого-нибудь сервиса. И каждый раз «под капотом» практически одно и тоже: транспорт, валидатор, реализация клиента, реализация сервера. Наблюдая порой за коллегами я не раз замечал, как реализация какой-нибудь связки «клиент-сервер» кочует от проекта к проекту, с каждой новой итерацией copy/paster, обрастая незначительными изменениями, то есть коллеги просто брали подходящее решение, копировали и заменяли содержание обработчиков, практически не трогая все остальное (ну разве что состав данных менялся безусловно). Но идея clibri именно в этом — дать возможность разработчику вообще не думать о том, что там делает метод send_something_somewhere. И я подумал, возможно будет интересно, если:
не описывать в протоколе ничего кроме данных (может он в рамках того же проекта будет использоваться для чего-то иного, импорта/экспорта, например)
описывая логику коммуникации (workflow) не описывать никаких функций, а описывать только возвраты и последствия возвратов. Добиться от схемы workflow однозначного и единственного толкования.
сделать так, чтобы разработчик в большей части случаев мог бы просто добавить свой код в уже готовый обработчик. То есть сгенерированное решение должно компилироваться сразу же, ещё до того, как добавлено «мясо» в обработчики.
Я не сравнивал clibri по производительность с protobuf ни с точки зрения кодирования/декодирования, ни с точки зрения экономичности, хотя, наверное, это следует сделать, но быть может немного позднее, ибо сейчас это будет сравнение автомобиля с шаттлом челленджер.
Надеюсь ответил. Ещё раз спасибо за хороший вопрос.
Я так и не понял, а в чем разница между SelfKey
и AssignedKey
?
Вы хотите обратную связь и новых контрибьюторов, но при этом в проекте нет никакой документации (как минимум, в rust части), а без этого крайне сложно что-то делать. Кроме того, возникают опасения — я не забудет ли сам автор через полгода, для чего он делал тот или иной класс или метод?
Разница во владельцах. Владельцем SelfKey
является клиент и только он может определять значение ключа; в свою очередь владельцем AssignedKey
является сервер. То есть клиент может представить себя как ему угодно, а сервер может идентифицировать клиента по-своему, скажем добавить поле verified
или что-то в этом духе.
На счёт документации вы совершено правы. Базовые моменты я описал здесь в различных секциях. Но описание конкретных методов всё ещё является слабым место. Я думаю, что это будет embedded документация, то есть встроенная непосредственно в код. Раз уж код генерируется и будут безусловно обновления, то и документацию следуют генерировать тоже.
Спасибо за ваш комментарий.
То есть клиент может представить себя как ему угодно
Ладно, а каковы гипотетические сценарии, для чего это нужно? Кому и зачем он так себя будет представлять?
а сервер может идентифицировать клиента по-своему, скажем добавить поле verified
Ну такое поле все же не идентификация, а атрибут уже идентифицированного клиента. Думаю, вряд ли кто-то скажет спасибо, если у него в системе вдруг окажется два клиента с одинаковым именем, отличающихся только плашкой verified
. Как минимум, это неочевидно, а как максимум — сознательная попытка фишинга.
Мне кажется мы просто говорим с вами о разных вещах. Если не ошибаюсь, вы подразумеваете что-то вроде авторизации (или верификации?), поправьте если я не прав.
Назначение SelfKey
и AssignedKey
немного в другом, в ассоциировании (наверное будет более точно). Например, SelfKey { uuid: string, lang: string, age: number }
(поле uuid добавляется всегда автоматически, даже если этого не сделал разработчик, uuid присваивается сервером после успешного подключения). И, например, AssignedKey { age_confirmed: bool }
.
Клиент может выставить и язык, и возраст, как его душе будет угодно, но лишь сервер (после какой-то проверки) может заключить, что возраст 18+ подтверждён.
Я думаю, что понятнее будет, если мы посмотрим на гипотетический запрос, после ответа на который, нам надо сделать broadcast всем русско-говорящим клиентам 18+. Например, пришло сообщение в чат и мы хотим сделать рассылку другим клиентам, но только тем у кого подтвержден возраст и указан целевой язык.
import { Response } from "../implementation/responses/message.request";
import {
Identification,
Protocol,
} from "../implementation/responses";
import { Scope } from "../implementation/scope";
// Обработчик входящего сообщения
export function response(
request: Protocol.Message.Request,
scope: Scope
): Promise<Response> {
// Добавляем сообщение в БД или куда-то еще
...
return Promise.resolve(
new Response(
// Готовим ответ клиенту (отправителю сообщения)
new Protocol.Message.Accepted({
uuid: scope.consumer.uuid(),
})
)
// Создаем список клиентов для broadcast
.broadcast(scope.filter.filter((ident: Identification) => {
// Broadcast message будет отправлять только клиентам, удовлетворящим следующему
// условию
return ident.getAssigned()?.age_confirmed && ident.getKey().lang === 'ru';
}))
// Создаем сообщение которое будет отправлено (broadcast message)
.EventsMessage(
new Protocol.Events.Message({
...,
})
)
);
}
Если вы сейчас подумали о том, что подобные данные хранятся обычно в БД и вытаскиваются по мере необходимости - вы совершенно правы. Идея в том, чтобы часть подобных данных иметь "под рукой" для составления списков broadcast.
Стоит также упомянуть, что и по умолчанию наличие AssignedKey
не требуется, равно как можно избежать и использование SelfKey
(он будет создан автоматически с единственным полем - uuid
). Просто в ввиду слишком большого размера статьи, мне пришлось многое опускать. В документации (вы её уже отругали :)) есть разделы, посвященные настройке producer и стратегий в отношении ключей.
Если не ошибаюсь, вы подразумеваете что-то вроде авторизации (или верификации?)
Конечно, а с чем еще должен ассоциироваться Key
? Кроме того, вы сами в примерах кода в статье написали об "идентификации". После вашего объяснения понятно, что на само деле они должны называться Properties
объекта, и наверное лучше назвать их очевидными именами:
ServerProperties
ClientProperties
uuid
при этом действительно является ключом, и наверное его лучше отдельным образом хранить, тем более, если он всегда есть, зачем его позволять указывать?
Спасибо за хорошее замечание. В переименовании действительно есть смысл.
зачем его позволять указывать?
Нет клиент не может изменить uuid
(хоть это и поле ключа), uuid
назначается только сервером.
Логика в протоколе. Генерируем сетевое решение