Class: AceMQ::AMQP::Patterns::ConsumerGroup
- Inherits:
-
Object
- Object
- AceMQ::AMQP::Patterns::ConsumerGroup
- Defined in:
- lib/acemq/amqp/patterns/consumer_group.rb
Overview
A group of consumers reading the same queue.
group = Patterns::ConsumerGroup.new(mq, "orders.new", size: 4) do ||
place(.payload)
Ack.accept
end
at_exit { group.close }
Two things it saves. Starting workers by hand means remembering to stop every one, and a partial shutdown leaves messages held by a consumer nobody is waiting for. And a group can be sized from configuration, which is the number most often changed after a service is running.
Concurrency, or a group?
concurrency: on Connection#consume runs several handlers on one
consumer and one channel. A group runs several consumers, each with its
own channel and its own prefetch. Reach for the group when handlers are
slow enough that one channel's prefetch becomes the limit, or when a
fair share across processes matters: the broker round-robins between
consumers, so four here compete evenly with four in another instance
where one consumer with concurrency four would not.
Instance Attribute Summary collapse
-
#consumers ⇒ Object
readonly
Returns the value of attribute consumers.
-
#queue ⇒ Object
readonly
Returns the value of attribute queue.
Instance Method Summary collapse
-
#close(timeout: Connection::DRAIN_TIMEOUT) ⇒ nil
Stops every consumer and waits for the handlers already running.
-
#initialize(connection, queue, size:, **options, &handler) ⇒ ConsumerGroup
constructor
A new instance of ConsumerGroup.
-
#size ⇒ Object
How many consumers are running.
Constructor Details
#initialize(connection, queue, size:, **options, &handler) ⇒ ConsumerGroup
Returns a new instance of ConsumerGroup.
51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/acemq/amqp/patterns/consumer_group.rb', line 51 def initialize(connection, queue, size:, **, &handler) unless handler raise ArgumentError, "a consumer group needs a block to handle messages" end unless size.to_i.positive? raise ArgumentError, "a consumer group needs at least one consumer, not #{size}" end @queue = queue @lock = Mutex.new @consumers = start(connection, size.to_i, , handler) end |
Instance Attribute Details
#consumers ⇒ Object (readonly)
Returns the value of attribute consumers.
45 46 47 |
# File 'lib/acemq/amqp/patterns/consumer_group.rb', line 45 def consumers @consumers end |
#queue ⇒ Object (readonly)
Returns the value of attribute queue.
45 46 47 |
# File 'lib/acemq/amqp/patterns/consumer_group.rb', line 45 def queue @queue end |
Instance Method Details
#close(timeout: Connection::DRAIN_TIMEOUT) ⇒ nil
Stops every consumer and waits for the handlers already running.
Every one is stopped even when one of them refuses, because leaving the rest running after a failed shutdown is worse than the failure. The first refusal is raised once the others are down.
The timeout is for the group, not for each member. A group is the one place this library makes several consumers at once, so a per-consumer wait is multiplied by the size the caller asked for: four consumers at thirty seconds each is two minutes, which is past every orchestrator's grace period and therefore a drain that ends in SIGKILL with everything in flight unsettled. Connection#close is bounded the same way, by the same code.
85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/acemq/amqp/patterns/consumer_group.rb', line 85 def close(timeout: Connection::DRAIN_TIMEOUT) running = @lock.synchronize do taken = @consumers @consumers = [] taken end failure, stranded = Drain.of(running, timeout) raise failure if failure raise DrainTimeout.new(stranded, timeout) unless stranded.empty? nil end |
#size ⇒ Object
How many consumers are running.
66 |
# File 'lib/acemq/amqp/patterns/consumer_group.rb', line 66 def size = @consumers.size |