Messaging With Rabbitmq in Golang
Messaging With Rabbitmq in Golang
RabbitMQ in Golang
I’m Andi Pangeran
Currently : GOPAY - Backend Developer
@apangeran
Community :
@a_pangeran
https://
cybercoding.wordpress.com
Overview
Communication basic, Synchronous vs.
Asynchronous
RabbitMQ in a nutshell
Production Checklist
watch your communication...
Synchronous
3.
Payment
External H2H
Service
2.
1.
4.
Order Invoice
6. Service Service
5.
Mail
Service
Asynchronous
Location Transparency
Sender Broker Receiver Sender can send whether the recipient ready or not
Payment
External H2H
Service
Order Invoice
BROKER
Service Service
Mail
Service
delivery guarantees...
Two General Problem
General General
A B
Castle
Two armies, each led by a different general, are preparing to attack a fortified city. The armies are encamped near the city, each in its
own valley. A third valley separates the two hills, and the only way for the two generals to communicate is by sending messengers
through the valley. Unfortunately, the valley is occupied by the city's defenders and there's a chance that any given messenger sent
through the valley will be captured.
It is required that the two generals have their armies attack the city at the same time in order to succeed, else the lone
attacker army will die trying.
At Most Once
General General
A B
Castle
If it lost or the recipient fails to process it, there is no attempt to recover it (retried)
At Least Once
General General
A B
Castle
Resend can be the result of lost confirmation, the recipient may receive the message more than once
Exactly Once
General General
A B
Castle
Implement deduplicator for keep track of which request already been processed
RabbitMQ in Nutshell...
AMQP Model
Broker
Routes
Sender Exchanger Queue Receiver
Queue
A queue binds to the exchange with a routing key K
When a new message with routing key R arrives at the direct exchange,
Exchanger
the exchange routes it to the queue if K = R
Queue
Queue
Fanout Exchange
Broker
Queue
routes messages to all of the queues that are bound to it and the routing
key is ignored.
Exchanger when a new message is published to that exchange a copy of the message
Queue is delivered to all N queues
Queue
Topic Exchange
Broker
Queue
A queue binds to the exchange with a routing key K
When a new message with routing key R arrives at the direct exchange,
Exchanger
the exchange routes it to the queue if K = R
Queue
Queue
Acknowledgements
exchangeName := ""
routingKey := client.publishQueue
publishing := amqp.Publishing{
ContentType: messaging.MESSAGING_CONTENT_TYPE,
ContentEncoding: "UTF-8",
CorrelationId: cid,
DeliveryMode: amqp.Persistent,
Body: data,
}
table := make(amqp.Table)
table["x-dead-letter-exchange"] = ""
table["x-dead-letter-routing-key"] = deadLetterQueue
if _, err := channel.QueueDeclare(c.QueueName, true, false, false, false, table); err != nil {
return err
}
go c.runEventProcessor(deliveryChan)
check github.com/meong1234/go-rabbit
production checklist...
Graceful Shutdown
Wait OS interrupt signal and trigger stop
for {
select {
On Close, stop process new message wait for all worker done. ex: case d := <-deliveryChan:
if len(d.Body) == 0 {
using stop channel and waitgroup d.Nack(false, false)
return
}
func AppRunner(daemon util.Daemon) error { c.Logger.Debugf("recieved message %s\n", d.CorrelationId)
err := daemon.Start()
func (c *amqpSubscriber) Close() error { workerNumber := pool.Get()
if err != nil {
c.Logger.Debugf("close triggered") event := amqpEvent{d}
return err c.processingWG.Add(1)
close(c.stopChan)
}
go func(worker int, event *amqpEvent, log util.Logger) {
// Waiting for any tasks being processed to finish logger := log.WithField("workerNumber", worker).
osSignals := make(chan os.Signal) WithField("correlationId", event.GetCorrelationID())
c.processingWG.Wait()
signal.Notify(osSignals, os.Interrupt)
ctx := util.NewSessionCtx(event.GetCorrelationID(), logger)
if err :=
select { c.channel.Cancel(c.consumerId, false); err != nil { logger.WithField("message", string(event.GetBody())).Debug("process event started")
return err
case <-osSignals: c.processor(ctx, event)
logger.Debug("process event finish")
}util.Log.Infof("osSignal Interrupt trigerred")
return
return c.channel.Close()
daemon.Stop() defer func() {
}} c.processingWG.Done()
pool.Put(worker)
} }()
case <-c.stopChan:
c.Logger.Debugf("stop triggered")
return
}
}
Reconnect
Remember fallacy of distributed system, The network is reliable. we can handle by listening close event
for reconnect
for {
errors := make(chan *amqp.Error) select {
b.conn.NotifyClose(errors) case err := <-b.errors:
util.Log.Warnf("Connection lost: %v\n", err)
time.Sleep(10 * time.Second)
b.connect()
case stop := <-b.watchStop:
if stop {
return
}
}
}
Consumer Prefetch
}
) return nil
}
Nodes Utilization:
Exchange performance :
Queue Performance:
Message rates, Messages that move in or out of a queue per second, whether unacknowledged,
delivered, acknowledged, or redelivered
Consumer utilization, Proportion of time that the queue can deliver messages to consumers
Reference
https://www.rabbitmq.com/
https://insidethecpu.com/2014/11/11/rabbitmq-qos-vs-competing-consumers/
https://www.manning.com/books/rabbitmq-in-depth
https://www.datadoghq.com/blog/rabbitmq-monitoring/
https://www.slideshare.net/ktoso/building-a-reactive-system-with-akka-workshop-oreilly-saconf-nyc
https://en.wikipedia.org/wiki/Two_Generals%27_Problem