forked from armon/relay
/
publisher.go
95 lines (86 loc) · 2.14 KB
/
publisher.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
package relay
import (
"bytes"
"fmt"
"github.com/streadway/amqp"
"log"
"strconv"
"time"
)
// Publisher is a type that is used only for publishing messages to a single queue.
// Multiple Publishers can multiplex a single relay
type Publisher struct {
conf *Config
queue string
channel *amqp.Channel
contentType string
mode uint8
buf bytes.Buffer
ackCh chan uint64
nackCh chan uint64
errCh chan *amqp.Error
}
// Publish will send the message to the server to be consumed
func (p *Publisher) Publish(in interface{}) error {
// Check for close
if p.channel == nil {
return ChannelClosed
}
// Encode the message
conf := p.conf
buf := &p.buf
buf.Reset()
if err := conf.Serializer.RelayEncode(buf, in); err != nil {
return fmt.Errorf("Failed to encode message! Got: %s", err)
}
// Format the message
msg := amqp.Publishing{
DeliveryMode: p.mode,
Timestamp: time.Now().UTC(),
ContentType: p.contentType,
Body: buf.Bytes(),
}
// Check for a message ttl
if p.conf.MessageTTL > 0 {
msec := int(p.conf.MessageTTL / time.Millisecond)
msg.Expiration = strconv.Itoa(msec)
}
// Publish the message
if err := p.channel.Publish(conf.Exchange, p.queue, false, false, msg); err != nil {
return fmt.Errorf("Failed to publish to '%s'! Got: %s", p.queue, err)
}
// Check if we wait for confirmation
if !p.conf.DisablePublishConfirm {
select {
case _, ok := <-p.ackCh:
if !ok {
return ChannelClosed
}
return nil
case _, ok := <-p.nackCh:
if !ok {
return ChannelClosed
}
return fmt.Errorf("Failed to publish to '%s'! Got negative ack.", p.queue)
case err, ok := <-p.errCh:
if !ok {
return ChannelClosed
}
log.Printf("[ERR] Publisher got error: (Code %d Server: %v Recoverable: %v) %s",
err.Code, err.Server, err.Recover, err.Reason)
return fmt.Errorf("Failed to publish to '%s'! Got: %s", err.Error())
}
}
return nil
}
// Close will shutdown the publisher
func (p *Publisher) Close() error {
// Make sure close is idempotent
if p.channel == nil {
return nil
}
defer func() {
p.channel = nil
}()
return p.channel.Close()
}