От того, как вы разрабатываете свои приложения, будет зависеть, будет ли недвижимость гарантирована.

Очередь FIFO - это очередь сообщений, которая гарантирует единовременную доставку без дублирования сообщений на стороне получателя. Это свойство звучит достаточно просто, но его довольно сложно реализовать в среде распределенной системы.

В настоящее время многие облачные технологии абстрагируют все компоненты распределенной системы до терминов, которые мы понимаем. Однако даже при том, что они могут продавать, что их служба или очередь сообщений является очередью FIFO, все еще существуют определенные ограничения на эти свойства. Вы не можете полагаться исключительно на их службу для доставки единовременного сообщения и очереди FIFO и проектировать свою систему горизонтально, как обычно, потому что свойство не может храниться в этой среде.

В этой статье давайте углубимся в свойства очереди FIFO, и я расскажу вам о трех простых сценариях, которые нарушают эти свойства, и о том, как вы можете решить эту проблему. В конце статьи вы сможете самостоятельно определить, может ли свойство очереди FIFO существовать в распределенных системах.

Очередь FIFO в реальном мире

Очередь FIFO - это очередь сообщений, которая находится в порядке «первым пришел - первым обслужен». Это означает, что любое указание сообщений, которые вы публикуете в очереди сообщений, будет извлечено в том же порядке, в каком оно было опубликовано.

Это звучит довольно просто для понимания. Мы часто узнаем об этом в нашем 101-м классе о создании очереди для ответов на вопросы алгоритма. Мы поняли, что порядок имеет значение в порядке поступления. Это тоже кое-что подразумевает - при извлечении не будет повторяющихся сообщений.

Таким образом, мы можем сделать вывод, что двумя основными свойствами очереди FIFO являются: порядок сообщений по принципу «первым пришел - первым обслужен» и отсутствие повторяющихся сообщений в системе.

Заказ сообщений

Создать очередь FIFO в единой среде несложно. Один издатель публикует сообщения в системе сообщений, а один подписчик подписывается на систему сообщений. Издатель ждет, пока подписчик не подтвердит сообщение, прежде чем отправить сообщение следующему значению.

В приведенном выше сценарии у нас есть один поток издателя и одна линия подписчиков. Мы можем гарантировать, что сообщение находится в порядке по какой-то временной метке или идентификатору последовательности.

Однако в среде нескольких одновременно работающих издателей и потребителей упорядочить сообщения сложно. Почему? Давайте посмотрим на приведенный ниже сценарий, в котором у нас есть несколько издателей и один подписчик, а также несколько подписчиков с одним издателем.

Один сценарий, который приводит к нарушению порядка сообщений в среде с несколькими издателями, - это когда каждый издатель публикует сообщения в брокере сообщений. Брокеру обычно необходимо подтвердить, что он получил сообщение от подписчика, иначе сообщение будет потеряно. Если сервер брокера не работает, издатель обычно снова отправляет сообщения брокеру.

Представьте, что у вас есть 2 издателя - издатель A и издатель B. Издатель A отправляет сообщение с меткой времени в систему сообщений перед издателем B. Когда издатель A отправляет сообщение брокеру, брокер не может подтвердить сообщение из-за какого-то внутреннего сервера ошибка. Пару раз пройди, он снова встанет. Тем временем издатель B отправляет свое сообщение брокеру сообщений. Посредник сообщений получил сообщения от издателя B. Затем издатель A снова отправляет сообщение брокеру сообщений с интервалом повтора. У нас есть несоответствие сообщения о заказе.

Чтобы добиться упорядочения сообщений, нескольким издателям необходимо согласовать друг с другом, какое сообщение отправить первым. Это можно сделать двумя способами: либо издатели координируют друг друга, чтобы прикрепить понятие порядка, либо система сообщений должна обеспечивать представление о порядке в каждой входящей системе. В любом случае нам нужен централизованный подход для получения понятия временной метки, чтобы избежать дрейфа часов, и идентификатора последовательности для обеспечения согласованности. У нас может быть система координации, такая как Zookeeper, для координации доставки заказа при отправке сообщения. Однако приведенные выше решения означают, что нам нужно ограничивать нашу пропускную способность только одним издателем за раз.

Давайте посмотрим на принимающую сторону. Если у нас есть несколько подписчиков, которые подписываются на очередь, может быть сценарий, при котором сообщение будет не в порядке. Если у вас есть 2 подписчика - подписчик A и подписчик B. После того, как подписчик A подтвердил сообщение, которое он опросил из очереди, экземпляр умер. Тем временем подписчик B получил следующее предстоящее сообщение из очереди, принял и обработал его. После того, как подписчик А был резервным, он обработал это сообщение. В этом случае система нарушает свойства упорядочивания букв.

