Контекст проблемы:
Около 17:12 1 числа 9 месяца поступила жалоба от специалистов по внедрению:некоторые устройства не могли нормально обновляться, не получали команды из консоли и испытывали другие проблемы. Коллега проверил журналы проекта control (далее — control) и обнаружил, что control не получил отправленные устройствами данные их теней, поэтому не отправил команды. Проект control напрямую взаимодействует с устройствами и на основании полученной от них информации отправляет им команды и параметры конфигурации, включая обновление, отправку журналов и прочее. До запуска IoT control получал текущую информацию об устройствах из сообщений heartbeat, а после запуска IoT — из данных теней устройств
для получения текущей информации об устройстве, control подписывается на данные тени устройства, но эти данные пересылает служба теней (сокращённо IoT), а напрямую сообщения о тени устройства он не принимает, подробную схему передачи смотрите на двух рисунках ниже:
Приложение (control) получает состояние устройства

Приложение(control)отправляет команды устройству

Узнав, что control не получает сообщения тени устройства, я сразу открыл консоль rabbitmq, чтобы проверить наличие сообщений и выяснить две вещи:1.Отправляло ли устройство сообщения 2.Работает ли rabbitmq нормально, ниже на рис.1、рис.2 приведены два снимка консоли rabbitmq, сделанные в тот момент, из Рисунок 1 Можно совершенно ясно установить, что устройство отправляет сообщения, однако многие из них находятся в состоянии unacked(не подтверждены Это означает, что сообщение уже доставлено потребителю, но тот ещё не отправил ack; теоретически через некоторое время всё должно прийти в норму);в какой именно очереди накапливаются сообщения unacked, см.рис.2,“spacebridgeiot-shadow” как раз используется нами для получения информации о тенях устройств, передаваемой самими устройствами. Сообщения накопились в очереди, поэтому логично, что до control они не доходили. Понаблюдав некоторое время, я обнаружил, что число unacked стало равно 0, однако общее число total почти не изменилось. Казалось, что сообщения unacked вернулись в очередь и снова ждут доставки. И действительно, через несколько минут опять появилось много сообщений unacked, а ещё через несколько минут они снова вернулись в очередь. На стороне control сообщения по-прежнему не поступали. Тогда я посмотрел логи IoT и обнаружил, что сообщения о тенях устройств вообще не приходят. В консоли rabbitmq открыл «spacebridgeiot-shadow” Оказалось, у этой очереди больше нет потребителей, как Рисунок 3 показано. При просмотре журналов rabbitmq действительно обнаружились сообщения об ошибках, например Рисунок 4 как показано, rabbitmq самостоятельно закрыл соединение.
Рисунок 1:обзорная схема rabbitmq

Рисунок 2:статистика очередей rabbitmq

Рисунок 3:Обзор spacebridgeiot-shadow

Рисунок 4:сообщение об ошибке rabbitmq

Временное решение:
Поскольку к тому времени уже поступило множество жалоб, мы применили довольно грубое решение — «удалить накопившиеся сообщения». После удаления всё действительно нормализовалось(Примечание: проблемы в рабочей среде нужно решать как можно скорее. У нас нет времени сначала анализировать журналы, а затем последовательно устранять проблему — действовать нужно быстро)。

Воспроизведение проблемы вне рабочей среды:
1.Накопить большое количество сообщений (как минимум десятки тысяч) в очереди «mirrorTestQueue» rabbitmq в среде 10.200.41.166
2.Остановить потребителя mirrorTestQueue и запустить его снова после накопления сообщений
3.Накопление завершено, перезапускаем потребителя
Как мы и предполагали, за несколько секунд потребителю были отправлены тысячи сообщений. Через несколько минут rabbitmq сам закрыл соединение с потребителем, и после этого потребитель очереди перестал отображаться в консоли. Поскольку у нашего потребителя настроено автоматическое восстановление, через некоторое время он снова подключается автоматически,
Но вскоре соединение снова разрывалось — почти так же, как у нас в рабочей среде. Что же вызывало эту проблему? Честно говоря, тогда у меня не было никаких идей, а поиски в интернете тоже не дали особенно удовлетворительного ответа(Тогда я не уловил сути проблемы и искал по слишком общим ключевым словам),позднее
Мы предположили, что проблема могла возникнуть на уровне TCP, и решили перехватить пакеты, чтобы поискать зацепки. И действительно, нам повезло. Без лишних слов — сразу к изображению.


