Работа с дублированием в очереди сообщений - PullRequest
11 голосов
/ 28 июня 2011

Я спорил с моим программистом о том, как лучше всего это сделать.У нас есть данные, которые поступают со скоростью около 10000 объектов в секунду.Это необходимо обрабатывать асинхронно, но достаточно свободного упорядочения, поэтому каждый объект циклически вставляется в одну из нескольких очередей сообщений (есть также несколько производителей и потребителей).Каждый объект составляет ~ 300 байт.И он должен быть долговечным, чтобы MQ были настроены на сохранение на диске.

Проблема в том, что часто эти объекты дублируются (так как они неизбежно дублируются в данных, поступающих к производителю).У них есть 10-байтовые уникальные идентификаторы.Это не катастрофично, если объекты дублируются в очереди, но это если они дублируются при обработке после извлечения из очереди.Каков наилучший способ обеспечить максимально возможную линейную масштабируемость, не допуская дублирования при обработке объектов?И, возможно, это связано с тем, должен ли весь объект храниться в очереди сообщений или только идентификатор с телом, хранящимся в чем-то вроде кассандры? Подтверждено, где происходит дублирование.Кроме того, до сих пор у меня было 2 рекомендации для Redis.Ранее я рассматривал RabbitMQ.Каковы плюсы и минусы каждого в отношении моих требований?

Ответы [ 3 ]

3 голосов
/ 28 июня 2011

ps: это первый раз в моей жизни, когда на сайте Redis возникают проблемы, но, держу пари, когда вы посещаете его, они решили проблему

> We have data that comes in at a rate
> of about 10000 objects per second.
> This needs to be processed
> asynchronously, but loose ordering is
> sufficient, so each object is inserted
> round-robin-ly into one of several
> message queues (there are also several
> producers and consumers)

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

Сначала я хочу показать вам информацию о моем ноутбуке(Мне это нравится, но большой сервер будет намного быстрее;)).Мой папа (был немного впечатлен :)) недавно купил новый ПК, и он сильно бьет по моему ноутбуку (8 процессоров вместо 2).

-Computer-
Processor       : 2x Intel(R) Core(TM)2 Duo CPU     T7100  @ 1.80GHz
Memory      : 2051MB (1152MB used)
Operating System        : Ubuntu 10.10
User Name       : alfred (alfred)
-Display-
Resolution      : 1920x1080 pixels
OpenGL Renderer     : Unknown
X11 Vendor      : The X.Org Foundation
-Multimedia-
Audio Adapter       : HDA-Intel - HDA Intel
-Input Devices-
 Power Button
 Lid Switch
 Sleep Button
 Power Button
 AT Translated Set 2 keyboard
 Microsoft Comfort Curve Keyboard 2000
 Microsoft Comfort Curve Keyboard 2000
 Logitech Trackball
 Video Bus
 PS/2 Logitech Wheel Mouse
-SCSI Disks-
HL-DT-ST DVDRAM GSA-T20N
ATA WDC WD1600BEVS-2

Ниже тестов с использованием redis-benchmark на моей машине даже безпроводим большую оптимизацию redis:

alfred@alfred-laptop:~/database/redis-2.2.0-rc4/src$ ./redis-benchmark 
====== PING (inline) ======
  10000 requests completed in 0.22 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

94.84% <= 1 milliseconds
98.74% <= 2 milliseconds
99.65% <= 3 milliseconds
100.00% <= 4 milliseconds
46296.30 requests per second

====== PING ======
  10000 requests completed in 0.22 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

91.30% <= 1 milliseconds
98.21% <= 2 milliseconds
99.29% <= 3 milliseconds
99.52% <= 4 milliseconds
100.00% <= 4 milliseconds
45662.10 requests per second

====== MSET (10 keys) ======
  10000 requests completed in 0.32 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

3.45% <= 1 milliseconds
88.55% <= 2 milliseconds
97.86% <= 3 milliseconds
98.92% <= 4 milliseconds
99.80% <= 5 milliseconds
99.94% <= 6 milliseconds
99.95% <= 9 milliseconds
99.96% <= 10 milliseconds
100.00% <= 10 milliseconds
30864.20 requests per second

