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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
|
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Source code and contact info at http://github.com/streadway/amqp
package amqp
import (
"reflect"
"sync"
"sync/atomic"
)
// 0 1 3 7 size+7 size+8
// +------+---------+-------------+ +------------+ +-----------+
// | type | channel | size | | payload | | frame-end |
// +------+---------+-------------+ +------------+ +-----------+
// octet short long size octets octet
const frameHeaderSize = 1 + 2 + 4 + 1
/*
Channel represents an AMQP channel. Used as a context for valid message
exchange. Errors on methods with this Channel as a receiver means this channel
should be discarded and a new channel established.
*/
type Channel struct {
destructor sync.Once
m sync.Mutex // struct field mutex
confirmM sync.Mutex // publisher confirms state mutex
notifyM sync.RWMutex
connection *Connection
rpc chan message
consumers *consumers
id uint16
// closed is set to 1 when the channel has been closed - see Channel.send()
closed int32
// true when we will never notify again
noNotify bool
// Channel and Connection exceptions will be broadcast on these listeners.
closes []chan *Error
// Listeners for active=true flow control. When true is sent to a listener,
// publishing should pause until false is sent to listeners.
flows []chan bool
// Listeners for returned publishings for unroutable messages on mandatory
// publishings or undeliverable messages on immediate publishings.
returns []chan Return
// Listeners for when the server notifies the client that
// a consumer has been cancelled.
cancels []chan string
// Allocated when in confirm mode in order to track publish counter and order confirms
confirms *confirms
confirming bool
// Selects on any errors from shutdown during RPC
errors chan *Error
// State machine that manages frame order, must only be mutated by the connection
recv func(*Channel, frame) error
// Current state for frame re-assembly, only mutated from recv
message messageWithContent
header *headerFrame
body []byte
}
// Constructs a new channel with the given framing rules
func newChannel(c *Connection, id uint16) *Channel {
return &Channel{
connection: c,
id: id,
rpc: make(chan message),
consumers: makeConsumers(),
confirms: newConfirms(),
recv: (*Channel).recvMethod,
errors: make(chan *Error, 1),
}
}
// shutdown is called by Connection after the channel has been removed from the
// connection registry.
func (ch *Channel) shutdown(e *Error) {
ch.destructor.Do(func() {
ch.m.Lock()
defer ch.m.Unlock()
// Grab an exclusive lock for the notify channels
ch.notifyM.Lock()
defer ch.notifyM.Unlock()
// Broadcast abnormal shutdown
if e != nil {
for _, c := range ch.closes {
c <- e
}
}
// Signal that from now on, Channel.send() should call
// Channel.sendClosed()
atomic.StoreInt32(&ch.closed, 1)
// Notify RPC if we're selecting
if e != nil {
ch.errors <- e
}
ch.consumers.close()
for _, c := range ch.closes {
close(c)
}
for _, c := range ch.flows {
close(c)
}
for _, c := range ch.returns {
close(c)
}
for _, c := range ch.cancels {
close(c)
}
// Set the slices to nil to prevent the dispatch() range from sending on
// the now closed channels after we release the notifyM mutex
ch.flows = nil
ch.closes = nil
ch.returns = nil
ch.cancels = nil
if ch.confirms != nil {
ch.confirms.Close()
}
close(ch.errors)
ch.noNotify = true
})
}
// send calls Channel.sendOpen() during normal operation.
//
// After the channel has been closed, send calls Channel.sendClosed(), ensuring
// only 'channel.close' is sent to the server.
func (ch *Channel) send(msg message) (err error) {
// If the channel is closed, use Channel.sendClosed()
if atomic.LoadInt32(&ch.closed) == 1 {
return ch.sendClosed(msg)
}
return ch.sendOpen(msg)
}
func (ch *Channel) open() error {
return ch.call(&channelOpen{}, &channelOpenOk{})
}
// Performs a request/response call for when the message is not NoWait and is
// specified as Synchronous.
func (ch *Channel) call(req message, res ...message) error {
if err := ch.send(req); err != nil {
return err
}
if req.wait() {
select {
case e, ok := <-ch.errors:
if ok {
return e
}
return ErrClosed
case msg := <-ch.rpc:
if msg != nil {
for _, try := range res {
if reflect.TypeOf(msg) == reflect.TypeOf(try) {
// *res = *msg
vres := reflect.ValueOf(try).Elem()
vmsg := reflect.ValueOf(msg).Elem()
vres.Set(vmsg)
return nil
}
}
return ErrCommandInvalid
}
// RPC channel has been closed without an error, likely due to a hard
// error on the Connection. This indicates we have already been
// shutdown and if were waiting, will have returned from the errors chan.
return ErrClosed
}
}
return nil
}
func (ch *Channel) sendClosed(msg message) (err error) {
// After a 'channel.close' is sent or received the only valid response is
// channel.close-ok
if _, ok := msg.(*channelCloseOk); ok {
return ch.connection.send(&methodFrame{
ChannelId: ch.id,
Method: msg,
})
}
return ErrClosed
}
func (ch *Channel) sendOpen(msg message) (err error) {
if content, ok := msg.(messageWithContent); ok {
props, body := content.getContent()
class, _ := content.id()
// catch client max frame size==0 and server max frame size==0
// set size to length of what we're trying to publish
var size int
if ch.connection.Config.FrameSize > 0 {
size = ch.connection.Config.FrameSize - frameHeaderSize
} else {
size = len(body)
}
if err = ch.connection.send(&methodFrame{
ChannelId: ch.id,
Method: content,
}); err != nil {
return
}
if err = ch.connection.send(&headerFrame{
ChannelId: ch.id,
ClassId: class,
Size: uint64(len(body)),
Properties: props,
}); err != nil {
return
}
// chunk body into size (max frame size - frame header size)
for i, j := 0, size; i < len(body); i, j = j, j+size {
if j > len(body) {
j = len(body)
}
if err = ch.connection.send(&bodyFrame{
ChannelId: ch.id,
Body: body[i:j],
}); err != nil {
return
}
}
} else {
err = ch.connection.send(&methodFrame{
ChannelId: ch.id,
Method: msg,
})
}
return
}
// Eventually called via the state machine from the connection's reader
// goroutine, so assumes serialized access.
func (ch *Channel) dispatch(msg message) {
switch m := msg.(type) {
case *channelClose:
// lock before sending connection.close-ok
// to avoid unexpected interleaving with basic.publish frames if
// publishing is happening concurrently
ch.m.Lock()
ch.send(&channelCloseOk{})
ch.m.Unlock()
ch.connection.closeChannel(ch, newError(m.ReplyCode, m.ReplyText))
case *channelFlow:
ch.notifyM.RLock()
for _, c := range ch.flows {
c <- m.Active
}
ch.notifyM.RUnlock()
ch.send(&channelFlowOk{Active: m.Active})
case *basicCancel:
ch.notifyM.RLock()
for _, c := range ch.cancels {
c <- m.ConsumerTag
}
ch.notifyM.RUnlock()
ch.consumers.cancel(m.ConsumerTag)
case *basicReturn:
ret := newReturn(*m)
ch.notifyM.RLock()
for _, c := range ch.returns {
c <- *ret
}
ch.notifyM.RUnlock()
case *basicAck:
if ch.confirming {
if m.Multiple {
ch.confirms.Multiple(Confirmation{m.DeliveryTag, true})
} else {
ch.confirms.One(Confirmation{m.DeliveryTag, true})
}
}
case *basicNack:
if ch.confirming {
if m.Multiple {
ch.confirms.Multiple(Confirmation{m.DeliveryTag, false})
} else {
ch.confirms.One(Confirmation{m.DeliveryTag, false})
}
}
case *basicDeliver:
ch.consumers.send(m.ConsumerTag, newDelivery(ch, m))
// TODO log failed consumer and close channel, this can happen when
// deliveries are in flight and a no-wait cancel has happened
default:
ch.rpc <- msg
}
}
func (ch *Channel) transition(f func(*Channel, frame) error) error {
ch.recv = f
return nil
}
func (ch *Channel) recvMethod(f frame) error {
switch frame := f.(type) {
case *methodFrame:
if msg, ok := frame.Method.(messageWithContent); ok {
ch.body = make([]byte, 0)
ch.message = msg
return ch.transition((*Channel).recvHeader)
}
ch.dispatch(frame.Method) // termination state
return ch.transition((*Channel).recvMethod)
case *headerFrame:
// drop
return ch.transition((*Channel).recvMethod)
case *bodyFrame:
// drop
return ch.transition((*Channel).recvMethod)
}
panic("unexpected frame type")
}
func (ch *Channel) recvHeader(f frame) error {
switch frame := f.(type) {
case *methodFrame:
// interrupt content and handle method
return ch.recvMethod(f)
case *headerFrame:
// start collecting if we expect body frames
ch.header = frame
if frame.Size == 0 {
ch.message.setContent(ch.header.Properties, ch.body)
ch.dispatch(ch.message) // termination state
return ch.transition((*Channel).recvMethod)
}
return ch.transition((*Channel).recvContent)
case *bodyFrame:
// drop and reset
return ch.transition((*Channel).recvMethod)
}
panic("unexpected frame type")
}
// state after method + header and before the length
// defined by the header has been reached
func (ch *Channel) recvContent(f frame) error {
switch frame := f.(type) {
case *methodFrame:
// interrupt content and handle method
return ch.recvMethod(f)
case *headerFrame:
// drop and reset
return ch.transition((*Channel).recvMethod)
case *bodyFrame:
ch.body = append(ch.body, frame.Body...)
if uint64(len(ch.body)) >= ch.header.Size {
ch.message.setContent(ch.header.Properties, ch.body)
ch.dispatch(ch.message) // termination state
return ch.transition((*Channel).recvMethod)
}
return ch.transition((*Channel).recvContent)
}
panic("unexpected frame type")
}
/*
Close initiate a clean channel closure by sending a close message with the error
code set to '200'.
It is safe to call this method multiple times.
*/
func (ch *Channel) Close() error {
defer ch.connection.closeChannel(ch, nil)
return ch.call(
&channelClose{ReplyCode: replySuccess},
&channelCloseOk{},
)
}
/*
NotifyClose registers a listener for when the server sends a channel or
connection exception in the form of a Connection.Close or Channel.Close method.
Connection exceptions will be broadcast to all open channels and all channels
will be closed, where channel exceptions will only be broadcast to listeners to
this channel.
The chan provided will be closed when the Channel is closed and on a
graceful close, no error will be sent.
*/
func (ch *Channel) NotifyClose(c chan *Error) chan *Error {
ch.notifyM.Lock()
defer ch.notifyM.Unlock()
if ch.noNotify {
close(c)
} else {
ch.closes = append(ch.closes, c)
}
return c
}
/*
NotifyFlow registers a listener for basic.flow methods sent by the server.
When `false` is sent on one of the listener channels, all publishers should
pause until a `true` is sent.
The server may ask the producer to pause or restart the flow of Publishings
sent by on a channel. This is a simple flow-control mechanism that a server can
use to avoid overflowing its queues or otherwise finding itself receiving more
messages than it can process. Note that this method is not intended for window
control. It does not affect contents returned by basic.get-ok methods.
When a new channel is opened, it is active (flow is active). Some
applications assume that channels are inactive until started. To emulate
this behavior a client MAY open the channel, then pause it.
Publishers should respond to a flow messages as rapidly as possible and the
server may disconnect over producing channels that do not respect these
messages.
basic.flow-ok methods will always be returned to the server regardless of
the number of listeners there are.
To control the flow of deliveries from the server, use the Channel.Flow()
method instead.
Note: RabbitMQ will rather use TCP pushback on the network connection instead
of sending basic.flow. This means that if a single channel is producing too
much on the same connection, all channels using that connection will suffer,
including acknowledgments from deliveries. Use different Connections if you
desire to interleave consumers and producers in the same process to avoid your
basic.ack messages from getting rate limited with your basic.publish messages.
*/
func (ch *Channel) NotifyFlow(c chan bool) chan bool {
ch.notifyM.Lock()
defer ch.notifyM.Unlock()
if ch.noNotify {
close(c)
} else {
ch.flows = append(ch.flows, c)
}
return c
}
/*
NotifyReturn registers a listener for basic.return methods. These can be sent
from the server when a publish is undeliverable either from the mandatory or
immediate flags.
A return struct has a copy of the Publishing along with some error
information about why the publishing failed.
*/
func (ch *Channel) NotifyReturn(c chan Return) chan Return {
ch.notifyM.Lock()
defer ch.notifyM.Unlock()
if ch.noNotify {
close(c)
} else {
ch.returns = append(ch.returns, c)
}
return c
}
/*
NotifyCancel registers a listener for basic.cancel methods. These can be sent
from the server when a queue is deleted or when consuming from a mirrored queue
where the master has just failed (and was moved to another node).
The subscription tag is returned to the listener.
*/
func (ch *Channel) NotifyCancel(c chan string) chan string {
ch.notifyM.Lock()
defer ch.notifyM.Unlock()
if ch.noNotify {
close(c)
} else {
ch.cancels = append(ch.cancels, c)
}
return c
}
/*
NotifyConfirm calls NotifyPublish and starts a goroutine sending
ordered Ack and Nack DeliveryTag to the respective channels.
For strict ordering, use NotifyPublish instead.
*/
func (ch *Channel) NotifyConfirm(ack, nack chan uint64) (chan uint64, chan uint64) {
confirms := ch.NotifyPublish(make(chan Confirmation, len(ack)+len(nack)))
go func() {
for c := range confirms {
if c.Ack {
ack <- c.DeliveryTag
} else {
nack <- c.DeliveryTag
}
}
close(ack)
if nack != ack {
close(nack)
}
}()
return ack, nack
}
/*
NotifyPublish registers a listener for reliable publishing. Receives from this
chan for every publish after Channel.Confirm will be in order starting with
DeliveryTag 1.
There will be one and only one Confirmation Publishing starting with the
delivery tag of 1 and progressing sequentially until the total number of
Publishings have been seen by the server.
Acknowledgments will be received in the order of delivery from the
NotifyPublish channels even if the server acknowledges them out of order.
The listener chan will be closed when the Channel is closed.
The capacity of the chan Confirmation must be at least as large as the
number of outstanding publishings. Not having enough buffered chans will
create a deadlock if you attempt to perform other operations on the Connection
or Channel while confirms are in-flight.
It's advisable to wait for all Confirmations to arrive before calling
Channel.Close() or Connection.Close().
*/
func (ch *Channel) NotifyPublish(confirm chan Confirmation) chan Confirmation {
ch.notifyM.Lock()
defer ch.notifyM.Unlock()
if ch.noNotify {
close(confirm)
} else {
ch.confirms.Listen(confirm)
}
return confirm
}
/*
Qos controls how many messages or how many bytes the server will try to keep on
the network for consumers before receiving delivery acks. The intent of Qos is
to make sure the network buffers stay full between the server and client.
With a prefetch count greater than zero, the server will deliver that many
messages to consumers before acknowledgments are received. The server ignores
this option when consumers are started with noAck because no acknowledgments
are expected or sent.
With a prefetch size greater than zero, the server will try to keep at least
that many bytes of deliveries flushed to the network before receiving
acknowledgments from the consumers. This option is ignored when consumers are
started with noAck.
When global is true, these Qos settings apply to all existing and future
consumers on all channels on the same connection. When false, the Channel.Qos
settings will apply to all existing and future consumers on this channel.
Please see the RabbitMQ Consumer Prefetch documentation for an explanation of
how the global flag is implemented in RabbitMQ, as it differs from the
AMQP 0.9.1 specification in that global Qos settings are limited in scope to
channels, not connections (https://www.rabbitmq.com/consumer-prefetch.html).
To get round-robin behavior between consumers consuming from the same queue on
different connections, set the prefetch count to 1, and the next available
message on the server will be delivered to the next available consumer.
If your consumer work time is reasonably consistent and not much greater
than two times your network round trip time, you will see significant
throughput improvements starting with a prefetch count of 2 or slightly
greater as described by benchmarks on RabbitMQ.
http://www.rabbitmq.com/blog/2012/04/25/rabbitmq-performance-measurements-part-2/
*/
func (ch *Channel) Qos(prefetchCount, prefetchSize int, global bool) error {
return ch.call(
&basicQos{
PrefetchCount: uint16(prefetchCount),
PrefetchSize: uint32(prefetchSize),
Global: global,
},
&basicQosOk{},
)
}
/*
Cancel stops deliveries to the consumer chan established in Channel.Consume and
identified by consumer.
Only use this method to cleanly stop receiving deliveries from the server and
cleanly shut down the consumer chan identified by this tag. Using this method
and waiting for remaining messages to flush from the consumer chan will ensure
all messages received on the network will be delivered to the receiver of your
consumer chan.
Continue consuming from the chan Delivery provided by Channel.Consume until the
chan closes.
When noWait is true, do not wait for the server to acknowledge the cancel.
Only use this when you are certain there are no deliveries in flight that
require an acknowledgment, otherwise they will arrive and be dropped in the
client without an ack, and will not be redelivered to other consumers.
*/
func (ch *Channel) Cancel(consumer string, noWait bool) error {
req := &basicCancel{
ConsumerTag: consumer,
NoWait: noWait,
}
res := &basicCancelOk{}
if err := ch.call(req, res); err != nil {
return err
}
if req.wait() {
ch.consumers.cancel(res.ConsumerTag)
} else {
// Potentially could drop deliveries in flight
ch.consumers.cancel(consumer)
}
return nil
}
/*
QueueDeclare declares a queue to hold messages and deliver to consumers.
Declaring creates a queue if it doesn't already exist, or ensures that an
existing queue matches the same parameters.
Every queue declared gets a default binding to the empty exchange "" which has
the type "direct" with the routing key matching the queue's name. With this
default binding, it is possible to publish messages that route directly to
this queue by publishing to "" with the routing key of the queue name.
QueueDeclare("alerts", true, false, false, false, nil)
Publish("", "alerts", false, false, Publishing{Body: []byte("...")})
Delivery Exchange Key Queue
-----------------------------------------------
key: alerts -> "" -> alerts -> alerts
The queue name may be empty, in which case the server will generate a unique name
which will be returned in the Name field of Queue struct.
Durable and Non-Auto-Deleted queues will survive server restarts and remain
when there are no remaining consumers or bindings. Persistent publishings will
be restored in this queue on server restart. These queues are only able to be
bound to durable exchanges.
Non-Durable and Auto-Deleted queues will not be redeclared on server restart
and will be deleted by the server after a short time when the last consumer is
canceled or the last consumer's channel is closed. Queues with this lifetime
can also be deleted normally with QueueDelete. These durable queues can only
be bound to non-durable exchanges.
Non-Durable and Non-Auto-Deleted queues will remain declared as long as the
server is running regardless of how many consumers. This lifetime is useful
for temporary topologies that may have long delays between consumer activity.
These queues can only be bound to non-durable exchanges.
Durable and Auto-Deleted queues will be restored on server restart, but without
active consumers will not survive and be removed. This Lifetime is unlikely
to be useful.
Exclusive queues are only accessible by the connection that declares them and
will be deleted when the connection closes. Channels on other connections
will receive an error when attempting to declare, bind, consume, purge or
delete a queue with the same name.
When noWait is true, the queue will assume to be declared on the server. A
channel exception will arrive if the conditions are met for existing queues
or attempting to modify an existing queue from a different connection.
When the error return value is not nil, you can assume the queue could not be
declared with these parameters, and the channel will be closed.
*/
func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
if err := args.Validate(); err != nil {
return Queue{}, err
}
req := &queueDeclare{
Queue: name,
Passive: false,
Durable: durable,
AutoDelete: autoDelete,
Exclusive: exclusive,
NoWait: noWait,
Arguments: args,
}
res := &queueDeclareOk{}
if err := ch.call(req, res); err != nil {
return Queue{}, err
}
if req.wait() {
return Queue{
Name: res.Queue,
Messages: int(res.MessageCount),
Consumers: int(res.ConsumerCount),
}, nil
}
return Queue{Name: name}, nil
}
/*
QueueDeclarePassive is functionally and parametrically equivalent to
QueueDeclare, except that it sets the "passive" attribute to true. A passive
queue is assumed by RabbitMQ to already exist, and attempting to connect to a
non-existent queue will cause RabbitMQ to throw an exception. This function
can be used to test for the existence of a queue.
*/
func (ch *Channel) QueueDeclarePassive(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {
if err := args.Validate(); err != nil {
return Queue{}, err
}
req := &queueDeclare{
Queue: name,
Passive: true,
Durable: durable,
AutoDelete: autoDelete,
Exclusive: exclusive,
NoWait: noWait,
Arguments: args,
}
res := &queueDeclareOk{}
if err := ch.call(req, res); err != nil {
return Queue{}, err
}
if req.wait() {
return Queue{
Name: res.Queue,
Messages: int(res.MessageCount),
Consumers: int(res.ConsumerCount),
}, nil
}
return Queue{Name: name}, nil
}
/*
QueueInspect passively declares a queue by name to inspect the current message
count and consumer count.
Use this method to check how many messages ready for delivery reside in the queue,
how many consumers are receiving deliveries, and whether a queue by this
name already exists.
If the queue by this name exists, use Channel.QueueDeclare check if it is
declared with specific parameters.
If a queue by this name does not exist, an error will be returned and the
channel will be closed.
*/
func (ch *Channel) QueueInspect(name string) (Queue, error) {
req := &queueDeclare{
Queue: name,
Passive: true,
}
res := &queueDeclareOk{}
err := ch.call(req, res)
state := Queue{
Name: name,
Messages: int(res.MessageCount),
Consumers: int(res.ConsumerCount),
}
return state, err
}
/*
QueueBind binds an exchange to a queue so that publishings to the exchange will
be routed to the queue when the publishing routing key matches the binding
routing key.
QueueBind("pagers", "alert", "log", false, nil)
QueueBind("emails", "info", "log", false, nil)
Delivery Exchange Key Queue
-----------------------------------------------
key: alert --> log ----> alert --> pagers
key: info ---> log ----> info ---> emails
key: debug --> log (none) (dropped)
If a binding with the same key and arguments already exists between the
exchange and queue, the attempt to rebind will be ignored and the existing
binding will be retained.
In the case that multiple bindings may cause the message to be routed to the
same queue, the server will only route the publishing once. This is possible
with topic exchanges.
QueueBind("pagers", "alert", "amq.topic", false, nil)
QueueBind("emails", "info", "amq.topic", false, nil)
QueueBind("emails", "#", "amq.topic", false, nil) // match everything
Delivery Exchange Key Queue
-----------------------------------------------
key: alert --> amq.topic ----> alert --> pagers
key: info ---> amq.topic ----> # ------> emails
\---> info ---/
key: debug --> amq.topic ----> # ------> emails
It is only possible to bind a durable queue to a durable exchange regardless of
whether the queue or exchange is auto-deleted. Bindings between durable queues
and exchanges will also be restored on server restart.
If the binding could not complete, an error will be returned and the channel
will be closed.
When noWait is false and the queue could not be bound, the channel will be
closed with an error.
*/
func (ch *Channel) QueueBind(name, key, exchange string, noWait bool, args Table) error {
if err := args.Validate(); err != nil {
return err
}
return ch.call(
&queueBind{
Queue: name,
Exchange: exchange,
RoutingKey: key,
NoWait: noWait,
Arguments: args,
},
&queueBindOk{},
)
}
/*
QueueUnbind removes a binding between an exchange and queue matching the key and
arguments.
It is possible to send and empty string for the exchange name which means to
unbind the queue from the default exchange.
*/
func (ch *Channel) QueueUnbind(name, key, exchange string, args Table) error {
if err := args.Validate(); err != nil {
return err
}
return ch.call(
&queueUnbind{
Queue: name,
Exchange: exchange,
RoutingKey: key,
Arguments: args,
},
&queueUnbindOk{},
)
}
/*
QueuePurge removes all messages from the named queue which are not waiting to
be acknowledged. Messages that have been delivered but have not yet been
acknowledged will not be removed.
When successful, returns the number of messages purged.
If noWait is true, do not wait for the server response and the number of
messages purged will not be meaningful.
*/
func (ch *Channel) QueuePurge(name string, noWait bool) (int, error) {
req := &queuePurge{
Queue: name,
NoWait: noWait,
}
res := &queuePurgeOk{}
err := ch.call(req, res)
return int(res.MessageCount), err
}
/*
QueueDelete removes the queue from the server including all bindings then
purges the messages based on server configuration, returning the number of
messages purged.
When ifUnused is true, the queue will not be deleted if there are any
consumers on the queue. If there are consumers, an error will be returned and
the channel will be closed.
When ifEmpty is true, the queue will not be deleted if there are any messages
remaining on the queue. If there are messages, an error will be returned and
the channel will be closed.
When noWait is true, the queue will be deleted without waiting for a response
from the server. The purged message count will not be meaningful. If the queue
could not be deleted, a channel exception will be raised and the channel will
be closed.
*/
func (ch *Channel) QueueDelete(name string, ifUnused, ifEmpty, noWait bool) (int, error) {
req := &queueDelete{
Queue: name,
IfUnused: ifUnused,
IfEmpty: ifEmpty,
NoWait: noWait,
}
res := &queueDeleteOk{}
err := ch.call(req, res)
return int(res.MessageCount), err
}
/*
Consume immediately starts delivering queued messages.
Begin receiving on the returned chan Delivery before any other operation on the
Connection or Channel.
Continues deliveries to the returned chan Delivery until Channel.Cancel,
Connection.Close, Channel.Close, or an AMQP exception occurs. Consumers must
range over the chan to ensure all deliveries are received. Unreceived
deliveries will block all methods on the same connection.
All deliveries in AMQP must be acknowledged. It is expected of the consumer to
call Delivery.Ack after it has successfully processed the delivery. If the
consumer is cancelled or the channel or connection is closed any unacknowledged
deliveries will be requeued at the end of the same queue.
The consumer is identified by a string that is unique and scoped for all
consumers on this channel. If you wish to eventually cancel the consumer, use
the same non-empty identifier in Channel.Cancel. An empty string will cause
the library to generate a unique identity. The consumer identity will be
included in every Delivery in the ConsumerTag field
When autoAck (also known as noAck) is true, the server will acknowledge
deliveries to this consumer prior to writing the delivery to the network. When
autoAck is true, the consumer should not call Delivery.Ack. Automatically
acknowledging deliveries means that some deliveries may get lost if the
consumer is unable to process them after the server delivers them.
See http://www.rabbitmq.com/confirms.html for more details.
When exclusive is true, the server will ensure that this is the sole consumer
from this queue. When exclusive is false, the server will fairly distribute
deliveries across multiple consumers.
The noLocal flag is not supported by RabbitMQ.
It's advisable to use separate connections for
Channel.Publish and Channel.Consume so not to have TCP pushback on publishing
affect the ability to consume messages, so this parameter is here mostly for
completeness.
When noWait is true, do not wait for the server to confirm the request and
immediately begin deliveries. If it is not possible to consume, a channel
exception will be raised and the channel will be closed.
Optional arguments can be provided that have specific semantics for the queue
or server.
Inflight messages, limited by Channel.Qos will be buffered until received from
the returned chan.
When the Channel or Connection is closed, all buffered and inflight messages will
be dropped.
When the consumer tag is cancelled, all inflight messages will be delivered until
the returned chan is closed.
*/
func (ch *Channel) Consume(queue, consumer string, autoAck, exclusive, noLocal, noWait bool, args Table) (<-chan Delivery, error) {
// When we return from ch.call, there may be a delivery already for the
// consumer that hasn't been added to the consumer hash yet. Because of
// this, we never rely on the server picking a consumer tag for us.
if err := args.Validate(); err != nil {
return nil, err
}
if consumer == "" {
consumer = uniqueConsumerTag()
}
req := &basicConsume{
Queue: queue,
ConsumerTag: consumer,
NoLocal: noLocal,
NoAck: autoAck,
Exclusive: exclusive,
NoWait: noWait,
Arguments: args,
}
res := &basicConsumeOk{}
deliveries := make(chan Delivery)
ch.consumers.add(consumer, deliveries)
if err := ch.call(req, res); err != nil {
ch.consumers.cancel(consumer)
return nil, err
}
return (<-chan Delivery)(deliveries), nil
}
/*
ExchangeDeclare declares an exchange on the server. If the exchange does not
already exist, the server will create it. If the exchange exists, the server
verifies that it is of the provided type, durability and auto-delete flags.
Errors returned from this method will close the channel.
Exchange names starting with "amq." are reserved for pre-declared and
standardized exchanges. The client MAY declare an exchange starting with
"amq." if the passive option is set, or the exchange already exists. Names can
consist of a non-empty sequence of letters, digits, hyphen, underscore,
period, or colon.
Each exchange belongs to one of a set of exchange kinds/types implemented by
the server. The exchange types define the functionality of the exchange - i.e.
how messages are routed through it. Once an exchange is declared, its type
cannot be changed. The common types are "direct", "fanout", "topic" and
"headers".
Durable and Non-Auto-Deleted exchanges will survive server restarts and remain
declared when there are no remaining bindings. This is the best lifetime for
long-lived exchange configurations like stable routes and default exchanges.
Non-Durable and Auto-Deleted exchanges will be deleted when there are no
remaining bindings and not restored on server restart. This lifetime is
useful for temporary topologies that should not pollute the virtual host on
failure or after the consumers have completed.
Non-Durable and Non-Auto-deleted exchanges will remain as long as the server is
running including when there are no remaining bindings. This is useful for
temporary topologies that may have long delays between bindings.
Durable and Auto-Deleted exchanges will survive server restarts and will be
removed before and after server restarts when there are no remaining bindings.
These exchanges are useful for robust temporary topologies or when you require
binding durable queues to auto-deleted exchanges.
Note: RabbitMQ declares the default exchange types like 'amq.fanout' as
durable, so queues that bind to these pre-declared exchanges must also be
durable.
Exchanges declared as `internal` do not accept accept publishings. Internal
exchanges are useful when you wish to implement inter-exchange topologies
that should not be exposed to users of the broker.
When noWait is true, declare without waiting for a confirmation from the server.
The channel may be closed as a result of an error. Add a NotifyClose listener
to respond to any exceptions.
Optional amqp.Table of arguments that are specific to the server's implementation of
the exchange can be sent for exchange types that require extra parameters.
*/
func (ch *Channel) ExchangeDeclare(name, kind string, durable, autoDelete, internal, noWait bool, args Table) error {
if err := args.Validate(); err != nil {
return err
}
return ch.call(
&exchangeDeclare{
Exchange: name,
Type: kind,
Passive: false,
Durable: durable,
AutoDelete: autoDelete,
Internal: internal,
NoWait: noWait,
Arguments: args,
},
&exchangeDeclareOk{},
)
}
/*
ExchangeDeclarePassive is functionally and parametrically equivalent to
ExchangeDeclare, except that it sets the "passive" attribute to true. A passive
exchange is assumed by RabbitMQ to already exist, and attempting to connect to a
non-existent exchange will cause RabbitMQ to throw an exception. This function
can be used to detect the existence of an exchange.
*/
func (ch *Channel) ExchangeDeclarePassive(name, kind string, durable, autoDelete, internal, noWait bool, args Table) error {
if err := args.Validate(); err != nil {
return err
}
return ch.call(
&exchangeDeclare{
Exchange: name,
Type: kind,
Passive: true,
Durable: durable,
AutoDelete: autoDelete,
Internal: internal,
NoWait: noWait,
Arguments: args,
},
&exchangeDeclareOk{},
)
}
/*
ExchangeDelete removes the named exchange from the server. When an exchange is
deleted all queue bindings on the exchange are also deleted. If this exchange
does not exist, the channel will be closed with an error.
When ifUnused is true, the server will only delete the exchange if it has no queue
bindings. If the exchange has queue bindings the server does not delete it
but close the channel with an exception instead. Set this to true if you are
not the sole owner of the exchange.
When noWait is true, do not wait for a server confirmation that the exchange has
been deleted. Failing to delete the channel could close the channel. Add a
NotifyClose listener to respond to these channel exceptions.
*/
func (ch *Channel) ExchangeDelete(name string, ifUnused, noWait bool) error {
return ch.call(
&exchangeDelete{
Exchange: name,
IfUnused: ifUnused,
NoWait: noWait,
},
&exchangeDeleteOk{},
)
}
/*
ExchangeBind binds an exchange to another exchange to create inter-exchange
routing topologies on the server. This can decouple the private topology and
routing exchanges from exchanges intended solely for publishing endpoints.
Binding two exchanges with identical arguments will not create duplicate
bindings.
Binding one exchange to another with multiple bindings will only deliver a
message once. For example if you bind your exchange to `amq.fanout` with two
different binding keys, only a single message will be delivered to your
exchange even though multiple bindings will match.
Given a message delivered to the source exchange, the message will be forwarded
to the destination exchange when the routing key is matched.
ExchangeBind("sell", "MSFT", "trade", false, nil)
ExchangeBind("buy", "AAPL", "trade", false, nil)
Delivery Source Key Destination
example exchange exchange
-----------------------------------------------
key: AAPL --> trade ----> MSFT sell
\---> AAPL --> buy
When noWait is true, do not wait for the server to confirm the binding. If any
error occurs the channel will be closed. Add a listener to NotifyClose to
handle these errors.
Optional arguments specific to the exchanges bound can also be specified.
*/
func (ch *Channel) ExchangeBind(destination, key, source string, noWait bool, args Table) error {
if err := args.Validate(); err != nil {
return err
}
return ch.call(
&exchangeBind{
Destination: destination,
Source: source,
RoutingKey: key,
NoWait: noWait,
Arguments: args,
},
&exchangeBindOk{},
)
}
/*
ExchangeUnbind unbinds the destination exchange from the source exchange on the
server by removing the routing key between them. This is the inverse of
ExchangeBind. If the binding does not currently exist, an error will be
returned.
When noWait is true, do not wait for the server to confirm the deletion of the
binding. If any error occurs the channel will be closed. Add a listener to
NotifyClose to handle these errors.
Optional arguments that are specific to the type of exchanges bound can also be
provided. These must match the same arguments specified in ExchangeBind to
identify the binding.
*/
func (ch *Channel) ExchangeUnbind(destination, key, source string, noWait bool, args Table) error {
if err := args.Validate(); err != nil {
return err
}
return ch.call(
&exchangeUnbind{
Destination: destination,
Source: source,
RoutingKey: key,
NoWait: noWait,
Arguments: args,
},
&exchangeUnbindOk{},
)
}
/*
Publish sends a Publishing from the client to an exchange on the server.
When you want a single message to be delivered to a single queue, you can
publish to the default exchange with the routingKey of the queue name. This is
because every declared queue gets an implicit route to the default exchange.
Since publishings are asynchronous, any undeliverable message will get returned
by the server. Add a listener with Channel.NotifyReturn to handle any
undeliverable message when calling publish with either the mandatory or
immediate parameters as true.
Publishings can be undeliverable when the mandatory flag is true and no queue is
bound that matches the routing key, or when the immediate flag is true and no
consumer on the matched queue is ready to accept the delivery.
This can return an error when the channel, connection or socket is closed. The
error or lack of an error does not indicate whether the server has received this
publishing.
It is possible for publishing to not reach the broker if the underlying socket
is shut down without pending publishing packets being flushed from the kernel
buffers. The easy way of making it probable that all publishings reach the
server is to always call Connection.Close before terminating your publishing
application. The way to ensure that all publishings reach the server is to add
a listener to Channel.NotifyPublish and put the channel in confirm mode with
Channel.Confirm. Publishing delivery tags and their corresponding
confirmations start at 1. Exit when all publishings are confirmed.
When Publish does not return an error and the channel is in confirm mode, the
internal counter for DeliveryTags with the first confirmation starts at 1.
*/
func (ch *Channel) Publish(exchange, key string, mandatory, immediate bool, msg Publishing) error {
if err := msg.Headers.Validate(); err != nil {
return err
}
ch.m.Lock()
defer ch.m.Unlock()
if err := ch.send(&basicPublish{
Exchange: exchange,
RoutingKey: key,
Mandatory: mandatory,
Immediate: immediate,
Body: msg.Body,
Properties: properties{
Headers: msg.Headers,
ContentType: msg.ContentType,
ContentEncoding: msg.ContentEncoding,
DeliveryMode: msg.DeliveryMode,
Priority: msg.Priority,
CorrelationId: msg.CorrelationId,
ReplyTo: msg.ReplyTo,
Expiration: msg.Expiration,
MessageId: msg.MessageId,
Timestamp: msg.Timestamp,
Type: msg.Type,
UserId: msg.UserId,
AppId: msg.AppId,
},
}); err != nil {
return err
}
if ch.confirming {
ch.confirms.Publish()
}
return nil
}
/*
Get synchronously receives a single Delivery from the head of a queue from the
server to the client. In almost all cases, using Channel.Consume will be
preferred.
If there was a delivery waiting on the queue and that delivery was received, the
second return value will be true. If there was no delivery waiting or an error
occurred, the ok bool will be false.
All deliveries must be acknowledged including those from Channel.Get. Call
Delivery.Ack on the returned delivery when you have fully processed this
delivery.
When autoAck is true, the server will automatically acknowledge this message so
you don't have to. But if you are unable to fully process this message before
the channel or connection is closed, the message will not get requeued.
*/
func (ch *Channel) Get(queue string, autoAck bool) (msg Delivery, ok bool, err error) {
req := &basicGet{Queue: queue, NoAck: autoAck}
res := &basicGetOk{}
empty := &basicGetEmpty{}
if err := ch.call(req, res, empty); err != nil {
return Delivery{}, false, err
}
if res.DeliveryTag > 0 {
return *(newDelivery(ch, res)), true, nil
}
return Delivery{}, false, nil
}
/*
Tx puts the channel into transaction mode on the server. All publishings and
acknowledgments following this method will be atomically committed or rolled
back for a single queue. Call either Channel.TxCommit or Channel.TxRollback to
leave a this transaction and immediately start a new transaction.
The atomicity across multiple queues is not defined as queue declarations and
bindings are not included in the transaction.
The behavior of publishings that are delivered as mandatory or immediate while
the channel is in a transaction is not defined.
Once a channel has been put into transaction mode, it cannot be taken out of
transaction mode. Use a different channel for non-transactional semantics.
*/
func (ch *Channel) Tx() error {
return ch.call(
&txSelect{},
&txSelectOk{},
)
}
/*
TxCommit atomically commits all publishings and acknowledgments for a single
queue and immediately start a new transaction.
Calling this method without having called Channel.Tx is an error.
*/
func (ch *Channel) TxCommit() error {
return ch.call(
&txCommit{},
&txCommitOk{},
)
}
/*
TxRollback atomically rolls back all publishings and acknowledgments for a
single queue and immediately start a new transaction.
Calling this method without having called Channel.Tx is an error.
*/
func (ch *Channel) TxRollback() error {
return ch.call(
&txRollback{},
&txRollbackOk{},
)
}
/*
Flow pauses the delivery of messages to consumers on this channel. Channels
are opened with flow control active, to open a channel with paused
deliveries immediately call this method with `false` after calling
Connection.Channel.
When active is `false`, this method asks the server to temporarily pause deliveries
until called again with active as `true`.
Channel.Get methods will not be affected by flow control.
This method is not intended to act as window control. Use Channel.Qos to limit
the number of unacknowledged messages or bytes in flight instead.
The server may also send us flow methods to throttle our publishings. A well
behaving publishing client should add a listener with Channel.NotifyFlow and
pause its publishings when `false` is sent on that channel.
Note: RabbitMQ prefers to use TCP push back to control flow for all channels on
a connection, so under high volume scenarios, it's wise to open separate
Connections for publishings and deliveries.
*/
func (ch *Channel) Flow(active bool) error {
return ch.call(
&channelFlow{Active: active},
&channelFlowOk{},
)
}
/*
Confirm puts this channel into confirm mode so that the client can ensure all
publishings have successfully been received by the server. After entering this
mode, the server will send a basic.ack or basic.nack message with the deliver
tag set to a 1 based incremental index corresponding to every publishing
received after the this method returns.
Add a listener to Channel.NotifyPublish to respond to the Confirmations. If
Channel.NotifyPublish is not called, the Confirmations will be silently
ignored.
The order of acknowledgments is not bound to the order of deliveries.
Ack and Nack confirmations will arrive at some point in the future.
Unroutable mandatory or immediate messages are acknowledged immediately after
any Channel.NotifyReturn listeners have been notified. Other messages are
acknowledged when all queues that should have the message routed to them have
either received acknowledgment of delivery or have enqueued the message,
persisting the message if necessary.
When noWait is true, the client will not wait for a response. A channel
exception could occur if the server does not support this method.
*/
func (ch *Channel) Confirm(noWait bool) error {
if err := ch.call(
&confirmSelect{Nowait: noWait},
&confirmSelectOk{},
); err != nil {
return err
}
ch.confirmM.Lock()
ch.confirming = true
ch.confirmM.Unlock()
return nil
}
/*
Recover redelivers all unacknowledged deliveries on this channel.
When requeue is false, messages will be redelivered to the original consumer.
When requeue is true, messages will be redelivered to any available consumer,
potentially including the original.
If the deliveries cannot be recovered, an error will be returned and the channel
will be closed.
Note: this method is not implemented on RabbitMQ, use Delivery.Nack instead
*/
func (ch *Channel) Recover(requeue bool) error {
return ch.call(
&basicRecover{Requeue: requeue},
&basicRecoverOk{},
)
}
/*
Ack acknowledges a delivery by its delivery tag when having been consumed with
Channel.Consume or Channel.Get.
Ack acknowledges all message received prior to the delivery tag when multiple
is true.
See also Delivery.Ack
*/
func (ch *Channel) Ack(tag uint64, multiple bool) error {
ch.m.Lock()
defer ch.m.Unlock()
return ch.send(&basicAck{
DeliveryTag: tag,
Multiple: multiple,
})
}
/*
Nack negatively acknowledges a delivery by its delivery tag. Prefer this
method to notify the server that you were not able to process this delivery and
it must be redelivered or dropped.
See also Delivery.Nack
*/
func (ch *Channel) Nack(tag uint64, multiple bool, requeue bool) error {
ch.m.Lock()
defer ch.m.Unlock()
return ch.send(&basicNack{
DeliveryTag: tag,
Multiple: multiple,
Requeue: requeue,
})
}
/*
Reject negatively acknowledges a delivery by its delivery tag. Prefer Nack
over Reject when communicating with a RabbitMQ server because you can Nack
multiple messages, reducing the amount of protocol messages to exchange.
See also Delivery.Reject
*/
func (ch *Channel) Reject(tag uint64, requeue bool) error {
ch.m.Lock()
defer ch.m.Unlock()
return ch.send(&basicReject{
DeliveryTag: tag,
Requeue: requeue,
})
}
|