2016-12-01 10:52:22 -02:00
|
|
|
package amqp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
|
2016-12-01 11:40:58 -02:00
|
|
|
"github.com/eventials/goevents/messaging"
|
|
|
|
|
2016-12-01 10:52:22 -02:00
|
|
|
amqplib "github.com/streadway/amqp"
|
|
|
|
)
|
|
|
|
|
|
|
|
type handler struct {
|
|
|
|
action string
|
2016-12-01 11:40:58 -02:00
|
|
|
handler messaging.EventHandler
|
2016-12-01 10:52:22 -02:00
|
|
|
re *regexp.Regexp
|
|
|
|
}
|
|
|
|
|
|
|
|
type Consumer struct {
|
|
|
|
conn *Connection
|
|
|
|
autoAck bool
|
|
|
|
handlers []handler
|
2016-12-28 09:20:29 -02:00
|
|
|
|
|
|
|
channel *amqplib.Channel
|
|
|
|
queue *amqplib.Queue
|
|
|
|
|
|
|
|
exchangeName string
|
|
|
|
queueName string
|
2016-12-01 10:52:22 -02:00
|
|
|
}
|
|
|
|
|
2016-12-01 16:17:55 -02:00
|
|
|
// NewConsumer returns a new AMQP Consumer.
|
2016-12-28 09:20:29 -02:00
|
|
|
func NewConsumer(c messaging.Connection, autoAck bool, exchange, queue string) (messaging.Consumer, error) {
|
2016-12-01 10:52:22 -02:00
|
|
|
amqpConn := c.(*Connection)
|
|
|
|
|
2016-12-28 09:20:29 -02:00
|
|
|
ch, err := amqpConn.connection.Channel()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ch.ExchangeDeclare(
|
|
|
|
exchange, // name
|
|
|
|
"topic", // type
|
|
|
|
true, // durable
|
|
|
|
false, // auto-delete
|
|
|
|
false, // internal
|
|
|
|
false, // no-wait
|
|
|
|
nil, // arguments
|
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
q, err := ch.QueueDeclare(
|
|
|
|
queue, // name
|
|
|
|
true, // durable
|
|
|
|
false, // auto-delete
|
|
|
|
false, // exclusive
|
|
|
|
false, // no-wait
|
|
|
|
nil, // arguments
|
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-12-01 10:52:22 -02:00
|
|
|
return &Consumer{
|
|
|
|
amqpConn,
|
|
|
|
autoAck,
|
|
|
|
make([]handler, 0),
|
2016-12-28 09:20:29 -02:00
|
|
|
ch,
|
|
|
|
&q,
|
|
|
|
exchange,
|
|
|
|
queue,
|
2016-12-01 10:52:22 -02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2016-12-28 09:20:29 -02:00
|
|
|
func (c *Consumer) Close() {
|
|
|
|
c.channel.Close()
|
|
|
|
}
|
|
|
|
|
2016-12-01 10:52:22 -02:00
|
|
|
func (c *Consumer) dispatch(msg amqplib.Delivery) {
|
|
|
|
if fn, ok := c.getHandler(msg.RoutingKey); ok {
|
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
if !c.autoAck {
|
|
|
|
msg.Nack(false, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
ok := fn(msg.Body)
|
|
|
|
|
|
|
|
if !c.autoAck {
|
|
|
|
if ok {
|
|
|
|
msg.Ack(false)
|
|
|
|
} else {
|
|
|
|
msg.Nack(false, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// got a message from wrong exchange?
|
|
|
|
// ignore and don't requeue.
|
|
|
|
msg.Nack(false, false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-01 11:40:58 -02:00
|
|
|
func (c *Consumer) getHandler(action string) (messaging.EventHandler, bool) {
|
2016-12-01 10:52:22 -02:00
|
|
|
for _, h := range c.handlers {
|
|
|
|
if h.re.MatchString(action) {
|
|
|
|
return h.handler, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
2016-12-01 16:17:55 -02:00
|
|
|
// Subscribe allow to subscribe an action handler.
|
2016-12-01 11:40:58 -02:00
|
|
|
func (c *Consumer) Subscribe(action string, handlerFn messaging.EventHandler) error {
|
2016-12-01 10:52:22 -02:00
|
|
|
// TODO: Replace # pattern too.
|
|
|
|
pattern := strings.Replace(action, "*", "(.*)", 0)
|
|
|
|
re, err := regexp.Compile(pattern)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-12-28 09:20:29 -02:00
|
|
|
err = c.channel.QueueBind(
|
|
|
|
c.queueName, // queue name
|
|
|
|
action, // routing key
|
|
|
|
c.exchangeName, // exchange
|
|
|
|
false, // no-wait
|
|
|
|
nil, // arguments
|
2016-12-01 10:52:22 -02:00
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.handlers = append(c.handlers, handler{
|
|
|
|
action,
|
|
|
|
handlerFn,
|
|
|
|
re,
|
|
|
|
})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-12-01 16:17:55 -02:00
|
|
|
// Unsubscribe allows to unsubscribe an action handler.
|
2016-12-01 10:52:22 -02:00
|
|
|
func (c *Consumer) Unsubscribe(action string) error {
|
2016-12-28 09:20:29 -02:00
|
|
|
err := c.channel.QueueUnbind(
|
|
|
|
c.queueName, // queue name
|
|
|
|
action, // routing key
|
|
|
|
c.exchangeName, // exchange
|
|
|
|
nil, // arguments
|
2016-12-01 10:52:22 -02:00
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
idx := -1
|
|
|
|
|
|
|
|
for i, h := range c.handlers {
|
|
|
|
if h.action == action {
|
|
|
|
idx = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if idx != -1 {
|
|
|
|
c.handlers = append(c.handlers[:idx], c.handlers[idx+1:]...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-12-01 16:17:55 -02:00
|
|
|
// Listen start to listen for new messages.
|
2016-12-28 09:20:29 -02:00
|
|
|
func (c *Consumer) Consume() error {
|
|
|
|
msgs, err := c.channel.Consume(
|
|
|
|
c.queueName, // queue
|
|
|
|
"", // consumer
|
|
|
|
c.autoAck, // auto ack
|
|
|
|
false, // exclusive
|
|
|
|
false, // no local
|
|
|
|
false, // no wait
|
|
|
|
nil, // args
|
2016-12-01 10:52:22 -02:00
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-12-28 09:20:29 -02:00
|
|
|
for m := range msgs {
|
|
|
|
c.dispatch(m)
|
2016-12-01 10:52:22 -02:00
|
|
|
}
|
|
|
|
|
2016-12-28 09:20:29 -02:00
|
|
|
return nil
|
2016-12-01 10:52:22 -02:00
|
|
|
}
|