====== SET ======
  10000 requests completed in 0.21 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

92.45% <= 1 milliseconds
98.78% <= 2 milliseconds
99.00% <= 3 milliseconds
99.01% <= 4 milliseconds
99.53% <= 5 milliseconds
100.00% <= 5 milliseconds
47169.81 requests per second

====== GET ======
  10000 requests completed in 0.21 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

94.50% <= 1 milliseconds
98.21% <= 2 milliseconds
99.50% <= 3 milliseconds
100.00% <= 3 milliseconds
47619.05 requests per second

====== INCR ======
  10000 requests completed in 0.23 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

91.90% <= 1 milliseconds
97.45% <= 2 milliseconds
98.59% <= 3 milliseconds
99.51% <= 10 milliseconds
99.78% <= 11 milliseconds
100.00% <= 11 milliseconds
44444.45 requests per second

====== LPUSH ======
  10000 requests completed in 0.21 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

95.02% <= 1 milliseconds
98.51% <= 2 milliseconds
99.23% <= 3 milliseconds
99.51% <= 5 milliseconds
99.52% <= 6 milliseconds
100.00% <= 6 milliseconds
47619.05 requests per second

====== LPOP ======
  10000 requests completed in 0.21 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

95.89% <= 1 milliseconds
98.69% <= 2 milliseconds
98.96% <= 3 milliseconds
99.51% <= 5 milliseconds
99.98% <= 6 milliseconds
100.00% <= 6 milliseconds
47619.05 requests per second

====== SADD ======
  10000 requests completed in 0.22 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

91.08% <= 1 milliseconds
97.79% <= 2 milliseconds
98.61% <= 3 milliseconds
99.25% <= 4 milliseconds
99.51% <= 5 milliseconds
99.81% <= 6 milliseconds
100.00% <= 6 milliseconds
45454.55 requests per second

====== SPOP ======
  10000 requests completed in 0.22 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

91.88% <= 1 milliseconds
98.64% <= 2 milliseconds
99.09% <= 3 milliseconds
99.40% <= 4 milliseconds
99.48% <= 5 milliseconds
99.60% <= 6 milliseconds
99.98% <= 11 milliseconds
100.00% <= 11 milliseconds
46296.30 requests per second

====== LPUSH (again, in order to bench LRANGE) ======
  10000 requests completed in 0.23 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

91.00% <= 1 milliseconds
97.82% <= 2 milliseconds
99.01% <= 3 milliseconds
99.56% <= 4 milliseconds
99.73% <= 5 milliseconds
99.77% <= 7 milliseconds
100.00% <= 7 milliseconds
44247.79 requests per second

====== LRANGE (first 100 elements) ======
  10000 requests completed in 0.39 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

6.24% <= 1 milliseconds
75.78% <= 2 milliseconds
93.69% <= 3 milliseconds
97.29% <= 4 milliseconds
98.74% <= 5 milliseconds
99.45% <= 6 milliseconds
99.52% <= 7 milliseconds
99.93% <= 8 milliseconds
100.00% <= 8 milliseconds
25906.74 requests per second

====== LRANGE (first 300 elements) ======
  10000 requests completed in 0.78 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

1.30% <= 1 milliseconds
5.07% <= 2 milliseconds
36.42% <= 3 milliseconds
72.75% <= 4 milliseconds
93.26% <= 5 milliseconds
97.36% <= 6 milliseconds
98.72% <= 7 milliseconds
99.35% <= 8 milliseconds
100.00% <= 8 milliseconds
12886.60 requests per second

====== LRANGE (first 450 elements) ======
  10000 requests completed in 1.10 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

0.67% <= 1 milliseconds
3.64% <= 2 milliseconds
8.01% <= 3 milliseconds
23.59% <= 4 milliseconds
56.69% <= 5 milliseconds
76.34% <= 6 milliseconds
90.00% <= 7 milliseconds
96.92% <= 8 milliseconds
98.55% <= 9 milliseconds
99.06% <= 10 milliseconds
99.53% <= 11 milliseconds
100.00% <= 11 milliseconds
9066.18 requests per second

