/
main.go
150 lines (140 loc) · 3.65 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Package coelho offers pub/sub functions to send/recieve messages from rabbitMQ.
// The idea is to have a simple exported Subscribe and Publish functions.
// Sub
package coelho
import (
"time"
log "github.com/Sirupsen/logrus"
"github.com/streadway/amqp"
"golang.org/x/net/context"
)
// Message mirrors a rabbitMQ message
type Message struct {
Body []byte
Rk string
Msg amqp.Delivery
}
// Session composes an amqp.Connection with an amqp.Channel
type Session struct {
*amqp.Connection
*amqp.Channel
}
// Rabbit holds the details of the Con, Ch
type Rabbit struct {
Arguments map[string]interface{}
AutoAck bool
Delete bool // delete when usused
Durable bool
Exchange string
ExchangeType string
Exclusive bool
Internal bool
NoWait bool
QoS int
}
// DeclareExc decleares an exchange with false auto-delete, and false internal flags.
func (r Rabbit) DeclareExc(ch *amqp.Channel) error {
err := ch.ExchangeDeclare(
r.Exchange, // name
r.ExchangeType, // type
r.Durable, // durable
r.Delete, // auto-deleted
r.Internal, // internal
r.NoWait, // no-wait
r.Arguments, // arguments
)
return err
}
// Bind the queue to an exchange
func (r Rabbit) Bind(ch *amqp.Channel, queueName string, rk string) error {
log.Infof("Binding queue %s to exchange %s with routing key %s with %s exchange ",
queueName, r.Exchange, rk, r.ExchangeType)
err := ch.QueueBind(
queueName,
rk,
r.Exchange,
r.NoWait,
r.Arguments)
return err
}
// DeclareQueue returns a decleared queue
func (r Rabbit) DeclareQueue(ch *amqp.Channel, queueName string) (amqp.Queue, error) {
qd, err := ch.QueueDeclare(
queueName,
r.Durable,
r.Delete,
r.Exclusive,
r.NoWait,
r.Arguments,
)
return qd, err
}
// Close tears the connection down, taking the channel with it.
func (s Session) Close() error {
if s.Connection == nil {
return nil
}
return s.Connection.Close()
}
/*Redial continually connects to the URL, returns no longer possible
*no guarantee on the number of sessions returned on close.
*==============
*URL reference
*amqp://user:pass@host:port/vhost
*a different URL-structure will not work
*==============
*/
func (r Rabbit) Redial(ctx context.Context, url string) chan Session {
sessions := make(chan Session)
go func() {
defer close(sessions)
for {
select {
default:
log.Info("Dialing")
case <-ctx.Done():
log.Infof("Shutting down session factory")
return
}
conn, err := amqp.Dial(url)
if err != nil {
log.Warnf("Can't dial. Waiting 10 seconds...")
time.Sleep(10 * time.Second)
conn, err = amqp.Dial(url)
if err != nil {
log.Errorf("cannot (re)dial: %v: %q", err, url)
return
}
}
ch, err := conn.Channel()
if err != nil {
log.Errorf("cannot create channel %v: %v", r.Exchange, err)
return
}
// idempotent declaration
if err := r.DeclareExc(ch); err != nil {
log.Errorf("cannot declare %v exchange: %v", r.ExchangeType, err)
return
}
//Deliveries on the returned chan will be buffered indefinitely. To limit memory
//of this buffer, use the Channel.Qos method to limit the amount of
//unacknowledged/buffered deliveries the server will deliver on this Channel.
err = ch.Qos(
r.QoS, // prefetch count
0, // prefetch size
false) // global
if err != nil {
log.Errorf("Error setting Qos %v", err)
}
select {
// this will block here if the subscriber is not using the session
case sessions <- Session{conn, ch}:
log.Info("New session has been initialized.")
case <-ctx.Done():
log.Infof("Shutting down session")
return
}
}
}()
return sessions
}