Pull to refresh

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 назначается только сервером.

Под "указывать" я имел ввиду, что незачем его описывать в спецификации, если он и так всегда есть. Смысла в этом все равно никакого (по крайней мере я не вижу, а вы не описали, есть ли он)

Sign up to leave a comment.

Articles