====== LRANGE (first 600 elements) ======
  10000 requests completed in 1.48 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

0.85% <= 1 milliseconds
9.23% <= 2 milliseconds
11.03% <= 3 milliseconds
15.94% <= 4 milliseconds
27.55% <= 5 milliseconds
41.10% <= 6 milliseconds
56.23% <= 7 milliseconds
78.41% <= 8 milliseconds
87.37% <= 9 milliseconds
92.81% <= 10 milliseconds
95.10% <= 11 milliseconds
97.03% <= 12 milliseconds
98.46% <= 13 milliseconds
99.05% <= 14 milliseconds
99.37% <= 15 milliseconds
99.40% <= 17 milliseconds
99.67% <= 18 milliseconds
99.81% <= 19 milliseconds
99.97% <= 20 milliseconds
100.00% <= 20 milliseconds
6752.19 requests per second

Как можно надеяться, из сравнительного анализа моего простого ноутбука вы, вероятно, просто нуждаетесь в одной очереди сообщений, потому что может перенаправить обработку 10000 lpush запросов за 0,23 секунды и 10000 lpop запросов за 0,21 секунды.Когда вам просто нужна одна очередь, я думаю, что ваша проблема больше не является проблемой (или производители дублируют дубликаты, которые я не до конца понимаю?).

> And it needs to be durable, so the MQs
> are configured to persist to disk.

redis также сохраняется на диске.

> The problem is that often these
> objects are duplicated. They do have
> 10-byte unique ids. It's not
> catastrophic if objects are duplicated
> in the queue, but it is if they're
> duplicated in the processing after
> being taken from the queue. What's the
> best way to go about ensuring as close
> as possible to linear scalability
> whilst ensuring there's no duplication
> in the processing of the objects?

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

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

Если возможно, вы должны хранить всю свою информацию непосредственно в памяти, потому что ничто не может работать так же быстро, как память (хорошо, ваш кеш память еще быстрее, но действительно очень мала, плюс вы не можете получить к ней доступ через свой код)Redis хранит всю вашу информацию в памяти и делает снимки на диск.Я думаю, что вы должны иметь возможность хранить всю свою информацию в памяти и пропустить, используя что-то вроде Кассандры.

Давайте рассмотрим, что каждый объект составляет 400 байт на каждый объект со скоростью 10000 в секунду => 4000000 байт для всех объектов в секунду => 4 МБ / с, если мои вычисления верны.Вы можете легко хранить это количество информации в вашей памяти.Если вы не можете это сделать, вам стоит подумать об обновлении памяти, если это вообще возможно, потому что память уже не так дорога.

2 голосов
/ 28 июня 2011

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

Я видел, как этот сценарий происходил разными способами; работники, работающие по тайм-ауту, из-за которых сообщение снова становится видимым в очереди (и, следовательно, обрабатываются во второй раз, что характерно для Kestrel), неправильно настроенные посредники (на ум приходит HA ActiveMQ), неправильно настроенные клиенты (приходит на ум маршрутизация Spring плюс Camel) , двойная отправка клиентов и т. д. Существует только несколько способов решения этой проблемы.

Так как я не могу на самом деле диагностировать проблему, я подключу redis здесь. Вы можете легко объединить что-то вроде SPOP (что O (1), как и SADD) с pub / sub для невероятно быстрого, постоянного времени, без дубликатов (наборы должны содержать уникальные элементы) очередь. Хотя это проект ruby, resque может помочь. По крайней мере, стоит посмотреть.

Удачи.

1 голос
/ 30 июня 2011

Если вы не возражаете бросить верблюда в смесь, тогда вы можете использовать idempotent-consumer EIP, чтобы помочь с этим.

Кроме того, Группы сообщений ActiveMQ могут использоваться для группировки связанных сообщений и упрощения их выполнения дублирующих проверок, при этом сохраняя высокую пропускную способность и т. Д. *

...