golang

Авакари — сервис для публикации и подписки на релевантные сообщения

  • четверг, 22 июня 2023 г. в 00:00:16
https://habr.com/ru/articles/742972/

В предыдущей статье "Дерево Киви для поиска шаблона по тексту" я рассказывал, как можно эффективно находить по входящему сообщению все удовлетворяющие "поисковые запросы", избегая их перебора "в лоб" и прямого сопоставления. Это можно применить реализации полноценной Pub/Sub-системы, дополняющей уже существующие, такие как Nats, Apache Kafka или AWS SNS новой возможностью поддерживать действительно масштабируемое число wildcard-подписок. В этой статье я расскажу о такой системе.

Авакари на текущий момент - backend сервис, который использует gRPC API по ряду причин:

  • Эффективное использование ресурсов (HTTP/2, бинарная сериализация Protobuf)

  • Streaming - необходимо для реализации push уведомлений от сервиса клиенту. Здесь отдельное спасибо Кириллу Гусакову за подсказку.

Получение и отправка сообщений

Потоковая передача сообщений используется как для отправки сообщений в систему (Publish), так и для получения сообщений на выходе.

При выборе формата сообщений я решил остановиться на нейтральном стандарте CloudEvents. Исходные требования к формату сообщений были следующие:

  • Поддержка произвольных key-value метаданных, по которым будет происходить сопоставление с подписками

  • Независимость от протокола

  • Удобство (де)сериализации с помощью Protobuf

Пример payload при получении/отправке сообщений выглядит следующим образом:

{
   "msgs": [
      {
         "id": "3426d090-1b8a-4a09-ac9c-41f2de24d5ac",
         "type": "example.type",
         "source": "example/uri",
         "spec_version": "1.0",
         "attributes": {
            "author": {
               "ce_string": "Obi-Wan Kenobi"
            },
            "time": {
               "ce_timestamp": "1985-04-12T23:20:50.52Z"
            }
         },
         "text_data": "I felt a great disturbance in the force"
      }
   ]
}

Подписки

Для работы с пользовательскими подписками достаточно простого набора CRUDL операций (также gRPC). Чтобы пользователь мог определять, как выбирать сообщения, подписки должны содержать условия выбора.

Kiwi-условия

Так как входящие сообщения имеют метаданные в виде набора "key: input value", то условия выбора сообщения можно выразить как: "key: wildcard", где key - это искомый ключ метаданных, а wildcard - шаблон. Kiwi-условие, если короче.

Дополнительно kiwi-условия имеют следующие свойства:

  • Not - условие является отрицанием, то есть совпадение исключает сообщение

  • Partial - искать совпадения также среди лексем из input, который разбивается

Пример:

   "cond": {
      "not": false,
      "ktc": {
        "key": "title",
        "pattern": "?usk", // musk, Musk, dusk, ...
        "partial": true
      }
    }

Такому условию должны удовлетворять сообщения, которые в метаданных по ключу "title" содержат отдельные слова как, например, "musk" или полные строки вроде "Elon Musk" или "Dusk and her embrace".

Группы условий

Для полноценного описания того, какие сообщения требуется получать, kiwi-условий недостаточно. Необходимо иметь возможность описывать сразу множество условий в одной и той же подписке (например по разным ключам метаданных) и применять логические операции:

  • And

  • Or

  • Xor

Для этой цели служат группы условий. Так как группа тоже является условием, можно использовать вложенность.

Полный пример описания подписки с групповым условием:

{
   "md": {
      "description": "my subscription 1",
      "enabled": true
   }, 
   "cond": {
      "not": false,
      // "gc" means "group condition"
      "gc": { 
         "logic": 0, // 0 - And, 1 - Or, 2 - Xor
         "group": [
            {
               "not": false,
               // "ktc" means "kiwi-tree condition"
               "ktc": {
                  "key": "key0", 
                  "pattern": "pattern?", 
                  "partial": false
               }
            }, 
            {
               "not": true,
               "ktc": {
                  "key": "key1", 
                  "pattern": "pattern1", 
                  "partial": true
               }
            }
         ]
      }
   }
}

Как это попробовать

На текущий момент есть два варианта:

  1. Самостоятельно раздеплоить Core систему, например, на minikube у себя дома.

  2. Использовать демо сервис в облаке.

В облачном варианте уже используется producer-rss компонент, который регулярно поглощает RSS из разных источников, конвертирует данные в сообщения и отправляет в систему. То есть можно подписаться на новости по какому-либо критерию и получать их.

Планы на будущее

В первую очередь, конечно же нужно добиться выполнения критериев production-ready и запустить боевой экземпляр сервиса. Тут ещё довольно много рутинной работы, такой как автоматизация выдачи сертификатов.

В перспективе Авакари можно будет использовать в довольно широком наборе сфер, таких как инвестиции, СМИ, реклама.

Можно представить интеграцию с чем-то вроде Авито, где новое объявление о продаже уведомляет всех, кто подписан на специфический товар, например iphone 42.

Вместо изобретения собственного UI есть идея реализации чат-бота, который будет позволять управлять подписками, отправлять и получать сообщения в/из Авакари.

Проект является Open Source, поэтому замечания, исправления и дополнения - приветсвуются. В набор входит уже более дюжины различных микросервисов, написанных на Go.