556 lines
16 KiB
Plaintext
556 lines
16 KiB
Plaintext
|
= Rest сервис Topic-management
|
|||
|
|
|||
|
=== Rest сервис для работы с топиками.
|
|||
|
|
|||
|
Сервис состоит из 2-х контекстов:
|
|||
|
|
|||
|
- _topic-management_ - управление топиками (создание/изменение/удаление)
|
|||
|
- _topic-subscription_ - клиентский сервис подписки и публикации сообщения
|
|||
|
|
|||
|
При смене контекста в конфигурационных файлах требуется перераздать права на данный сервис.
|
|||
|
|
|||
|
Схема работы подписок
|
|||
|
|
|||
|
image::Topic_publish_diagram.png[]
|
|||
|
|
|||
|
Схема работы нотификаций
|
|||
|
|
|||
|
image::Scheme_of_work_notifications(events).png[]
|
|||
|
|
|||
|
=== Права
|
|||
|
|
|||
|
Право доступа в сервис _topic-management_
|
|||
|
|
|||
|
Для доступа к сервису _topic-management_ у аккаунта должно быть право доступа в данный сервис.
|
|||
|
|
|||
|
По умолчанию право на доступ к сервису есть у пользователя _admin_.
|
|||
|
|
|||
|
Используя данного пользователя в сервисе _permission-management_ можно раздать права на аккаунты систем.
|
|||
|
|
|||
|
Параметры для выдачи аккаунту прав на сервис:
|
|||
|
|
|||
|
- _objectId_: идентификатор учётной записи
|
|||
|
- _objectType_: account
|
|||
|
- _subjectId_: topic-management
|
|||
|
- _subjectType_: service
|
|||
|
|
|||
|
Право manage сервиса _topic-subscription_
|
|||
|
|
|||
|
Для доступа к менеджерским функциям сервиса _topic-subscription_ у аккаунта должно быть право manage - данное право открывает доступ к управлению подписками.
|
|||
|
|
|||
|
Функционал управления подписками реализован в методах _subscribe_ и _unsubscribe_, пример использования приведён в подпунктах “Массовая обработка при наличии права manage для данного сервиса”.
|
|||
|
|
|||
|
Параметры для выдачи аккаунту права _manage_:
|
|||
|
|
|||
|
- _objectId_: идентификатор учётной записи
|
|||
|
- _objectType_: account
|
|||
|
- _subjectId_: topic-subscription
|
|||
|
- _subjectType_: service
|
|||
|
- _action_: manage
|
|||
|
|
|||
|
=== Конфигурационные файлы
|
|||
|
|
|||
|
Конфигурационные файлы по умолчанию хранятся в папке сервера <karaf_home>\etc\
|
|||
|
|
|||
|
- _ru.entaxy.esb.system.event.handler.cfg_
|
|||
|
|
|||
|
[source,properties]
|
|||
|
----
|
|||
|
# максимальное колчичество попыток отправить сообщение в очередь
|
|||
|
# почитать подробнее можно в https://camel.apache.org/components/latest/eips/dead-letter-channel.html#deadLetterChannel-Redelivery
|
|||
|
redelivery.maximumRedeliveries=-1
|
|||
|
# время между попытками отправить сообщение в очередь
|
|||
|
redelivery.redeliveryDelay=5000
|
|||
|
|
|||
|
#cron - по умолчанию запускается каждые 00:00:00,
|
|||
|
#cron выражение использует знак "+" как разделитель для модуля quartz2l
|
|||
|
quirtz.job.clean.cron=0+0+0+*+*+?+*
|
|||
|
----
|
|||
|
|
|||
|
- _ru.entaxy.esb.system.event.rest.cfg_
|
|||
|
|
|||
|
[source,properties]
|
|||
|
----
|
|||
|
service.host=http://0.0.0.0
|
|||
|
|
|||
|
service.port.management=9090
|
|||
|
#Рутовый контекст управляющего сервиса, при изменении требуется перераздать права для данного сервиса
|
|||
|
service.root.path.management=/topic-management
|
|||
|
|
|||
|
service.port.subscription=9092
|
|||
|
#Рутовый контекст клиентского сервиса, при изменении требуется перераздать права для данного сервиса
|
|||
|
service.root.path.subscription=/topic-subscription
|
|||
|
----
|
|||
|
|
|||
|
=== Методы topic-management:
|
|||
|
|
|||
|
- _create_ - создать топик
|
|||
|
При создании и изменении топика, есть возможность передать списки систем, которым доступна подписка или публикация в данном топике.
|
|||
|
|
|||
|
Запрос:
|
|||
|
|
|||
|
_POST server:9090/topic-management/create_
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"topicName": "boomNews",
|
|||
|
"possibleSubscribers" : ["systemUuid1", "systemUuid2"],
|
|||
|
"possiblePublishers" : ["systemUuid3", "systemUuid4"]
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
Ответ:
|
|||
|
|
|||
|
Статус 201
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Topic created",
|
|||
|
"topicName": "<topic_name>",
|
|||
|
"subscriberErrors": {
|
|||
|
"systemNotFound": [
|
|||
|
"<system_uuid>"
|
|||
|
]
|
|||
|
},
|
|||
|
"publisherErrors": {
|
|||
|
"systemNotFound": []
|
|||
|
}
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
- update - изменить топик (название не меняется, так как на него завязана сама очередь в брокере)
|
|||
|
|
|||
|
Запрос:
|
|||
|
|
|||
|
_POST server:9090/topic-management/update_
|
|||
|
|
|||
|
Тело
|
|||
|
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"topicName": "boomNews",
|
|||
|
"possibleSubscribers" : ["systemUuid1", "systemUuid2"],
|
|||
|
"possiblePublishers" : ["systemUuid3", "systemUuid4"]
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
Ответ:
|
|||
|
|
|||
|
Статус 201
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Topic updated",
|
|||
|
"topicName": "<topic_name>",
|
|||
|
"subscriberErrors": {
|
|||
|
"systemNotFound": [
|
|||
|
"<system_uuid>"
|
|||
|
]
|
|||
|
},
|
|||
|
"publisherErrors": {
|
|||
|
"systemNotFound": []
|
|||
|
}
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
- delete - топик помечается как удалённый
|
|||
|
|
|||
|
Запрос:
|
|||
|
|
|||
|
_POST server:9090/topic-management/delete_
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"topicName": "boomNews"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
Ответ:
|
|||
|
|
|||
|
Статус 200
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Topic deleted",
|
|||
|
"topicName": "<topic_name>"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
|
|||
|
- clean - очистка хранилища топиков
|
|||
|
|
|||
|
Окончательное удаление топиков, помеченных как удалённые.
|
|||
|
|
|||
|
Запрос:
|
|||
|
|
|||
|
_POST server:9090/topic-management/clean_
|
|||
|
|
|||
|
Ответ:
|
|||
|
|
|||
|
Статус 200
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Cleaned",
|
|||
|
"topicDeleted": "<count_of_deleted_topics>"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
|
|||
|
=== Методы topic-subscription:
|
|||
|
|
|||
|
- _subscribe_ - подписаться на топик
|
|||
|
|
|||
|
Запрос:
|
|||
|
|
|||
|
_POST server:9090/topic-subscription/subscribe_
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"topicName": "boomNews",
|
|||
|
"subscriptionType": "PUSH"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
Ответ:
|
|||
|
|
|||
|
Статус 201
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Subscription created",
|
|||
|
"topicName": "<topic_name>",
|
|||
|
"systemName": "<system_name>",
|
|||
|
"subscriptionType": "<subscription_type>"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
=== Массовая обработка при наличии права manage для данного сервиса.
|
|||
|
|
|||
|
- _subscribe_ - подписаться на топик
|
|||
|
|
|||
|
Запрос:
|
|||
|
|
|||
|
_POST server:9092/topic-subscription/subscribe_
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"topicName": "boomNews",
|
|||
|
"systemUuids": [
|
|||
|
{
|
|||
|
"systemUuid": "NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN",
|
|||
|
"subscriptionType": "PUSH"
|
|||
|
},
|
|||
|
{
|
|||
|
"systemUuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
|
|||
|
"subscriptionType": "PULL"
|
|||
|
},
|
|||
|
{
|
|||
|
"systemUuid": "YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY",
|
|||
|
"subscriptionType": "PULL"
|
|||
|
}
|
|||
|
]
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
Ответ:
|
|||
|
|
|||
|
Статус 200
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
[
|
|||
|
{
|
|||
|
"title": "Internal Server Error",
|
|||
|
"detail": "System not found NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN",
|
|||
|
"reason": "javax.persistence.NoResultException: No entity found for query"
|
|||
|
},
|
|||
|
{
|
|||
|
"title": "Subscription created",
|
|||
|
"topicName": "ooooo111-ff6e-4219-a878-bff120c495f1",
|
|||
|
"systemUUID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
|
|||
|
"subscriptionType": "PULL"
|
|||
|
},
|
|||
|
{
|
|||
|
"title": "Forbidden",
|
|||
|
"detail": "No permission to subscribe for system YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"
|
|||
|
}
|
|||
|
]
|
|||
|
----
|
|||
|
|
|||
|
|
|||
|
- _unsubscribe_ - отписаться от топика
|
|||
|
|
|||
|
Запрос:
|
|||
|
|
|||
|
_POST server:9090/topic-subscription/unsubscribe_
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"topicName": "boomNews"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
Ответ:
|
|||
|
|
|||
|
Статус 200
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Subscription deleted",
|
|||
|
"topicName": "<topic_name>",
|
|||
|
"systemName": "<system_name>"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
=== Массовая обработка при наличии права manage для данного сервиса.
|
|||
|
|
|||
|
Запрос:
|
|||
|
|
|||
|
_POST server:9092/topic-subscription/unsubscribe_
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"topicName": "boomNews",
|
|||
|
"systemUuids": [
|
|||
|
{
|
|||
|
"systemUuid":"NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN",
|
|||
|
"subscriptionType": "PUSH"
|
|||
|
},
|
|||
|
{
|
|||
|
"systemUuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
|
|||
|
"subscriptionType": "PULL"
|
|||
|
},
|
|||
|
{
|
|||
|
"systemUuid": "YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY",
|
|||
|
"subscriptionType": "PULL"
|
|||
|
}
|
|||
|
]
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
Ответ:
|
|||
|
|
|||
|
Статус 200
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
[
|
|||
|
{
|
|||
|
"title": "Internal Server Error",
|
|||
|
"detail": "System not found NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN",
|
|||
|
"reason": "javax.persistence.NoResultException: No entity found for query"
|
|||
|
},
|
|||
|
{
|
|||
|
"title": "Subscription deleted",
|
|||
|
"topicName": "ooooo111-ff6e-4219-a878-bff120c495f1",
|
|||
|
"systemUUID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
|||
|
},
|
|||
|
{
|
|||
|
"title": "Subscription not found",
|
|||
|
"topicName": "ooooo111-ff6e-4219-a878-bff120c495f1",
|
|||
|
"systemUUID": "YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"
|
|||
|
}
|
|||
|
]
|
|||
|
----
|
|||
|
|
|||
|
- _publish_ - опубликовать событие
|
|||
|
|
|||
|
Запрос:
|
|||
|
|
|||
|
_POST server:9090/topic-subscription/publish_
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"topicName": "boomNews", "message": "messageText2"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
Ответ:
|
|||
|
Статус 200
|
|||
|
Тело
|
|||
|
{
|
|||
|
"title": "Message published",
|
|||
|
"topicName": "<topic_name>"
|
|||
|
}
|
|||
|
|
|||
|
=== Примеры ответов при ошибке
|
|||
|
|
|||
|
- _Передан некорректный JSON или логин не определён_
|
|||
|
|
|||
|
Статус 400
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Incorrect input parameters",
|
|||
|
"detail": "Cannot parse incoming JSON or login/system not defined"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
- _Подписка не найдена_
|
|||
|
|
|||
|
Статус 404
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Subscription not found",
|
|||
|
"topicName": "${exchangeProperty.topicName}",
|
|||
|
"systemUUID": "${header.X-SystemUuid}"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
- _Топик не зарегистрирован_
|
|||
|
|
|||
|
Статус 400
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Topic not registered",
|
|||
|
"topicName": "${exchangeProperty.topicName}"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
- _Система не найдена_
|
|||
|
|
|||
|
Статус 500
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Internal Server Error",
|
|||
|
"detail": "System not found ${header.X-SystemUuid}",
|
|||
|
"reason": "${exception.stacktrace}"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
- _Неизвестный тип подписки_
|
|||
|
|
|||
|
Статус 500
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Internal Server Error",
|
|||
|
"detail": "Unknown subscription type ${exchangeProperty.subscriptionType}",
|
|||
|
"reason": "${exception.stacktrace}"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
- _Нет прав на выполнение запрошенной операции_
|
|||
|
|
|||
|
Статус 403
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Forbidden",
|
|||
|
"detail": "No permission to subscribe"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
- _Неизвестная ошибка_
|
|||
|
|
|||
|
Статус 500
|
|||
|
|
|||
|
Тело
|
|||
|
[source,json]
|
|||
|
----
|
|||
|
{
|
|||
|
"title": "Internal Server Error",
|
|||
|
"detail": "Unknown exception",
|
|||
|
"reason": "${exception.stacktrace}"
|
|||
|
}
|
|||
|
----
|
|||
|
|
|||
|
== Работа модуля топиков в кластере
|
|||
|
|
|||
|
=== Подписки
|
|||
|
|
|||
|
*PULL подписки не реализованы!*
|
|||
|
|
|||
|
Для реализации подписки используются _durable shareable_ подписчики _Apache Artemis_ топиков, а для доставки сообщений из топиков создаются специальные маршруты отправляющие сообщения в подписанную систему(PUSH-подписка). При работе в кластере системой создаются дублирующие маршруты на каждом узле, которые работают в конкурентном режиме, т.е. при падении одного из узлов доставка сообщений будет производиться оставшимися узлами.
|
|||
|
|
|||
|
Работа в кластере обеспечивается через компоненты _Apache Felix Event Admin_, реализующий рассылку служебных событий по топикам внутри узла,
|
|||
|
и _Apache Karaf Cellar_ с интеграцией с _Event Admin_, реализующий рассылку данного события по всем узлам кластера. При создании/удалении подписки система создаёт соответствующее событие и отправляет в специальный топик _subscription_, далее подписчики на всех узлах, входящих в кластер, получают и обрабатывают данное событие.
|
|||
|
|
|||
|
=== Настройки
|
|||
|
|
|||
|
Настройки производятся на любом узле либо на мастер узле, в зависимости от настроек кластера.
|
|||
|
|
|||
|
Для синхронизации топика _subscription_ в конфигурационном файле _org.apache.karaf.cellar.groups.cfg_, нужно добавить строки:
|
|||
|
|
|||
|
[source,properties]
|
|||
|
----
|
|||
|
default.event.blacklist.inbound = none
|
|||
|
default.event.blacklist.outbound = none
|
|||
|
default.event.whitelist.inbound = subscription
|
|||
|
default.event.whitelist.outbound = subscription
|
|||
|
----
|
|||
|
|
|||
|
Далее если _Apache Karaf Cellar_ настроен правильно, то конфигурационные файлы _org.apache.karaf.cellar.groups.cfg_ должны синхронизироваться и строки, представленные выше, появятся на всех узлах.
|
|||
|
|
|||
|
Если синхронизация конфигурационных файлов не проходит, можно проверить статус _cellar_ продюсера командой:
|
|||
|
|
|||
|
cluster:producer-status
|
|||
|
|
|||
|
если выключен, то можно включить командой:
|
|||
|
|
|||
|
cluster:producer-start
|
|||
|
|
|||
|
=== Удаление топиков
|
|||
|
|
|||
|
Удаление топиков происходит в 2 этапа
|
|||
|
|
|||
|
- При вызове метода _delete_ топик помечается в БД, как удалённый
|
|||
|
|
|||
|
- По расписанию запускается задача (по умолчанию раз в сутки в 00:00), которая удаляет топик окончательно вместе с подписками, правами и консьюмерами.
|
|||
|
Либо очистка запускается вручную вызовом метода _clean_ сервиса _topic-management_
|
|||
|
|
|||
|
Если до очистки системы изменить топик помеченный как удалённый или попытаться создать его заново, то восстановится старый топик
|
|||
|
|
|||
|
=== Плановая задача по очистке топиков
|
|||
|
|
|||
|
Т.к. удаление топиков происходит в 2 этапа, с помощью планировщика _Quartz2_, работающего в кластере, запускается задача по очистке. Запуск происходит в конкурентном режиме, узел первый запустивший задачу получает приоритет, на остальных узлах задача останавливается до следующего запуска.
|
|||
|
|
|||
|
Частота запуска задачи настраивается в конфигурационном файле _ru.entaxy.esb.system.event.handler.cfg_ в свойстве quirtz.job.clean.cron=0+0+0+*+*+?+*
|
|||
|
с помощью cron выражения. Особенность cron выражения для quartz в том что “++” используется, как разделитель.
|
|||
|
|