Комментарии 19
Павел, спасибо за статью. В архитектурных статьях мне всегда интересно не только само решение, но и ход рассуждений при выборе альтернатив. Если есть возможность, расскажите об этом немного.
Например,
Можно использовать Outbox и записывать сообщения в рамках транзакции в базу, для того чтобы у пользователя все прошло гладко, а вот в шину уже отправлять тогда, когда получится
Для продюсера можно написать библиотеку с общим интерфейсом, которая под капотом скрывает реализацию работы с Кафкой. Вы в итоге такой клиент и написали, только для Вашей собственной шины
Даже не представляю насколько эта шина должна быть сложным продуктом. Кафка довольно специфичный инструмент и ее конзюмеры не просто так сложные. Все эти сдвигания оффсетов, обработки ошибок, хертбиты и прочее. Судя по всему Ваша шина должна все это повторять и как-то с этим работать.
Паттерн outbox у нас поддерживается. Есть реализация клиента databus, которая на самом деле пишет события в отдельную специальную таблицу в той же базе данных, что и остальные сущности, а потом фоновый отдельный воркер перекладывает их в databus. Для клиента все работает атомарно внутри одной транзакции.
Написать общую библиотеку можно, но потом сложно следить за тем, чтобы ее своевременно обновляли и чтобы например, когда мы включаем один кластер или переводим его на другие адреса, или когда мы хотим заменить кафку на что-то другое, то это прошло бы незаметно для клиента.
Наш же сервис - это фасад.
Сама по себе шина не так сложна, фактически в ней просто websocket соединение вида pub/sub, где клиент может сказать в какие топики он хочет писать, а из каких читать.
Пока жив коннект от клиента - мы держим коннект в кафку. Плюс под капотом пара дополнительных штук типа набирания батча для клиента на консьюминге и так далее.
Общая задача была в том, чтобы защитить клиентов от знания о внутренней технологии, ее топологии и не дать совершать невалидные действия, так что решение в этом плане оправдывает себя
Мне показалось или вы переизобрели enterprise service bus? Еще интересно насколько хорошо работает WebSocket, который из коробки довольно примитивный в плане разрыва соединения и не имеет поддержки схемы данных (в отличие от Kafka)?
По-сути, переизобрели :)
Можете чуть подробнее раскрыть вопрос про разрыв соединения и схему данных?
Грубо говоря, если соединение рвется в websocket, то очень долго можно об этом не знать, особенно если это сетевая проблема и соединение именно разрывается, а не закрывается стандартно. Т.е. с точки зрения клиента ws все хорошо, а на самом деле нет и часть сообщений может не дойти после такого разрыва. Ну и вроде как нет какого-то стандартного механизма восстановления соединения после разрыва, каждый по своему делает.
А про схему имеется в виду некий аналог avro у kafka, которого для ws нет. Кстати, вы шлете сообщения в тексте или бинарно?
Выше уже ответили на многие вопросы.
У нас есть таймауты на подключения, keep-alive сообщения, чтобы убедиться, что клиент жив.
Также у нас есть реестр схем событий, благодаря которому мы можем валидировать контракты как в момент попытки деплоя сервиса, так и при публикации
То есть у вас нет единого реестра схем, а каждый сервис имеет контракты как пордьсер и консьюмер?
Спасибо, на RSocket посмотрим обязательно.
Дисбаланс подключений действительно возможен. Но нагрузка от таких подключений на databus не очень большая, а подключений много, поэтому в среднем распределение достаточно равномерное. Поэтому в этом плане у нас есть упрощение: сложной балансировки нет.
События действительно могут перепутаться, также, да, в текущей схеме может быть ситуация, когда часть событий останется в умершем ДЦ. Но мы намеренно не предоставляем гарантий порядка событий на данном этапе эволюции нашей системы и требуем от клиентов устойчивости к нарушениям порядка и дублям.
Camel для нашего случая не рассматривали. Наша шина представляет собой просто механизм обмена событий и не предполагает наличие логики по их обработке или трансформации. В будущем - возможно вокруг нее добавится какой-то механизм для обработки, тогда мы посмотрим на доступные варианты, может быть и camel, хотя он не очень ложится в стек нашей компании (из-за java/kotlin)
Да, формат конечно же похож на описание protobuf, спасибо за замечание
Сложно менять топологию системы. Если вы захотите отказаться от Kafka или разделить её на несколько кластеров, придётся обновить клиенты для всех пользователей, поменять адреса и настройки.
Под каждый язык разработки нужен свой клиент. В Авито мы пишем на Go, PHP, Python и других, получается зоопарк из разных систем только для обмена событиями.
С ростом системы прочитать сообщения из Kafka становится сложнее из-за растущей нагрузки.
Странно при таких вводных читать остальную часть статьи. По сути вы закрыли только 1 пункт, написав клиенты под все языки, только на этот раз не под стандартную кафку, а под свой специфичный протокол. Пункт 3 из коробки тоже не решается, история с подменой кафок - ну такое, по сути получается длинная транзакция.
Из минусов - на сапорте еще один сервис, со своей логикой и своими багами. Вроде как получилось надежно, а вроде как и не понятно зачем.
Редпанду не пробовали как замену кафке?
Спасибо за комментарий.
От части вы правы: мы получили дополнительный сервис в поддержку, протокол у нас собственный.
Тут правда надо заметить, что мы скрываем, что у нас вообще используется Кафка от пользователей, для них сервис скорее похож на amazon sqs, Yandex queues и так далее.
Пункт 3 не совсем понятен: почему становится труднее, такого в статье не утверждается.
Редпанда нам нравится и у нас есть планы на переход на нее, о чем однажды, думаю, будет следующая статья
Свой сервис всегда свой сервис, особенно со своим собственным протоколом :(
Реализация чужого протокола могла бы решить часть проблем, но это тот еще вызов :)
Пункт 3 - труднее не становится, но и не проще. По-прежнему есть самая большая кафка, и маленькая кафка - такая же большая, только локальная :) Кажется, что все это просто ждет, когда в очередной раз ресурсов не хватит - проблема архитектурно не решена, а закрыта вуалью фронта :)
Спасибо )
Дело в том, что проблема не в ресурсах, Кафка нормально масштабируется горизонтально. Дело в надёжности и времени восстановления.
Схема с несколькими кафками на запись (под каждый из дц) выбрана исходя из требования гарантировать доступность записи. У такого решения минус тоже есть, в виде сложности системы, например. Поэтому сейчас мы движемся к другой схеме, где будут отдельные независимые кластера меньшего размера
Отличная статья!
Какие гарантии доставки (at least once, at most once, exactly once) Kafka удалось сохранить и как?
Привет, а почему про репликатор не рассказали? Это важная часть архитектуры.
Как построить надёжную шину данных на Apache Kafka