До 13:06:25.643428 rabbitmq продолжал отправлять сообщения потребителю, но в момент 13:06:25.643428 TCP-окно потребителя начало полностью заполняться. Это продолжалось около 30 секунд, после чего rabbitmq сам разорвал соединение(отправил пакет RST). Затем потребитель подключился заново, окно снова стало полностью заполняться, и примерно через 30 секунд соединение вновь было разорвано.


Похоже, это происходит довольно регулярно: каждый раз длится 30 с, словно задано настраиваемым параметром. В общих чертах вывод такой: «tcp full window приводит к тому, что сервер сам разрывает соединение через rst, причём это происходит закономерно».
На этот раз я изменил ключевые слова поиска и нашёл Ответ,у rabbitmq есть параметр под названием tcp_listen_options.send_timeout — параметр, управляющий тайм-аутом записи; когда время ожидания записи истекает, отправляется TCP RST(https://github.com/rabbitmq/rabbitmq-java-client/issues/341),попробуем изменить это и посмотреть на результат:
1. Изменить тайм-аут записи на 10 с
tcp_listen_options.send_timeout = 10000
2.Перехватим пакеты и проверим, работает ли это


С момента заполнения окна до закрытия соединения проходит около 10 с.
Разбор наблюдавшегося поведения:
Поскольку у потребителя rabbitmq не был настроен prefetch, rabbitmq доставлял ему слишком много сообщений за один раз. Из-за этого TCP-окно потребителя заполнялось, что задействовало tcp_listen_options.send_timeout в rabbitmq. Когда время ожидания записи достигало порогового значения, rabbitmq разрывал TCP-соединение с потребителем.
Окончательное решение:
Ранее удаление сообщений было вынужденной мерой: хотя она решила проблему, подход оказался слишком грубым. Нужно найти более изящное решение. Если TCP-окно заполняется из-за слишком большого количества сообщений, отправляемых потребителю, стоит поработать над скоростью приёма: достаточно при подключении к rabbitmq сообщить ему, чтобы он не отправлял слишком много.(Следующий отрывок взят из https://blog.csdn.net/james_searcher/article/details/70308565)У rabbitmq есть параметр prefetch: он задаёт максимальное число неподтверждённых сообщений, которые может получить один потребитель. Что это означает? Для каждого consumer mq создаёт буфер размером prefetch. При получении очередного сообщения MQ помещает его в буфер, а затем передаёт клиенту. Когда приходит подтверждение ack (consumer отправляет команду baseack), mq освобождает одно место в буфере и добавляет новое сообщение. Но если буфер заполнен, MQ переходит в состояние блокировки. Если описать подробнее, предположим, что prefetch установлен в 10 и всего есть два consumer. Это означает, что каждый consumer предварительно забирает из queue по 10 сообщений и хранит их локально в ожидании обработки. При этом число unacked для данного channel становится равным 20. Rabbit доставляет сообщения в следующем порядке: сначала передаёт consumer1 все 10 сообщений, затем consumer2 — 10 сообщений. Если в этот момент нужно доставить новое сообщение, сначала проверяется, равно ли число unacked у channel значению 20; если да, сообщение не передаётся потребителям и остаётся в queue. Затем один из consumer подтверждает сообщение через ack, число unacked становится равным 19, и Rabbit определяет, у какого consumer число unacked меньше 10, после чего доставляет сообщение ему.
В коде это выглядит так

Как оценить это значение? У rabbitmq есть хорошая официальная статья об этом, поэтому не будем вдаваться в подробности. В нашей системе сейчас установлено значение 20.
https://www.rabbitmq.com/blog/2012/05/11/some-queuing-theory-throughput-latency-and-bandwidth/
Заключение:
В использовании rabbitmq мы пока новички, поэтому перед началом работы обязательно нужно побольше почитать документацию API rabbitmq, освоить часто используемые API и провести много экспериментов в локальной среде.