Мы также можем использовать то же решение, что и сценарий издателя, где один подписчик будет ждать, пока предыдущий подписчик завершит обработку, прежде чем он опрашивает следующее сообщение. В этом случае мы также ограничиваем нашу пропускную способность до одного подписчика за раз.

Из двух приведенных выше иллюстраций мы знаем, что решение для создания порядка сообщений состоит в том, чтобы ограничить пропускную способность одним издателем и подписчиком по одному. Предположим, что несколько потоков издателя асинхронно публикуют в одной и той же системе сообщений. В этом случае порядок не имеет значения. Сообщение появилось в очереди в зависимости от того, когда издатель был запланирован. На стороне подписчика нам нужно обрабатывать очередь по одному сообщению за раз, чтобы избежать любого вида неправильного порядка.

Ровно однажды сообщение

Чтобы получить одноразовое сообщение, мы не хотим дублировать данные в системе сообщений. Давайте посмотрим на синхронную среду и рассмотрим сценарии с несколькими издателями и подписчиками.

В синхронных средах редко встречаются повторяющиеся сообщения. Издатель может публиковать данные по одному; опубликуйте следующий, как только будет получен предыдущий, чтобы гарантировать отсутствие повторяющихся данных в брокере. Со стороны подписчика мы получали данные по одному. Следовательно, не будет условий гонки, при которых информация будет получена несколькими подписчиками.

В среде с несколькими издателями мы можем увидеть, как повторяющиеся данные могут возникать в системе сообщений. Представьте, что несколько издателей публикуют сообщения в очереди сообщений. Если брокеру сообщений не удается отправить подтверждение одному из издателей, потому что оно было недоступно, они будут повторять попытку системы несколько раз, чтобы избежать потери сообщения.

Один из способов решить эту проблему - создать идентификатор корреляции. Идентификатор корреляции будет идентификатором, который идентифицирует его ключ идемпотентности в сообщении. Следовательно, если система сообщений получила тот же идентификатор корреляции, она знает, что он уже был получен ранее. Например, Amazon SQS имеет deduplicationId в очереди FIFO, который служит идентификатором корреляции. Однако срок его действия составляет 5 минут. Если издатель отправляет второе сообщение через 5 минут, брокер сообщений не может гарантировать, отклонит ли он повторяющееся сообщение.

С другой стороны, единовременную доставку для нескольких подписчиков практически невозможно. Однако, даже если вы можете гарантировать ровно один раз на транспортном уровне, вы, вероятно, все равно этого не захотите. Если брокер сообщений удаляет сообщение сразу после того, как подписчик получил его, все еще существует вероятность сбоя даже до того, как подписчик обработает данные. В этом сценарии вы хотите, чтобы система сообщений снова доставляла сообщение.

Возможна однократная обработка дублирующих сообщений. Рассмотрим один сценарий: подписчик А получил сообщение от брокера. Прямо перед тем, как подписчик A собирается удалить сообщение, подписчик B опрашивает сообщение брокера. Следовательно, подписчик B будет иметь то же сообщение, что и подписчик A. Одним из решений этого является создание надежного хранилища на стороне получателя. Подписчик может записывать входящее сообщение в постоянное хранилище во время каждого опроса. После успешной записи в хранилище он удалит очередь из брокера сообщений. Надежное хранилище защитит от дублирования значений. Между тем, для обработки этих сообщений будет другой асинхронный фоновый поток, обрабатывающий сообщения. Эта концепция аналогична тому, как различные системы баз данных используют журнал упреждающей записи для хранения коммитов.

Закрытие

Очереди FIFO в единой среде легко понять. Однако в распределенной среде система может создать очередь FIFO, если один издатель публикует сообщение по одному, чтобы обеспечить упорядочение и создать систему, предотвращающую дублирование сообщений.

Если вы проектируете свои системы, которые обрабатывают эти два сценария, описанных выше, то ваша очередь FIFO может существовать в распределенной системе. Если вы не можете указать эти два свойства в своей очереди, ваша система не сможет выполнить обработку только один раз.

Теперь вы скажете мне, может ли существовать только однократная обработка во всем системном событии?

Спасибо за внимание! Если вам понравился этот пост, вы можете подписаться на меня и подписаться на меня на Medium, чтобы увидеть больше подобных постов.

Первоначально опубликовано на https://edward-huang.com.