Взаимодействие Orchestrator и RDP2 через MQTT
Доступно с версии 1.25.9+
Реализован новый асинхронный метод взаимодействия между службами Orchestrator (WebApi) и RDP2 по протоколу MQTT-over-WebSockets с использованием RabbitMQ в качестве брокера сообщений. Этот подход решает задачи масштабирования системы и снижает нагрузку при управлении большим количеством RDP-сессий.
Преимущества MQTT-over-WebSockets
- WebApi стал работать в двух режимах одновременно, обеспечивая обратную совместимость со старыми версиями RDP2 по HTTP и поддерживая новый режим по MQTT.
- RDP2 можно переключить в новый режим работы через конфигурационный файл с последующим перезапуском службы.
- Взаимодействие включает обмен сообщениями о статусах сессий, команды на завершение и уведомления о состоянии служб.
Сравнение методов взаимодействия
1. Действующий метод: взаимодействие через REST API
Служба RDP2 периодически опрашивает WebApi с помощью REST-запросов для получения списка сессий и отправки их статусов.
Цикл обработки сессий через REST API:
Основные операции:
-
GET
/api/RdpSessions?addressFilter={filterString}
→ Получение списка сессий для обработки -
PUT
/api/RdpSessions/UpdateConnectionStatuses
→ Отправка обновленных статусов сессий -
GET
/api/RdpSessions/CheckNoRdpPeriods?addressFilter={filterString}
→ Инициация проверки периодов простоя -
PUT
/api/RdpSessions/{id}/SetNeedForLogOffRDPSessionUser
→ Команда на завершение сессии RDP
2. Новый метод: взаимодействие по протоколу MQTT-over-WebSockets
Взаимодействие построено на обмене сообщениями через шину RabbitMQ.
- WebApi публикует команды и данные о сессиях (например, создать сессию).
- RDP2 подписывается на соответствующие темы, выполняет команды и публикует статусы сессий и служебные уведомления.
1. Уведомление о состоянии службы RDP2 (RdpServiceNoticeMessage)
При запуске или остановке служба RDP2 отправляет сообщение RdpServiceNoticeMessage:
/// <summary>
/// Уведомление о состоянии службы RDP2
/// </summary>
public class RdpServiceNotice
{
/// <summary>
/// Фильтр адресов обслуживаемых машин (пусто = все доступные адреса)
/// </summary>
public string AddressFilter { get; set; }
/// <summary>
/// IP-адрес машины со службой RDP2
/// </summary>
public string Host { get; set; }
/// <summary>
/// Флаг запуска/останова службы RDP2.
/// </summary>
public bool IsStarted { get; set; }
}Процесс обработки уведомлений службы RDP2:
- RDP2 публикует
RdpServiceNoticeMessageпри старте/остановке - WebApi сохраняет информацию в таблицу
Rdp2AddressFilters - WebApi отправляет полный список сессий для данной службы RDP2
- WebApi отправляет список через сообщение
RdpSessionDataMessage
Модель данных сессии RDP
public class RdpSessionData
{
public int Id { get; set; }
/// <summary>Имя RDP-пользователя (административное)</summary>
public string AdminName { get; set; }
/// <summary>Имя RDP-пользователя</summary>
public string UserName { get; set; }
/// <summary>Пароль пользователя</summary>
public string Password { get; set; }
/// <summary>IP-адрес удаленного компьютера</summary>
public string Address { get; set; }
/// <summary>Активна ли пауза без сессий роботов</summary>
public bool IsNoRdpPause { get; set; }
/// <summary>Требуется ли пауза без сессий роботов</summary>
public bool NeedNoRdpPause { get; set; }
/// <summary>Длительность паузы в секундах</summary>
public int NoRdpPauseDurationInSeconds { get; set; } = 60;
/// <summary>Поддерживать RDP-сессию постоянно</summary>
public bool KeepRdpSession { get; set; }
public bool UserProfileLoaded { get; set; }
public int AuthenticationLevel { get; set; }
public bool NegotiateSecurityLayer { get; set; }
public bool EnableCredSspSupport { get; set; }
/// <summary>Параметры подключения</summary>
public int DesktopWidth { get; set; }
public int DesktopHeight { get; set; }
public int ColorDepth { get; set; }
/// <summary>Статус подключения</summary>
public bool Connected { get; set; }
public DateTime? ConnectedChangedAt { get; set; }
public DateTime? ConnectedUpdatedAt { get; set; }
public int WorkerId { get; set; }
public int? RdpPort { get; set; }
/// <summary>Принудительное поддержание сессии (только Windows RDP)</summary>
public bool? ForceKeepRdpSession { get; set; }
}3. Управление сессиями через шину сообщений
Распределение сессий:
- WebApi отправляет
RdpSessionDataMessageс сессиями для конкретной службы RDP2 - RDP2 сохраняет сессии во внутреннем кэше
Мониторинг и управление:
- RDP2 регулярно проверяет состояние сессий из кэша
- Автоматически запускает/останавливает сессии по необходимости
- Публикует статусы сессий после каждого цикла проверки
4. Команда на завершение сессии (RdpSetNeedForLogoffMessage)
После обработки сессий RDP2 отправляет RdpSessionStatusesMessage:
/// <summary>
/// Статус RDP-сессии
/// </summary>
public sealed class RdpSessionStatus
{
/// <summary>ID сессии</summary>
public int SessionId { get; set; }
/// <summary>Статус подключения</summary>
public bool Connected { get; set; }
/// <summary>Время изменения статуса подключения</summary>
public DateTime ConnectedChangedAt { get; set; }
/// <summary>Время последнего обновления статуса</summary>
public DateTime ConnectedUpdatedAt { get; set; }
}Настройка нового метода взаимодействия
Для перевода взаимодействия между WebApi и RDP2 на новый режим требуется выполнить следующие шаги:
Шаг 1: Настройка RabbitMQ
На сервере с установленным RabbitMQ выполните следующие команды (для Windows — в C:\Program Files\RabbitMQ Server\rabbitmq_server-<Версия>\sbin)
rabbitmq-plugins enable rabbitmq_mqtt
rabbitmq-plugins enable rabbitmq_web_mqttНастройка брандмауэра: Откройте TCP-порт 15675, который используется для MQTT-over-WebSockets.
Шаг 2: Настройка NGINX
Добавьте в конфигурационный файл nginx.conf блок для проксирования MQTT-трафика.
http {
...
upstream mqtt {
server <IP_или_имя_сервера_RabbitMQ>:15675;
}
server {
...
location /mqtt/ {
proxy_pass http://mqtt;
rewrite ^/mqtt/(.*)$ /$1 break;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
...
}
}Проверьте конфигурацию:
nginx -tв случае отсутствия ошибок, перезапустите службу NGINX:
nginx -s reloadШаг 3: Настройка службы WebApi
В конфигурационном файле appsettings.Production.json (или другом актуальном) добавьте раздел Mqtt.
{
...,
"Mqtt": {
"Url": "wss://<IP или имя и порт сервера NGINX>/mqtt/ws",
"Username": "<пользователь RabbitMQ>",
"Password": "<(зашифрованный) пароль пользователя RabbitMQ>"
},
...
}Важно: WebApi начнет работать в двух режимах одновременно, отправляя сообщения в шину и продолжая отвечать на REST-запросы от старых версий RDP2.
Шаг 4: Настройка службы RDP2
В конфигурационном файле службы RDP2 (appsettings.Production.json) добавьте аналогичный раздел Mqtt.
{
...,
"Mqtt": {
"Url": "wss://<IP или имя и порт сервера NGINX>/mqtt/ws",
"Username": "<пользователь RabbitMQ>",
"Password": "<(зашифрованный) пароль пользователя RabbitMQ>"
},
...
}Проверка работоспособности
-
Проверка статуса служб Убедитесь, что службы RabbitMQ и NGINX запущены и работают.
-
Перезапуск приложений Перезапустите службы WebApi и RDP2.
-
Анализ логов после перезапуска
-
В логах WebApi:
-
При запуске не должно быть сообщений
Failed connecting to MQTT. -
Если появляется запись
"MQTT bus client is invalid, REST is used"— это означает, что подсистема MQTT отключена и взаимодействие между службами происходит через REST API. -
В логах RDP2:
-
При запуске не должно быть сообщений
Failed connecting to MQTT. -
При включенном отладочном логировании
"LTools.Orchestrator.RDP2.Bl.Services.OrchestratorMqttClient": "Debug", при корректной работе в логах будут появляться сообщения вида:Received session <user>@<address> Keep=<true|false> Connected=<true|false>Эти записи подтверждают, что RDP2 успешно получает информацию о сессиях от WebApi.
- Проверка в интерфейсе Orchestrator Убедитесь, что статусы RDP-сессий в веб-интерфейсе Orchestrator обновляются своевременно, без задержек.
Для работы MQTT необходимо наличие раздела Mqtt с корректным значением Url. При отсутствии этих настроек система автоматически переходит на взаимодействие через REST API.
Возврат к REST API
При проблемах с MQTT временно удалите раздел Mqtt из конфигурации для возврата к старому методу.