├── vendor ├── github.com │ └── streadway │ │ └── amqp │ │ ├── gen.sh │ │ ├── fuzz.go │ │ ├── pre-commit │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── auth.go │ │ ├── confirms.go │ │ ├── return.go │ │ ├── allocator.go │ │ ├── consumers.go │ │ ├── certs.sh │ │ ├── README.md │ │ ├── uri.go │ │ ├── doc.go │ │ ├── delivery.go │ │ ├── write.go │ │ ├── read.go │ │ ├── types.go │ │ ├── connection.go │ │ └── channel.go └── vendor.json ├── cmd ├── docker │ ├── haproxy │ │ ├── run.sh │ │ └── haproxy.cfg │ └── rabbitmq │ │ └── cluster.sh ├── consumer │ └── main.go └── producer │ └── main.go ├── Makefile ├── mq-docker.sh ├── .gitignore ├── README.md ├── g └── g.go └── mq ├── consumer_test.go ├── producer_test.go ├── common.go ├── mq.go ├── consumer.go └── producer.go /vendor/github.com/streadway/amqp/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | go run spec/gen.go < spec/amqp0-9-1.stripped.extended.xml | gofmt > spec091.go 3 | -------------------------------------------------------------------------------- /cmd/docker/haproxy/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker pull haproxy 4 | 5 | docker run -d --network rabbitmq-net --name rabbitmq-haproxy -p 5672:5672 -v ${PWD}:/usr/local/etc/haproxy:ro haproxy 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: producer consumer 2 | 3 | producer: 4 | go fmt ./cmd/producer 5 | go build ./cmd/producer 6 | 7 | consumer: 8 | go fmt ./cmd/consumer 9 | go build ./cmd/consumer 10 | 11 | clean: 12 | rm -rf ./producer 13 | rm -rf ./consumer 14 | -------------------------------------------------------------------------------- /mq-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker pull rabbitmq:3.7.4-management 4 | 5 | docker run -d --hostname zwf --name rabbitmq -p 8088:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_USER=zwf -e RABBITMQ_DEFAULT_PASS=123456 rabbitmq:3.7.4-management 6 | 7 | docker ps -a |grep rabbitmq 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package amqp 4 | 5 | import "bytes" 6 | 7 | func Fuzz(data []byte) int { 8 | r := reader{bytes.NewReader(data)} 9 | frame, err := r.ReadFrame() 10 | if err != nil { 11 | if frame != nil { 12 | panic("frame is not nil") 13 | } 14 | return 0 15 | } 16 | return 1 17 | } 18 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "m3Cak3i0uCgcy3ynUcZ6y/b+RNM=", 7 | "path": "github.com/streadway/amqp", 8 | "revision": "d27ae102b8892a98ca4edb74539436af62577379", 9 | "revisionTime": "2018-03-07T22:37:21Z" 10 | } 11 | ], 12 | "rootPath": "github.com/Hurricanezwf/rabbitmq-go" 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 简介 2 | 基于golang的[amqp](https://godoc.org/github.com/streadway/amqp)库封装的可快速使用的rabbitmq的生产者和消费者. 3 | * 支持自动断线重连 4 | * 简化了使用amqp库时的各种设置, 详细使用方法参照cmd/producer & cmd/consumer的demo实现 5 | * **最好先看下封装的源码判断是否符合自己的场景要求,而后决定是否使用** 6 | * 如果决定使用, 你可能需要修改一下mq包中的log库的指向. 7 | 8 | 9 | ### 编译 10 | * 推荐Go1.9+ 11 | * 直接make 12 | 13 | ### 如何嵌入到自己的代码中? 14 | * import "github.com/Hurricanezwf/rabbitmq-go/mq" 15 | * 参照cmd中的生产者和消费者demo使用 16 | -------------------------------------------------------------------------------- /g/g.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hurricanezwf. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package g 16 | 17 | var MQURL = "amqp://zwf:123456@192.168.2.101:5672/" 18 | 19 | //var MQURL = "amqp://zwf:zwf@localhost:5672/" 20 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | GOFMT_FILES=$(gofmt -l .) 4 | if [ -n "${GOFMT_FILES}" ]; then 5 | printf >&2 'gofmt failed for the following files:\n%s\n\nplease run "gofmt -w ." on your changes before committing.\n' "${GOFMT_FILES}" 6 | exit 1 7 | fi 8 | 9 | GOLINT_ERRORS=$(golint ./... | grep -v "Id should be") 10 | if [ -n "${GOLINT_ERRORS}" ]; then 11 | printf >&2 'golint failed for the following reasons:\n%s\n\nplease run 'golint ./...' on your changes before committing.\n' "${GOLINT_ERRORS}" 12 | exit 1 13 | fi 14 | 15 | GOVET_ERRORS=$(go tool vet *.go 2>&1) 16 | if [ -n "${GOVET_ERRORS}" ]; then 17 | printf >&2 'go vet failed for the following reasons:\n%s\n\nplease run "go tool vet *.go" on your changes before committing.\n' "${GOVET_ERRORS}" 18 | exit 1 19 | fi 20 | 21 | if [ -z "${NOTEST}" ]; then 22 | printf >&2 'Running short tests...\n' 23 | env AMQP_URL= go test -short -v | egrep 'PASS|ok' 24 | 25 | if [ $? -ne 0 ]; then 26 | printf >&2 'go test failed, please fix before committing.\n' 27 | exit 1 28 | fi 29 | fi 30 | -------------------------------------------------------------------------------- /cmd/docker/haproxy/haproxy.cfg: -------------------------------------------------------------------------------- 1 | global #全局属性 2 | maxconn 256 #最大同时256连接 3 | user root 4 | group root 5 | pidfile /var/run/haproxy.pid #指定保存HAProxy进程号的文件 6 | log 127.0.0.1 local0 info 7 | log 127.0.0.1 local1 warning 8 | 9 | 10 | defaults #默认参数 11 | log global 12 | mode http 13 | option httplog 14 | option dontlognull 15 | option tcplog 16 | timeout connect 5000ms #连接server端超时5s 17 | timeout client 10000ms #客户端响应超时10s 18 | timeout server 10000ms #server端响应超时10s 19 | 20 | 21 | # rabbitmq服务TCP代理 22 | listen rabbitmq 23 | bind *:5678 24 | mode tcp # 4层代理 25 | balance roundrobin 26 | server rabbitmq1 rabbitmq1:5672 weight 1 maxconn 60000 check inter 3s 27 | server rabbitmq2 rabbitmq2:5672 weight 1 maxconn 60000 check inter 3s 28 | server rabbitmq3 rabbitmq3:5672 weight 1 maxconn 60000 check inter 3s 29 | 30 | 31 | # rabbitmq-management服务HTTP代理 32 | frontend rabbitmq-management 33 | bind *:15678 34 | mode http 35 | default_backend rabbitmq-management 36 | 37 | 38 | # rabbitmq-management backend 39 | backend rabbitmq-management 40 | balance roundrobin 41 | option forwardfor 42 | server rabbitmq1-management rabbitmq1:15672 check 43 | server rabbitmq2-management rabbitmq2:15672 check 44 | server rabbitmq3-management rabbitmq3:15672 check 45 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Prequisites 2 | 3 | 1. Go: [https://golang.org/dl/](https://golang.org/dl/) 4 | 1. Golint `go get -u -v github.com/golang/lint/golint` 5 | 6 | ## Contributing 7 | 8 | The workflow is pretty standard: 9 | 10 | 1. Fork github.com/streadway/amqp 11 | 1. Add the pre-commit hook: `ln -s ../../pre-commit .git/hooks/pre-commit` 12 | 1. Create your feature branch (`git checkout -b my-new-feature`) 13 | 1. Run integration tests (see below) 14 | 1. **Implement tests** 15 | 1. Implement fixs 16 | 1. Commit your changes (`git commit -am 'Add some feature'`) 17 | 1. Push to a branch (`git push -u origin my-new-feature`) 18 | 1. Submit a pull request 19 | 20 | ## Running Tests 21 | 22 | The test suite assumes that: 23 | 24 | * A RabbitMQ node is running on localhost with all defaults: [https://www.rabbitmq.com/download.html](https://www.rabbitmq.com/download.html) 25 | * `AMQP_URL` is exported to `amqp://guest:guest@127.0.0.1:5672/` 26 | 27 | ### Integration Tests 28 | 29 | After starting a local RabbitMQ, run integration tests with the following: 30 | 31 | env AMQP_URL=amqp://guest:guest@127.0.0.1:5672/ go test -v -cpu 2 -tags integration -race 32 | 33 | All integration tests should use the `integrationConnection(...)` test 34 | helpers defined in `integration_test.go` to setup the integration environment 35 | and logging. 36 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/auth.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "fmt" 10 | ) 11 | 12 | // Authentication interface provides a means for different SASL authentication 13 | // mechanisms to be used during connection tuning. 14 | type Authentication interface { 15 | Mechanism() string 16 | Response() string 17 | } 18 | 19 | // PlainAuth is a similar to Basic Auth in HTTP. 20 | type PlainAuth struct { 21 | Username string 22 | Password string 23 | } 24 | 25 | // Mechanism returns "PLAIN" 26 | func (auth *PlainAuth) Mechanism() string { 27 | return "PLAIN" 28 | } 29 | 30 | // Response returns the null character delimited encoding for the SASL PLAIN Mechanism. 31 | func (auth *PlainAuth) Response() string { 32 | return fmt.Sprintf("\000%s\000%s", auth.Username, auth.Password) 33 | } 34 | 35 | // Finds the first mechanism preferred by the client that the server supports. 36 | func pickSASLMechanism(client []Authentication, serverMechanisms []string) (auth Authentication, ok bool) { 37 | for _, auth = range client { 38 | for _, mech := range serverMechanisms { 39 | if auth.Mechanism() == mech { 40 | return auth, true 41 | } 42 | } 43 | } 44 | 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /cmd/docker/rabbitmq/cluster.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 基础配置 4 | DefaultUser="zwf" 5 | DefaultPassword="zwf" 6 | ImageName="rabbitmq:3.7.4-management" 7 | 8 | 9 | docker pull ${ImageName} 10 | 11 | # 创建集群网络(不然不同结点不能互通) 12 | docker network create rabbitmq-net 13 | 14 | 15 | 16 | # 创建并启动容器 17 | docker run -d \ 18 | --name=rabbitmq1 \ 19 | -p 5673:5672 \ 20 | -p 15673:15672 \ 21 | -e RABBITMQ_NODENAME=rabbitmq1 \ 22 | -e RABBITMQ_ERLANG_COOKIE='zhouwenfeng' \ 23 | -e RABBITMQ_DEFAULT_USER=${DefaultUser} \ 24 | -e RABBITMQ_DEFAULT_PASS=${DefaultPassword} \ 25 | -h rabbitmq1 \ 26 | --net=rabbitmq-net \ 27 | ${ImageName} 28 | 29 | 30 | docker run -d \ 31 | --name=rabbitmq2 \ 32 | -p 5674:5672 \ 33 | -p 15674:15672 \ 34 | -e RABBITMQ_NODENAME=rabbitmq2 \ 35 | -e RABBITMQ_ERLANG_COOKIE='zhouwenfeng' \ 36 | -e RABBITMQ_DEFAULT_USER=${DefaultUser} \ 37 | -e RABBITMQ_DEFAULT_PASS=${DefaultPassword} \ 38 | -h rabbitmq2 \ 39 | --net=rabbitmq-net \ 40 | ${ImageName} 41 | 42 | 43 | docker run -d \ 44 | --name=rabbitmq3 \ 45 | -p 5675:5672 \ 46 | -p 15675:15672 \ 47 | -e RABBITMQ_NODENAME=rabbitmq3 \ 48 | -e RABBITMQ_ERLANG_COOKIE='zhouwenfeng' \ 49 | -e RABBITMQ_DEFAULT_USER=${DefaultUser} \ 50 | -e RABBITMQ_DEFAULT_PASS=${DefaultPassword} \ 51 | -h rabbitmq3 \ 52 | --net=rabbitmq-net \ 53 | ${ImageName} 54 | 55 | 56 | 57 | 58 | # 将结点2和3加入集群 59 | docker exec rabbitmq2 bash -c \ 60 | "rabbitmqctl stop_app && \ 61 | rabbitmqctl reset && \ 62 | rabbitmqctl join_cluster rabbitmq1@rabbitmq1 && \ 63 | rabbitmqctl start_app" 64 | 65 | docker exec rabbitmq3 bash -c \ 66 | "rabbitmqctl stop_app && \ 67 | rabbitmqctl reset && \ 68 | rabbitmqctl join_cluster rabbitmq1@rabbitmq1 && \ 69 | rabbitmqctl start_app" 70 | 71 | 72 | # 查看集群情况 73 | docker exec -it rabbitmq1 bash -c "rabbitmqctl cluster_status" 74 | 75 | 76 | 77 | # 通过正则表达式设置指定队列为镜像队列 78 | docker exec -it rabbitmq1 rabbitmqctl \ 79 | set_policy ha-all 'queue.+' '{"ha-mode":"all"}' 80 | -------------------------------------------------------------------------------- /mq/consumer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hurricanezwf. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mq 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | "time" 21 | ) 22 | 23 | func TestConsumer(t *testing.T) { 24 | m, err := New(mqUrl).Open() 25 | if err != nil { 26 | panic(err.Error()) 27 | } 28 | defer m.Close() 29 | 30 | c, err := m.Consumer("test-consume") 31 | if err != nil { 32 | panic(fmt.Sprintf("Create consumer failed, %v", err)) 33 | } 34 | defer c.Close() 35 | 36 | exb := []*ExchangeBinds{ 37 | &ExchangeBinds{ 38 | Exch: DefaultExchange("exch.unitest", ExchangeDirect), 39 | Bindings: []*Binding{ 40 | &Binding{ 41 | RouteKey: "route.unitest1", 42 | Queues: []*Queue{ 43 | DefaultQueue("queue.unitest1"), 44 | }, 45 | }, 46 | &Binding{ 47 | RouteKey: "route.unitest2", 48 | Queues: []*Queue{ 49 | DefaultQueue("queue.unitest2"), 50 | }, 51 | }, 52 | }, 53 | }, 54 | } 55 | 56 | msgC := make(chan *Delivery, 1) 57 | defer close(msgC) 58 | 59 | if err = c.SetExchangeBinds(exb).SetMsgCallback(msgC).Open(); err != nil { 60 | panic(fmt.Sprintf("Open failed, %v", err)) 61 | } 62 | 63 | i := 0 64 | for msg := range msgC { 65 | i++ 66 | if i%5 == 0 { 67 | c.CloseChan() 68 | } 69 | t.Logf("Consumer receive msg `%s`\n", string(msg.Body)) 70 | time.Sleep(time.Second) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /mq/producer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hurricanezwf. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mq 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | "time" 21 | ) 22 | 23 | const mqUrl = "amqp://zwf:123456@localhost:5672/" 24 | 25 | func TestProducer(t *testing.T) { 26 | m, err := New(mqUrl).Open() 27 | if err != nil { 28 | panic(err.Error()) 29 | } 30 | 31 | p, err := m.Producer("test-producer") 32 | if err != nil { 33 | panic(fmt.Sprintf("Create producer failed, %v", err)) 34 | } 35 | 36 | exb := []*ExchangeBinds{ 37 | &ExchangeBinds{ 38 | Exch: DefaultExchange("exch.unitest", ExchangeDirect), 39 | Bindings: []*Binding{ 40 | &Binding{ 41 | RouteKey: "route.unitest1", 42 | Queues: []*Queue{ 43 | DefaultQueue("queue.unitest1"), 44 | }, 45 | }, 46 | &Binding{ 47 | RouteKey: "route.unitest2", 48 | Queues: []*Queue{ 49 | DefaultQueue("queue.unitest2"), 50 | }, 51 | }, 52 | }, 53 | }, 54 | } 55 | 56 | if err = p.SetExchangeBinds(exb).Confirm(true).Open(); err != nil { 57 | panic(fmt.Sprintf("Open failed, %v", err)) 58 | } 59 | 60 | for i := 0; i < 1000; i++ { 61 | if i > 0 && i%3 == 0 { 62 | p.CloseChan() 63 | } 64 | err = p.Publish("exch.unitest", "route.unitest2", NewPublishMsg([]byte(`{"name":"zwf"}`))) 65 | t.Logf("Produce state:%d, err:%v\n", p.State(), err) 66 | time.Sleep(time.Second) 67 | } 68 | 69 | p.Close() 70 | m.Close() 71 | } 72 | -------------------------------------------------------------------------------- /cmd/consumer/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hurricanezwf. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "log" 19 | "time" 20 | 21 | "github.com/Hurricanezwf/rabbitmq-go/g" 22 | "github.com/Hurricanezwf/rabbitmq-go/mq" 23 | ) 24 | 25 | var ( 26 | MQURL = g.MQURL 27 | ) 28 | 29 | func main() { 30 | m, err := mq.New(MQURL).Open() 31 | if err != nil { 32 | log.Printf("[ERROR] %s\n", err.Error()) 33 | return 34 | } 35 | defer m.Close() 36 | 37 | c, err := m.Consumer("test-consume") 38 | if err != nil { 39 | log.Printf("[ERROR] Create consumer failed, %v\n", err) 40 | return 41 | } 42 | defer c.Close() 43 | 44 | exb := []*mq.ExchangeBinds{ 45 | &mq.ExchangeBinds{ 46 | Exch: mq.DefaultExchange("exch.unitest", mq.ExchangeDirect), 47 | Bindings: []*mq.Binding{ 48 | &mq.Binding{ 49 | RouteKey: "route.unitest1", 50 | Queues: []*mq.Queue{ 51 | mq.DefaultQueue("queue.unitest1"), 52 | }, 53 | }, 54 | &mq.Binding{ 55 | RouteKey: "route.unitest2", 56 | Queues: []*mq.Queue{ 57 | mq.DefaultQueue("queue.unitest2"), 58 | }, 59 | }, 60 | }, 61 | }, 62 | } 63 | 64 | msgC := make(chan mq.Delivery, 1) 65 | defer close(msgC) 66 | 67 | c.SetExchangeBinds(exb) 68 | c.SetMsgCallback(msgC) 69 | c.SetQos(10) 70 | if err = c.Open(); err != nil { 71 | log.Printf("[ERROR] Open failed, %v\n", err) 72 | return 73 | } 74 | 75 | for msg := range msgC { 76 | log.Printf("Tag(%d) Body: %s\n", msg.DeliveryTag, string(msg.Body)) 77 | msg.Ack(true) 78 | //if i%5 == 0 { 79 | // c.CloseChan() 80 | //} 81 | //log.Info("Consumer receive msg `%s`", string(msg)) 82 | time.Sleep(time.Second) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/confirms.go: -------------------------------------------------------------------------------- 1 | package amqp 2 | 3 | import "sync" 4 | 5 | // confirms resequences and notifies one or multiple publisher confirmation listeners 6 | type confirms struct { 7 | m sync.Mutex 8 | listeners []chan Confirmation 9 | sequencer map[uint64]Confirmation 10 | published uint64 11 | expecting uint64 12 | } 13 | 14 | // newConfirms allocates a confirms 15 | func newConfirms() *confirms { 16 | return &confirms{ 17 | sequencer: map[uint64]Confirmation{}, 18 | published: 0, 19 | expecting: 1, 20 | } 21 | } 22 | 23 | func (c *confirms) Listen(l chan Confirmation) { 24 | c.m.Lock() 25 | defer c.m.Unlock() 26 | 27 | c.listeners = append(c.listeners, l) 28 | } 29 | 30 | // publish increments the publishing counter 31 | func (c *confirms) Publish() uint64 { 32 | c.m.Lock() 33 | defer c.m.Unlock() 34 | 35 | c.published++ 36 | return c.published 37 | } 38 | 39 | // confirm confirms one publishing, increments the expecting delivery tag, and 40 | // removes bookkeeping for that delivery tag. 41 | func (c *confirms) confirm(confirmation Confirmation) { 42 | delete(c.sequencer, c.expecting) 43 | c.expecting++ 44 | for _, l := range c.listeners { 45 | l <- confirmation 46 | } 47 | } 48 | 49 | // resequence confirms any out of order delivered confirmations 50 | func (c *confirms) resequence() { 51 | for c.expecting <= c.published { 52 | sequenced, found := c.sequencer[c.expecting] 53 | if !found { 54 | return 55 | } 56 | c.confirm(sequenced) 57 | } 58 | } 59 | 60 | // one confirms one publishing and all following in the publishing sequence 61 | func (c *confirms) One(confirmed Confirmation) { 62 | c.m.Lock() 63 | defer c.m.Unlock() 64 | 65 | if c.expecting == confirmed.DeliveryTag { 66 | c.confirm(confirmed) 67 | } else { 68 | c.sequencer[confirmed.DeliveryTag] = confirmed 69 | } 70 | c.resequence() 71 | } 72 | 73 | // multiple confirms all publishings up until the delivery tag 74 | func (c *confirms) Multiple(confirmed Confirmation) { 75 | c.m.Lock() 76 | defer c.m.Unlock() 77 | 78 | for c.expecting <= confirmed.DeliveryTag { 79 | c.confirm(Confirmation{c.expecting, confirmed.Ack}) 80 | } 81 | c.resequence() 82 | } 83 | 84 | // Close closes all listeners, discarding any out of sequence confirmations 85 | func (c *confirms) Close() error { 86 | c.m.Lock() 87 | defer c.m.Unlock() 88 | 89 | for _, l := range c.listeners { 90 | close(l) 91 | } 92 | c.listeners = nil 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/return.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "time" 10 | ) 11 | 12 | // Return captures a flattened struct of fields returned by the server when a 13 | // Publishing is unable to be delivered either due to the `mandatory` flag set 14 | // and no route found, or `immediate` flag set and no free consumer. 15 | type Return struct { 16 | ReplyCode uint16 // reason 17 | ReplyText string // description 18 | Exchange string // basic.publish exchange 19 | RoutingKey string // basic.publish routing key 20 | 21 | // Properties 22 | ContentType string // MIME content type 23 | ContentEncoding string // MIME content encoding 24 | Headers Table // Application or header exchange table 25 | DeliveryMode uint8 // queue implementation use - non-persistent (1) or persistent (2) 26 | Priority uint8 // queue implementation use - 0 to 9 27 | CorrelationId string // application use - correlation identifier 28 | ReplyTo string // application use - address to to reply to (ex: RPC) 29 | Expiration string // implementation use - message expiration spec 30 | MessageId string // application use - message identifier 31 | Timestamp time.Time // application use - message timestamp 32 | Type string // application use - message type name 33 | UserId string // application use - creating user id 34 | AppId string // application use - creating application 35 | 36 | Body []byte 37 | } 38 | 39 | func newReturn(msg basicReturn) *Return { 40 | props, body := msg.getContent() 41 | 42 | return &Return{ 43 | ReplyCode: msg.ReplyCode, 44 | ReplyText: msg.ReplyText, 45 | Exchange: msg.Exchange, 46 | RoutingKey: msg.RoutingKey, 47 | 48 | Headers: props.Headers, 49 | ContentType: props.ContentType, 50 | ContentEncoding: props.ContentEncoding, 51 | DeliveryMode: props.DeliveryMode, 52 | Priority: props.Priority, 53 | CorrelationId: props.CorrelationId, 54 | ReplyTo: props.ReplyTo, 55 | Expiration: props.Expiration, 56 | MessageId: props.MessageId, 57 | Timestamp: props.Timestamp, 58 | Type: props.Type, 59 | UserId: props.UserId, 60 | AppId: props.AppId, 61 | 62 | Body: body, 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/allocator.go: -------------------------------------------------------------------------------- 1 | package amqp 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math/big" 7 | ) 8 | 9 | const ( 10 | free = 0 11 | allocated = 1 12 | ) 13 | 14 | // allocator maintains a bitset of allocated numbers. 15 | type allocator struct { 16 | pool *big.Int 17 | last int 18 | low int 19 | high int 20 | } 21 | 22 | // NewAllocator reserves and frees integers out of a range between low and 23 | // high. 24 | // 25 | // O(N) worst case space used, where N is maximum allocated, divided by 26 | // sizeof(big.Word) 27 | func newAllocator(low, high int) *allocator { 28 | return &allocator{ 29 | pool: big.NewInt(0), 30 | last: low, 31 | low: low, 32 | high: high, 33 | } 34 | } 35 | 36 | // String returns a string describing the contents of the allocator like 37 | // "allocator[low..high] reserved..until" 38 | // 39 | // O(N) where N is high-low 40 | func (a allocator) String() string { 41 | b := &bytes.Buffer{} 42 | fmt.Fprintf(b, "allocator[%d..%d]", a.low, a.high) 43 | 44 | for low := a.low; low <= a.high; low++ { 45 | high := low 46 | for a.reserved(high) && high <= a.high { 47 | high++ 48 | } 49 | 50 | if high > low+1 { 51 | fmt.Fprintf(b, " %d..%d", low, high-1) 52 | } else if high > low { 53 | fmt.Fprintf(b, " %d", high-1) 54 | } 55 | 56 | low = high 57 | } 58 | return b.String() 59 | } 60 | 61 | // Next reserves and returns the next available number out of the range between 62 | // low and high. If no number is available, false is returned. 63 | // 64 | // O(N) worst case runtime where N is allocated, but usually O(1) due to a 65 | // rolling index into the oldest allocation. 66 | func (a *allocator) next() (int, bool) { 67 | wrapped := a.last 68 | 69 | // Find trailing bit 70 | for ; a.last <= a.high; a.last++ { 71 | if a.reserve(a.last) { 72 | return a.last, true 73 | } 74 | } 75 | 76 | // Find preceding free'd pool 77 | a.last = a.low 78 | 79 | for ; a.last < wrapped; a.last++ { 80 | if a.reserve(a.last) { 81 | return a.last, true 82 | } 83 | } 84 | 85 | return 0, false 86 | } 87 | 88 | // reserve claims the bit if it is not already claimed, returning true if 89 | // successfully claimed. 90 | func (a *allocator) reserve(n int) bool { 91 | if a.reserved(n) { 92 | return false 93 | } 94 | a.pool.SetBit(a.pool, n-a.low, allocated) 95 | return true 96 | } 97 | 98 | // reserved returns true if the integer has been allocated 99 | func (a *allocator) reserved(n int) bool { 100 | return a.pool.Bit(n-a.low) == allocated 101 | } 102 | 103 | // release frees the use of the number for another allocation 104 | func (a *allocator) release(n int) { 105 | a.pool.SetBit(a.pool, n-a.low, free) 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/consumers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | "sync" 12 | "sync/atomic" 13 | ) 14 | 15 | var consumerSeq uint64 16 | 17 | func uniqueConsumerTag() string { 18 | return fmt.Sprintf("ctag-%s-%d", os.Args[0], atomic.AddUint64(&consumerSeq, 1)) 19 | } 20 | 21 | type consumerBuffers map[string]chan *Delivery 22 | 23 | // Concurrent type that manages the consumerTag -> 24 | // ingress consumerBuffer mapping 25 | type consumers struct { 26 | sync.WaitGroup // one for buffer 27 | closed chan struct{} // signal buffer 28 | 29 | sync.Mutex // protects below 30 | chans consumerBuffers 31 | } 32 | 33 | func makeConsumers() *consumers { 34 | return &consumers{ 35 | closed: make(chan struct{}), 36 | chans: make(consumerBuffers), 37 | } 38 | } 39 | 40 | func (subs *consumers) buffer(in chan *Delivery, out chan Delivery) { 41 | defer close(out) 42 | defer subs.Done() 43 | 44 | var inflight = in 45 | var queue []*Delivery 46 | 47 | for delivery := range in { 48 | queue = append(queue, delivery) 49 | 50 | for len(queue) > 0 { 51 | select { 52 | case <-subs.closed: 53 | // closed before drained, drop in-flight 54 | return 55 | 56 | case delivery, consuming := <-inflight: 57 | if consuming { 58 | queue = append(queue, delivery) 59 | } else { 60 | inflight = nil 61 | } 62 | 63 | case out <- *queue[0]: 64 | queue = queue[1:] 65 | } 66 | } 67 | } 68 | } 69 | 70 | // On key conflict, close the previous channel. 71 | func (subs *consumers) add(tag string, consumer chan Delivery) { 72 | subs.Lock() 73 | defer subs.Unlock() 74 | 75 | if prev, found := subs.chans[tag]; found { 76 | close(prev) 77 | } 78 | 79 | in := make(chan *Delivery) 80 | subs.chans[tag] = in 81 | 82 | subs.Add(1) 83 | go subs.buffer(in, consumer) 84 | } 85 | 86 | func (subs *consumers) cancel(tag string) (found bool) { 87 | subs.Lock() 88 | defer subs.Unlock() 89 | 90 | ch, found := subs.chans[tag] 91 | 92 | if found { 93 | delete(subs.chans, tag) 94 | close(ch) 95 | } 96 | 97 | return found 98 | } 99 | 100 | func (subs *consumers) close() { 101 | subs.Lock() 102 | defer subs.Unlock() 103 | 104 | close(subs.closed) 105 | 106 | for tag, ch := range subs.chans { 107 | delete(subs.chans, tag) 108 | close(ch) 109 | } 110 | 111 | subs.Wait() 112 | } 113 | 114 | // Sends a delivery to a the consumer identified by `tag`. 115 | // If unbuffered channels are used for Consume this method 116 | // could block all deliveries until the consumer 117 | // receives on the other end of the channel. 118 | func (subs *consumers) send(tag string, msg *Delivery) bool { 119 | subs.Lock() 120 | defer subs.Unlock() 121 | 122 | buffer, found := subs.chans[tag] 123 | if found { 124 | buffer <- msg 125 | } 126 | 127 | return found 128 | } 129 | -------------------------------------------------------------------------------- /cmd/producer/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hurricanezwf. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "log" 20 | "os" 21 | "os/signal" 22 | "strconv" 23 | "syscall" 24 | "time" 25 | 26 | "net/http" 27 | _ "net/http/pprof" 28 | 29 | "github.com/Hurricanezwf/rabbitmq-go/g" 30 | "github.com/Hurricanezwf/rabbitmq-go/mq" 31 | ) 32 | 33 | var ( 34 | MQURL = g.MQURL 35 | PProfAddr = ":11111" 36 | ) 37 | 38 | func main() { 39 | m, err := mq.New(MQURL).Open() 40 | if err != nil { 41 | log.Printf("[ERROR] %s\n", err.Error()) 42 | return 43 | } 44 | 45 | exb := []*mq.ExchangeBinds{ 46 | &mq.ExchangeBinds{ 47 | Exch: mq.DefaultExchange("exch.unitest", mq.ExchangeDirect), 48 | Bindings: []*mq.Binding{ 49 | &mq.Binding{ 50 | RouteKey: "route.unitest1", 51 | Queues: []*mq.Queue{ 52 | mq.DefaultQueue("queue.unitest1"), 53 | }, 54 | }, 55 | &mq.Binding{ 56 | RouteKey: "route.unitest2", 57 | Queues: []*mq.Queue{ 58 | mq.DefaultQueue("queue.unitest2"), 59 | }, 60 | }, 61 | }, 62 | }, 63 | } 64 | 65 | // 使用不同的producer并发publish 66 | for i := 0; i < 1; i++ { 67 | go func(idx int) { 68 | p, err := m.Producer(strconv.Itoa(i)) 69 | if err != nil { 70 | log.Printf("[ERROR] Create producer failed, %v\n", err) 71 | return 72 | } 73 | if err = p.SetExchangeBinds(exb).Confirm(true).Open(); err != nil { 74 | log.Printf("[ERROR] Open failed, %v\n", err) 75 | return 76 | } 77 | 78 | // 使用同一个producer并发publish 79 | for j := 0; j < 1; j++ { 80 | go func(v int) { 81 | for { 82 | v++ 83 | msg := mq.NewPublishMsg([]byte(fmt.Sprintf(`{"name":"zwf-%d"}`, v))) 84 | err = p.Publish("exch.unitest", "route.unitest2", msg) 85 | if err != nil { 86 | log.Printf("[ERROR] %s\n", err.Error()) 87 | } 88 | //log.Info("Producer(%d) state:%d, err:%v\n", i, p.State(), err) 89 | } 90 | }(j) 91 | time.Sleep(1 * time.Second) 92 | } 93 | 94 | }(i) 95 | } 96 | 97 | go func() { 98 | http.ListenAndServe(PProfAddr, nil) 99 | }() 100 | 101 | sig := make(chan os.Signal, 1) 102 | signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) 103 | <-sig 104 | //for i := 0; i < 1000; i++ { 105 | // if i > 0 && i%3 == 0 { 106 | // p.CloseChan() 107 | // } 108 | // err = p.Publish("exch.unitest", "route.unitest2", mq.NewPublishMsg([]byte(`{"name":"zwf"}`))) 109 | // log.Info("Produce state:%d, err:%v\n", p.State(), err) 110 | // time.Sleep(time.Second) 111 | //} 112 | 113 | //p.Close() 114 | //m.Close() 115 | } 116 | -------------------------------------------------------------------------------- /mq/common.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hurricanezwf. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mq 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/streadway/amqp" 21 | ) 22 | 23 | // Exchange type 24 | var ( 25 | ExchangeDirect = amqp.ExchangeDirect 26 | ExchangeFanout = amqp.ExchangeFanout 27 | ExchangeTopic = amqp.ExchangeTopic 28 | ExchangeHeaders = amqp.ExchangeHeaders 29 | ) 30 | 31 | // DeliveryMode 32 | var ( 33 | Transient uint8 = amqp.Transient 34 | Persistent uint8 = amqp.Persistent 35 | ) 36 | 37 | // ExchangeBinds exchange ==> routeKey ==> queues 38 | type ExchangeBinds struct { 39 | Exch *Exchange 40 | Bindings []*Binding 41 | } 42 | 43 | // Biding routeKey ==> queues 44 | type Binding struct { 45 | RouteKey string 46 | Queues []*Queue 47 | NoWait bool // default is false 48 | Args amqp.Table // default is nil 49 | } 50 | 51 | // Exchange 基于amqp的Exchange配置 52 | type Exchange struct { 53 | Name string 54 | Kind string 55 | Durable bool 56 | AutoDelete bool 57 | Internal bool 58 | NoWait bool 59 | Args amqp.Table // default is nil 60 | } 61 | 62 | func DefaultExchange(name string, kind string) *Exchange { 63 | return &Exchange{ 64 | Name: name, 65 | Kind: kind, 66 | Durable: true, 67 | AutoDelete: false, 68 | Internal: false, 69 | NoWait: false, 70 | Args: nil, 71 | } 72 | } 73 | 74 | // Queue 基于amqp的Queue配置 75 | type Queue struct { 76 | Name string 77 | Durable bool 78 | AutoDelete bool 79 | Exclusive bool 80 | NoWait bool 81 | Args amqp.Table 82 | } 83 | 84 | func DefaultQueue(name string) *Queue { 85 | return &Queue{ 86 | Name: name, 87 | Durable: true, 88 | AutoDelete: false, 89 | Exclusive: false, 90 | NoWait: false, 91 | Args: nil, 92 | } 93 | } 94 | 95 | // 生产者生产的数据格式 96 | type PublishMsg struct { 97 | ContentType string // MIME content type 98 | ContentEncoding string // MIME content type 99 | DeliveryMode uint8 // Transient or Persistent 100 | Priority uint8 // 0 to 9 101 | Timestamp time.Time 102 | Body []byte 103 | } 104 | 105 | func NewPublishMsg(body []byte) *PublishMsg { 106 | return &PublishMsg{ 107 | ContentType: "application/json", 108 | ContentEncoding: "", 109 | DeliveryMode: Persistent, 110 | Priority: uint8(5), 111 | Timestamp: time.Now(), 112 | Body: body, 113 | } 114 | } 115 | 116 | // 消费者消费选项 117 | type ConsumeOption struct { 118 | AutoAck bool 119 | Exclusive bool 120 | NoLocal bool 121 | NoWait bool 122 | Args amqp.Table 123 | } 124 | 125 | func DefaultConsumeOption() *ConsumeOption { 126 | return &ConsumeOption{ 127 | NoWait: true, 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/certs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Creates the CA, server and client certs to be used by tls_test.go 4 | # http://www.rabbitmq.com/ssl.html 5 | # 6 | # Copy stdout into the const section of tls_test.go or use for RabbitMQ 7 | # 8 | root=$PWD/certs 9 | 10 | if [ -f $root/ca/serial ]; then 11 | echo >&2 "Previous installation found" 12 | echo >&2 "Remove $root/ca and rerun to overwrite" 13 | exit 1 14 | fi 15 | 16 | mkdir -p $root/ca/private 17 | mkdir -p $root/ca/certs 18 | mkdir -p $root/server 19 | mkdir -p $root/client 20 | 21 | cd $root/ca 22 | 23 | chmod 700 private 24 | touch index.txt 25 | echo 'unique_subject = no' > index.txt.attr 26 | echo '01' > serial 27 | echo >openssl.cnf ' 28 | [ ca ] 29 | default_ca = testca 30 | 31 | [ testca ] 32 | dir = . 33 | certificate = $dir/cacert.pem 34 | database = $dir/index.txt 35 | new_certs_dir = $dir/certs 36 | private_key = $dir/private/cakey.pem 37 | serial = $dir/serial 38 | 39 | default_crl_days = 7 40 | default_days = 3650 41 | default_md = sha1 42 | 43 | policy = testca_policy 44 | x509_extensions = certificate_extensions 45 | 46 | [ testca_policy ] 47 | commonName = supplied 48 | stateOrProvinceName = optional 49 | countryName = optional 50 | emailAddress = optional 51 | organizationName = optional 52 | organizationalUnitName = optional 53 | 54 | [ certificate_extensions ] 55 | basicConstraints = CA:false 56 | 57 | [ req ] 58 | default_bits = 2048 59 | default_keyfile = ./private/cakey.pem 60 | default_md = sha1 61 | prompt = yes 62 | distinguished_name = root_ca_distinguished_name 63 | x509_extensions = root_ca_extensions 64 | 65 | [ root_ca_distinguished_name ] 66 | commonName = hostname 67 | 68 | [ root_ca_extensions ] 69 | basicConstraints = CA:true 70 | keyUsage = keyCertSign, cRLSign 71 | 72 | [ client_ca_extensions ] 73 | basicConstraints = CA:false 74 | keyUsage = digitalSignature 75 | extendedKeyUsage = 1.3.6.1.5.5.7.3.2 76 | 77 | [ server_ca_extensions ] 78 | basicConstraints = CA:false 79 | keyUsage = keyEncipherment 80 | extendedKeyUsage = 1.3.6.1.5.5.7.3.1 81 | subjectAltName = @alt_names 82 | 83 | [ alt_names ] 84 | IP.1 = 127.0.0.1 85 | ' 86 | 87 | openssl req \ 88 | -x509 \ 89 | -nodes \ 90 | -config openssl.cnf \ 91 | -newkey rsa:2048 \ 92 | -days 3650 \ 93 | -subj "/CN=MyTestCA/" \ 94 | -out cacert.pem \ 95 | -outform PEM 96 | 97 | openssl x509 \ 98 | -in cacert.pem \ 99 | -out cacert.cer \ 100 | -outform DER 101 | 102 | openssl genrsa -out $root/server/key.pem 2048 103 | openssl genrsa -out $root/client/key.pem 2048 104 | 105 | openssl req \ 106 | -new \ 107 | -nodes \ 108 | -config openssl.cnf \ 109 | -subj "/CN=127.0.0.1/O=server/" \ 110 | -key $root/server/key.pem \ 111 | -out $root/server/req.pem \ 112 | -outform PEM 113 | 114 | openssl req \ 115 | -new \ 116 | -nodes \ 117 | -config openssl.cnf \ 118 | -subj "/CN=127.0.0.1/O=client/" \ 119 | -key $root/client/key.pem \ 120 | -out $root/client/req.pem \ 121 | -outform PEM 122 | 123 | openssl ca \ 124 | -config openssl.cnf \ 125 | -in $root/server/req.pem \ 126 | -out $root/server/cert.pem \ 127 | -notext \ 128 | -batch \ 129 | -extensions server_ca_extensions 130 | 131 | openssl ca \ 132 | -config openssl.cnf \ 133 | -in $root/client/req.pem \ 134 | -out $root/client/cert.pem \ 135 | -notext \ 136 | -batch \ 137 | -extensions client_ca_extensions 138 | 139 | cat <<-END 140 | const caCert = \` 141 | `cat $root/ca/cacert.pem` 142 | \` 143 | 144 | const serverCert = \` 145 | `cat $root/server/cert.pem` 146 | \` 147 | 148 | const serverKey = \` 149 | `cat $root/server/key.pem` 150 | \` 151 | 152 | const clientCert = \` 153 | `cat $root/client/cert.pem` 154 | \` 155 | 156 | const clientKey = \` 157 | `cat $root/client/key.pem` 158 | \` 159 | END 160 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://api.travis-ci.org/streadway/amqp.svg)](http://travis-ci.org/streadway/amqp) [![GoDoc](https://godoc.org/github.com/streadway/amqp?status.svg)](http://godoc.org/github.com/streadway/amqp) 2 | 3 | # Go RabbitMQ Client Library 4 | 5 | This is an AMQP 0.9.1 client with RabbitMQ extensions in Go. 6 | 7 | ## Project Maturity 8 | 9 | This project has been used in production systems for many years. It is reasonably mature 10 | and feature complete, and as of November 2016 has [a team of maintainers](https://github.com/streadway/amqp/issues/215). 11 | 12 | Future API changes are unlikely but possible. They will be discussed on [Github 13 | issues](https://github.com/streadway/amqp/issues) along with any bugs or 14 | enhancements. 15 | 16 | ## Supported Go Versions 17 | 18 | This library supports two most recent Go release series, currently 1.8 and 1.9. 19 | 20 | 21 | ## Supported RabbitMQ Versions 22 | 23 | This project supports RabbitMQ versions starting with `2.0` but primarily tested 24 | against reasonably recent `3.x` releases. Some features and behaviours may be 25 | server version-specific. 26 | 27 | ## Goals 28 | 29 | Provide a functional interface that closely represents the AMQP 0.9.1 model 30 | targeted to RabbitMQ as a server. This includes the minimum necessary to 31 | interact the semantics of the protocol. 32 | 33 | ## Non-goals 34 | 35 | Things not intended to be supported. 36 | 37 | * Auto reconnect and re-synchronization of client and server topologies. 38 | * Reconnection would require understanding the error paths when the 39 | topology cannot be declared on reconnect. This would require a new set 40 | of types and code paths that are best suited at the call-site of this 41 | package. AMQP has a dynamic topology that needs all peers to agree. If 42 | this doesn't happen, the behavior is undefined. Instead of producing a 43 | possible interface with undefined behavior, this package is designed to 44 | be simple for the caller to implement the necessary connection-time 45 | topology declaration so that reconnection is trivial and encapsulated in 46 | the caller's application code. 47 | * AMQP Protocol negotiation for forward or backward compatibility. 48 | * 0.9.1 is stable and widely deployed. Versions 0.10 and 1.0 are divergent 49 | specifications that change the semantics and wire format of the protocol. 50 | We will accept patches for other protocol support but have no plans for 51 | implementation ourselves. 52 | * Anything other than PLAIN and EXTERNAL authentication mechanisms. 53 | * Keeping the mechanisms interface modular makes it possible to extend 54 | outside of this package. If other mechanisms prove to be popular, then 55 | we would accept patches to include them in this package. 56 | 57 | ## Usage 58 | 59 | See the 'examples' subdirectory for simple producers and consumers executables. 60 | If you have a use-case in mind which isn't well-represented by the examples, 61 | please file an issue. 62 | 63 | ## Documentation 64 | 65 | Use [Godoc documentation](http://godoc.org/github.com/streadway/amqp) for 66 | reference and usage. 67 | 68 | [RabbitMQ tutorials in 69 | Go](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go) are also 70 | available. 71 | 72 | ## Contributing 73 | 74 | Pull requests are very much welcomed. Create your pull request on a non-master 75 | branch, make sure a test or example is included that covers your change and 76 | your commits represent coherent changes that include a reason for the change. 77 | 78 | To run the integration tests, make sure you have RabbitMQ running on any host, 79 | export the environment variable `AMQP_URL=amqp://host/` and run `go test -tags 80 | integration`. TravisCI will also run the integration tests. 81 | 82 | Thanks to the [community of contributors](https://github.com/streadway/amqp/graphs/contributors). 83 | 84 | ## External packages 85 | 86 | * [Google App Engine Dialer support](https://github.com/soundtrackyourbrand/gaeamqp) 87 | * [RabbitMQ examples in Go](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go) 88 | 89 | ## License 90 | 91 | BSD 2 clause - see LICENSE for more details. 92 | 93 | 94 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/uri.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "errors" 10 | "net" 11 | "net/url" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | var errURIScheme = errors.New("AMQP scheme must be either 'amqp://' or 'amqps://'") 17 | var errURIWhitespace = errors.New("URI must not contain whitespace") 18 | 19 | var schemePorts = map[string]int{ 20 | "amqp": 5672, 21 | "amqps": 5671, 22 | } 23 | 24 | var defaultURI = URI{ 25 | Scheme: "amqp", 26 | Host: "localhost", 27 | Port: 5672, 28 | Username: "guest", 29 | Password: "guest", 30 | Vhost: "/", 31 | } 32 | 33 | // URI represents a parsed AMQP URI string. 34 | type URI struct { 35 | Scheme string 36 | Host string 37 | Port int 38 | Username string 39 | Password string 40 | Vhost string 41 | } 42 | 43 | // ParseURI attempts to parse the given AMQP URI according to the spec. 44 | // See http://www.rabbitmq.com/uri-spec.html. 45 | // 46 | // Default values for the fields are: 47 | // 48 | // Scheme: amqp 49 | // Host: localhost 50 | // Port: 5672 51 | // Username: guest 52 | // Password: guest 53 | // Vhost: / 54 | // 55 | func ParseURI(uri string) (URI, error) { 56 | builder := defaultURI 57 | 58 | if strings.Contains(uri, " ") == true { 59 | return builder, errURIWhitespace 60 | } 61 | 62 | u, err := url.Parse(uri) 63 | if err != nil { 64 | return builder, err 65 | } 66 | 67 | defaultPort, okScheme := schemePorts[u.Scheme] 68 | 69 | if okScheme { 70 | builder.Scheme = u.Scheme 71 | } else { 72 | return builder, errURIScheme 73 | } 74 | 75 | host := u.Hostname() 76 | port := u.Port() 77 | 78 | if host != "" { 79 | builder.Host = host 80 | } 81 | 82 | if port != "" { 83 | port32, err := strconv.ParseInt(port, 10, 32) 84 | if err != nil { 85 | return builder, err 86 | } 87 | builder.Port = int(port32) 88 | } else { 89 | builder.Port = defaultPort 90 | } 91 | 92 | if u.User != nil { 93 | builder.Username = u.User.Username() 94 | if password, ok := u.User.Password(); ok { 95 | builder.Password = password 96 | } 97 | } 98 | 99 | if u.Path != "" { 100 | if strings.HasPrefix(u.Path, "/") { 101 | if u.Host == "" && strings.HasPrefix(u.Path, "///") { 102 | // net/url doesn't handle local context authorities and leaves that up 103 | // to the scheme handler. In our case, we translate amqp:/// into the 104 | // default host and whatever the vhost should be 105 | if len(u.Path) > 3 { 106 | builder.Vhost = u.Path[3:] 107 | } 108 | } else if len(u.Path) > 1 { 109 | builder.Vhost = u.Path[1:] 110 | } 111 | } else { 112 | builder.Vhost = u.Path 113 | } 114 | } 115 | 116 | return builder, nil 117 | } 118 | 119 | // PlainAuth returns a PlainAuth structure based on the parsed URI's 120 | // Username and Password fields. 121 | func (uri URI) PlainAuth() *PlainAuth { 122 | return &PlainAuth{ 123 | Username: uri.Username, 124 | Password: uri.Password, 125 | } 126 | } 127 | 128 | func (uri URI) String() string { 129 | authority, err := url.Parse("") 130 | if err != nil { 131 | return err.Error() 132 | } 133 | 134 | authority.Scheme = uri.Scheme 135 | 136 | if uri.Username != defaultURI.Username || uri.Password != defaultURI.Password { 137 | authority.User = url.User(uri.Username) 138 | 139 | if uri.Password != defaultURI.Password { 140 | authority.User = url.UserPassword(uri.Username, uri.Password) 141 | } 142 | } 143 | 144 | authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port)) 145 | 146 | if defaultPort, found := schemePorts[uri.Scheme]; !found || defaultPort != uri.Port { 147 | authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port)) 148 | } else { 149 | // JoinHostPort() automatically add brackets to the host if it's 150 | // an IPv6 address. 151 | // 152 | // If not port is specified, JoinHostPort() return an IP address in the 153 | // form of "[::1]:", so we use TrimSuffix() to remove the extra ":". 154 | authority.Host = strings.TrimSuffix(net.JoinHostPort(uri.Host, ""), ":") 155 | } 156 | 157 | if uri.Vhost != defaultURI.Vhost { 158 | // Make sure net/url does not double escape, e.g. 159 | // "%2F" does not become "%252F". 160 | authority.Path = uri.Vhost 161 | authority.RawPath = url.QueryEscape(uri.Vhost) 162 | } else { 163 | authority.Path = "/" 164 | } 165 | 166 | return authority.String() 167 | } 168 | -------------------------------------------------------------------------------- /mq/mq.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hurricanezwf. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // mq封装了RabbitMQ的生产者消费者 16 | // 建议在大流量的情况下生产者和消费者的TCP连接分离,以免影响传输效率, 一个MQ对象对应一个TCP连接 17 | 18 | package mq 19 | 20 | import ( 21 | "errors" 22 | "fmt" 23 | "log" 24 | "sync" 25 | "time" 26 | 27 | "github.com/streadway/amqp" 28 | ) 29 | 30 | var ( 31 | StateClosed = uint8(0) 32 | StateOpened = uint8(1) 33 | StateReopening = uint8(2) 34 | ) 35 | 36 | type MQ struct { 37 | // RabbitMQ连接的url 38 | url string 39 | 40 | // 保护内部数据并发读写 41 | mutex sync.RWMutex 42 | 43 | // RabbitMQ TCP连接 44 | conn *amqp.Connection 45 | 46 | producers []*Producer 47 | consumers []*Consumer 48 | 49 | // RabbitMQ 监听连接错误 50 | closeC chan *amqp.Error 51 | // 监听用户手动关闭 52 | stopC chan struct{} 53 | 54 | // MQ状态 55 | state uint8 56 | } 57 | 58 | func New(url string) *MQ { 59 | return &MQ{ 60 | url: url, 61 | producers: make([]*Producer, 0, 1), 62 | state: StateClosed, 63 | } 64 | } 65 | 66 | func (m *MQ) Open() (mq *MQ, err error) { 67 | // 进行Open期间不允许做任何跟连接有关的事情 68 | m.mutex.Lock() 69 | defer m.mutex.Unlock() 70 | 71 | if m.state == StateOpened { 72 | return m, errors.New("MQ: Had been opened") 73 | } 74 | 75 | if m.conn, err = m.dial(); err != nil { 76 | return m, fmt.Errorf("MQ: Dial err: %v", err) 77 | } 78 | 79 | m.state = StateOpened 80 | m.stopC = make(chan struct{}) 81 | m.closeC = make(chan *amqp.Error, 1) 82 | m.conn.NotifyClose(m.closeC) 83 | 84 | go m.keepalive() 85 | 86 | return m, nil 87 | } 88 | 89 | func (m *MQ) Close() { 90 | m.mutex.Lock() 91 | 92 | // close producers 93 | for _, p := range m.producers { 94 | p.Close() 95 | } 96 | m.producers = m.producers[:0] 97 | 98 | // close consumers 99 | for _, c := range m.consumers { 100 | c.Close() 101 | } 102 | m.consumers = m.consumers[:0] 103 | 104 | // close mq connection 105 | select { 106 | case <-m.stopC: 107 | // had been closed 108 | default: 109 | close(m.stopC) 110 | } 111 | 112 | m.mutex.Unlock() 113 | 114 | // wait done 115 | for m.State() != StateClosed { 116 | time.Sleep(time.Second) 117 | } 118 | } 119 | 120 | func (m *MQ) Producer(name string) (*Producer, error) { 121 | m.mutex.Lock() 122 | defer m.mutex.Unlock() 123 | 124 | if m.state != StateOpened { 125 | return nil, fmt.Errorf("MQ: Not initialized, now state is %d", m.State) 126 | } 127 | p := newProducer(name, m) 128 | m.producers = append(m.producers, p) 129 | return p, nil 130 | } 131 | 132 | func (m *MQ) Consumer(name string) (*Consumer, error) { 133 | m.mutex.Lock() 134 | defer m.mutex.Unlock() 135 | 136 | if m.state != StateOpened { 137 | return nil, fmt.Errorf("MQ: Not initialized, now state is %d", m.State) 138 | } 139 | c := newConsumer(name, m) 140 | m.consumers = append(m.consumers, c) 141 | return c, nil 142 | } 143 | 144 | func (m *MQ) State() uint8 { 145 | m.mutex.RLock() 146 | defer m.mutex.RUnlock() 147 | return m.state 148 | } 149 | 150 | func (m *MQ) keepalive() { 151 | select { 152 | case <-m.stopC: 153 | // 正常关闭 154 | log.Println("[WARN] MQ: Shutdown normally.") 155 | m.mutex.Lock() 156 | m.conn.Close() 157 | m.state = StateClosed 158 | m.mutex.Unlock() 159 | 160 | case err := <-m.closeC: 161 | if err == nil { 162 | log.Println("[ERROR] MQ: Disconnected with MQ, but Error detail is nil") 163 | } else { 164 | log.Printf("[ERROR] MQ: Disconnected with MQ, code:%d, reason:%s\n", err.Code, err.Reason) 165 | } 166 | 167 | // tcp连接中断, 重新连接 168 | m.mutex.Lock() 169 | m.state = StateReopening 170 | m.mutex.Unlock() 171 | 172 | maxRetry := 99999999 173 | for i := 0; i < maxRetry; i++ { 174 | time.Sleep(time.Second) 175 | if _, e := m.Open(); e != nil { 176 | log.Printf("[ERROR] MQ: Connection recover failed for %d times, %v\n", i+1, e) 177 | continue 178 | } 179 | log.Printf("[INFO] MQ: Connection recover OK. Total try %d times\n", i+1) 180 | return 181 | } 182 | log.Printf("[ERROR] MQ: Try to reconnect to MQ failed over maxRetry(%d), so exit.\n", maxRetry) 183 | } 184 | } 185 | 186 | //func (m *MQ) Consumer() *Consumer { 187 | // return nil 188 | //} 189 | 190 | func (m *MQ) channel() (*amqp.Channel, error) { 191 | m.mutex.RLock() 192 | defer m.mutex.RUnlock() 193 | return m.conn.Channel() 194 | } 195 | 196 | func (m MQ) dial() (*amqp.Connection, error) { 197 | return amqp.Dial(m.url) 198 | } 199 | 200 | func init() { 201 | log.SetFlags(log.LstdFlags | log.Lshortfile) 202 | } 203 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | /* 7 | Package amqp is an AMQP 0.9.1 client with RabbitMQ extensions 8 | 9 | Understand the AMQP 0.9.1 messaging model by reviewing these links first. Much 10 | of the terminology in this library directly relates to AMQP concepts. 11 | 12 | Resources 13 | 14 | http://www.rabbitmq.com/tutorials/amqp-concepts.html 15 | http://www.rabbitmq.com/getstarted.html 16 | http://www.rabbitmq.com/amqp-0-9-1-reference.html 17 | 18 | Design 19 | 20 | Most other broker clients publish to queues, but in AMQP, clients publish 21 | Exchanges instead. AMQP is programmable, meaning that both the producers and 22 | consumers agree on the configuration of the broker, instead requiring an 23 | operator or system configuration that declares the logical topology in the 24 | broker. The routing between producers and consumer queues is via Bindings. 25 | These bindings form the logical topology of the broker. 26 | 27 | In this library, a message sent from publisher is called a "Publishing" and a 28 | message received to a consumer is called a "Delivery". The fields of 29 | Publishings and Deliveries are close but not exact mappings to the underlying 30 | wire format to maintain stronger types. Many other libraries will combine 31 | message properties with message headers. In this library, the message well 32 | known properties are strongly typed fields on the Publishings and Deliveries, 33 | whereas the user defined headers are in the Headers field. 34 | 35 | The method naming closely matches the protocol's method name with positional 36 | parameters mapping to named protocol message fields. The motivation here is to 37 | present a comprehensive view over all possible interactions with the server. 38 | 39 | Generally, methods that map to protocol methods of the "basic" class will be 40 | elided in this interface, and "select" methods of various channel mode selectors 41 | will be elided for example Channel.Confirm and Channel.Tx. 42 | 43 | The library is intentionally designed to be synchronous, where responses for 44 | each protocol message are required to be received in an RPC manner. Some 45 | methods have a noWait parameter like Channel.QueueDeclare, and some methods are 46 | asynchronous like Channel.Publish. The error values should still be checked for 47 | these methods as they will indicate IO failures like when the underlying 48 | connection closes. 49 | 50 | Asynchronous Events 51 | 52 | Clients of this library may be interested in receiving some of the protocol 53 | messages other than Deliveries like basic.ack methods while a channel is in 54 | confirm mode. 55 | 56 | The Notify* methods with Connection and Channel receivers model the pattern of 57 | asynchronous events like closes due to exceptions, or messages that are sent out 58 | of band from an RPC call like basic.ack or basic.flow. 59 | 60 | Any asynchronous events, including Deliveries and Publishings must always have 61 | a receiver until the corresponding chans are closed. Without asynchronous 62 | receivers, the sychronous methods will block. 63 | 64 | Use Case 65 | 66 | It's important as a client to an AMQP topology to ensure the state of the 67 | broker matches your expectations. For both publish and consume use cases, 68 | make sure you declare the queues, exchanges and bindings you expect to exist 69 | prior to calling Channel.Publish or Channel.Consume. 70 | 71 | // Connections start with amqp.Dial() typically from a command line argument 72 | // or environment variable. 73 | connection, err := amqp.Dial(os.Getenv("AMQP_URL")) 74 | 75 | // To cleanly shutdown by flushing kernel buffers, make sure to close and 76 | // wait for the response. 77 | defer connection.Close() 78 | 79 | // Most operations happen on a channel. If any error is returned on a 80 | // channel, the channel will no longer be valid, throw it away and try with 81 | // a different channel. If you use many channels, it's useful for the 82 | // server to 83 | channel, err := connection.Channel() 84 | 85 | // Declare your topology here, if it doesn't exist, it will be created, if 86 | // it existed already and is not what you expect, then that's considered an 87 | // error. 88 | 89 | // Use your connection on this topology with either Publish or Consume, or 90 | // inspect your queues with QueueInspect. It's unwise to mix Publish and 91 | // Consume to let TCP do its job well. 92 | 93 | SSL/TLS - Secure connections 94 | 95 | When Dial encounters an amqps:// scheme, it will use the zero value of a 96 | tls.Config. This will only perform server certificate and host verification. 97 | 98 | Use DialTLS when you wish to provide a client certificate (recommended), 99 | include a private certificate authority's certificate in the cert chain for 100 | server validity, or run insecure by not verifying the server certificate dial 101 | your own connection. DialTLS will use the provided tls.Config when it 102 | encounters an amqps:// scheme and will dial a plain connection when it 103 | encounters an amqp:// scheme. 104 | 105 | SSL/TLS in RabbitMQ is documented here: http://www.rabbitmq.com/ssl.html 106 | 107 | */ 108 | package amqp 109 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/delivery.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "errors" 10 | "time" 11 | ) 12 | 13 | var errDeliveryNotInitialized = errors.New("delivery not initialized") 14 | 15 | // Acknowledger notifies the server of successful or failed consumption of 16 | // delivieries via identifier found in the Delivery.DeliveryTag field. 17 | // 18 | // Applications can provide mock implementations in tests of Delivery handlers. 19 | type Acknowledger interface { 20 | Ack(tag uint64, multiple bool) error 21 | Nack(tag uint64, multiple bool, requeue bool) error 22 | Reject(tag uint64, requeue bool) error 23 | } 24 | 25 | // Delivery captures the fields for a previously delivered message resident in 26 | // a queue to be delivered by the server to a consumer from Channel.Consume or 27 | // Channel.Get. 28 | type Delivery struct { 29 | Acknowledger Acknowledger // the channel from which this delivery arrived 30 | 31 | Headers Table // Application or header exchange table 32 | 33 | // Properties 34 | ContentType string // MIME content type 35 | ContentEncoding string // MIME content encoding 36 | DeliveryMode uint8 // queue implementation use - non-persistent (1) or persistent (2) 37 | Priority uint8 // queue implementation use - 0 to 9 38 | CorrelationId string // application use - correlation identifier 39 | ReplyTo string // application use - address to reply to (ex: RPC) 40 | Expiration string // implementation use - message expiration spec 41 | MessageId string // application use - message identifier 42 | Timestamp time.Time // application use - message timestamp 43 | Type string // application use - message type name 44 | UserId string // application use - creating user - should be authenticated user 45 | AppId string // application use - creating application id 46 | 47 | // Valid only with Channel.Consume 48 | ConsumerTag string 49 | 50 | // Valid only with Channel.Get 51 | MessageCount uint32 52 | 53 | DeliveryTag uint64 54 | Redelivered bool 55 | Exchange string // basic.publish exhange 56 | RoutingKey string // basic.publish routing key 57 | 58 | Body []byte 59 | } 60 | 61 | func newDelivery(channel *Channel, msg messageWithContent) *Delivery { 62 | props, body := msg.getContent() 63 | 64 | delivery := Delivery{ 65 | Acknowledger: channel, 66 | 67 | Headers: props.Headers, 68 | ContentType: props.ContentType, 69 | ContentEncoding: props.ContentEncoding, 70 | DeliveryMode: props.DeliveryMode, 71 | Priority: props.Priority, 72 | CorrelationId: props.CorrelationId, 73 | ReplyTo: props.ReplyTo, 74 | Expiration: props.Expiration, 75 | MessageId: props.MessageId, 76 | Timestamp: props.Timestamp, 77 | Type: props.Type, 78 | UserId: props.UserId, 79 | AppId: props.AppId, 80 | 81 | Body: body, 82 | } 83 | 84 | // Properties for the delivery types 85 | switch m := msg.(type) { 86 | case *basicDeliver: 87 | delivery.ConsumerTag = m.ConsumerTag 88 | delivery.DeliveryTag = m.DeliveryTag 89 | delivery.Redelivered = m.Redelivered 90 | delivery.Exchange = m.Exchange 91 | delivery.RoutingKey = m.RoutingKey 92 | 93 | case *basicGetOk: 94 | delivery.MessageCount = m.MessageCount 95 | delivery.DeliveryTag = m.DeliveryTag 96 | delivery.Redelivered = m.Redelivered 97 | delivery.Exchange = m.Exchange 98 | delivery.RoutingKey = m.RoutingKey 99 | } 100 | 101 | return &delivery 102 | } 103 | 104 | /* 105 | Ack delegates an acknowledgement through the Acknowledger interface that the 106 | client or server has finished work on a delivery. 107 | 108 | All deliveries in AMQP must be acknowledged. If you called Channel.Consume 109 | with autoAck true then the server will be automatically ack each message and 110 | this method should not be called. Otherwise, you must call Delivery.Ack after 111 | you have successfully processed this delivery. 112 | 113 | When multiple is true, this delivery and all prior unacknowledged deliveries 114 | on the same channel will be acknowledged. This is useful for batch processing 115 | of deliveries. 116 | 117 | An error will indicate that the acknowledge could not be delivered to the 118 | channel it was sent from. 119 | 120 | Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every 121 | delivery that is not automatically acknowledged. 122 | */ 123 | func (d Delivery) Ack(multiple bool) error { 124 | if d.Acknowledger == nil { 125 | return errDeliveryNotInitialized 126 | } 127 | return d.Acknowledger.Ack(d.DeliveryTag, multiple) 128 | } 129 | 130 | /* 131 | Reject delegates a negatively acknowledgement through the Acknowledger interface. 132 | 133 | When requeue is true, queue this message to be delivered to a consumer on a 134 | different channel. When requeue is false or the server is unable to queue this 135 | message, it will be dropped. 136 | 137 | If you are batch processing deliveries, and your server supports it, prefer 138 | Delivery.Nack. 139 | 140 | Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every 141 | delivery that is not automatically acknowledged. 142 | */ 143 | func (d Delivery) Reject(requeue bool) error { 144 | if d.Acknowledger == nil { 145 | return errDeliveryNotInitialized 146 | } 147 | return d.Acknowledger.Reject(d.DeliveryTag, requeue) 148 | } 149 | 150 | /* 151 | Nack negatively acknowledge the delivery of message(s) identified by the 152 | delivery tag from either the client or server. 153 | 154 | When multiple is true, nack messages up to and including delivered messages up 155 | until the delivery tag delivered on the same channel. 156 | 157 | When requeue is true, request the server to deliver this message to a different 158 | consumer. If it is not possible or requeue is false, the message will be 159 | dropped or delivered to a server configured dead-letter queue. 160 | 161 | This method must not be used to select or requeue messages the client wishes 162 | not to handle, rather it is to inform the server that the client is incapable 163 | of handling this message at this time. 164 | 165 | Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every 166 | delivery that is not automatically acknowledged. 167 | */ 168 | func (d Delivery) Nack(multiple, requeue bool) error { 169 | if d.Acknowledger == nil { 170 | return errDeliveryNotInitialized 171 | } 172 | return d.Acknowledger.Nack(d.DeliveryTag, multiple, requeue) 173 | } 174 | -------------------------------------------------------------------------------- /mq/consumer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hurricanezwf. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mq 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "log" 21 | "sync" 22 | "time" 23 | 24 | "github.com/streadway/amqp" 25 | ) 26 | 27 | type Delivery struct { 28 | amqp.Delivery 29 | } 30 | 31 | type Consumer struct { 32 | // Consumer的名字, "" is OK 33 | name string 34 | 35 | // MQ实例 36 | mq *MQ 37 | 38 | // 保护数据并发安全 39 | mutex sync.RWMutex 40 | 41 | // MQ的会话channel 42 | ch *amqp.Channel 43 | 44 | // MQ的exchange与其绑定的queues 45 | exchangeBinds []*ExchangeBinds 46 | 47 | // Oos prefetch 48 | prefetch int 49 | 50 | // 上层用于接收消费出来的消息的管道 51 | callback chan<- Delivery 52 | 53 | // 监听会话channel关闭 54 | closeC chan *amqp.Error 55 | // Consumer关闭控制 56 | stopC chan struct{} 57 | 58 | // Consumer状态 59 | state uint8 60 | } 61 | 62 | func newConsumer(name string, mq *MQ) *Consumer { 63 | return &Consumer{ 64 | name: name, 65 | mq: mq, 66 | stopC: make(chan struct{}), 67 | } 68 | } 69 | 70 | func (c Consumer) Name() string { 71 | return c.name 72 | } 73 | 74 | // CloseChan 该接口仅用于测试使用, 勿手动调用 75 | func (c *Consumer) CloseChan() { 76 | c.mutex.Lock() 77 | c.ch.Close() 78 | c.mutex.Unlock() 79 | } 80 | 81 | func (c *Consumer) SetExchangeBinds(eb []*ExchangeBinds) *Consumer { 82 | c.mutex.Lock() 83 | if c.state != StateOpened { 84 | c.exchangeBinds = eb 85 | } 86 | c.mutex.Unlock() 87 | return c 88 | } 89 | 90 | func (c *Consumer) SetMsgCallback(cb chan<- Delivery) *Consumer { 91 | c.mutex.Lock() 92 | c.callback = cb 93 | c.mutex.Unlock() 94 | return c 95 | } 96 | 97 | // SetQos 设置channel粒度的Qos, prefetch取值范围[0,∞), 默认为0 98 | // 如果想要RoundRobin地进行消费,设置prefetch为1即可 99 | // 注意:在调用Open前设置 100 | func (c *Consumer) SetQos(prefetch int) *Consumer { 101 | c.mutex.Lock() 102 | c.prefetch = prefetch 103 | c.mutex.Unlock() 104 | return c 105 | } 106 | 107 | func (c *Consumer) Open() error { 108 | // Open期间不允许对channel做任何操作 109 | c.mutex.Lock() 110 | defer c.mutex.Unlock() 111 | 112 | // 参数校验 113 | if c.mq == nil { 114 | return errors.New("MQ: Bad consumer") 115 | } 116 | if len(c.exchangeBinds) <= 0 { 117 | return errors.New("MQ: No exchangeBinds found. You should SetExchangeBinds brefore open.") 118 | } 119 | 120 | // 状态检测 121 | if c.state == StateOpened { 122 | return errors.New("MQ: Consumer had been opened") 123 | } 124 | 125 | // 初始化channel 126 | ch, err := c.mq.channel() 127 | if err != nil { 128 | return fmt.Errorf("MQ: Create channel failed, %v", err) 129 | } 130 | 131 | err = func(ch *amqp.Channel) error { 132 | var e error 133 | if e = applyExchangeBinds(ch, c.exchangeBinds); e != nil { 134 | return e 135 | } 136 | if e = ch.Qos(c.prefetch, 0, false); e != nil { 137 | return e 138 | } 139 | return nil 140 | }(ch) 141 | if err != nil { 142 | ch.Close() 143 | return fmt.Errorf("MQ: %v", err) 144 | } 145 | 146 | c.ch = ch 147 | c.state = StateOpened 148 | c.stopC = make(chan struct{}) 149 | c.closeC = make(chan *amqp.Error, 1) 150 | c.ch.NotifyClose(c.closeC) 151 | 152 | // 开始循环消费 153 | opt := DefaultConsumeOption() 154 | notify := make(chan error, 1) 155 | c.consume(opt, notify) 156 | for e := range notify { 157 | if e != nil { 158 | log.Printf("[ERROR] %v\n", e) 159 | continue 160 | } 161 | break 162 | } 163 | close(notify) 164 | 165 | // 健康检测 166 | go c.keepalive() 167 | 168 | return nil 169 | } 170 | 171 | func (c *Consumer) Close() { 172 | c.mutex.Lock() 173 | defer c.mutex.Unlock() 174 | 175 | select { 176 | case <-c.stopC: 177 | // had been closed 178 | default: 179 | close(c.stopC) 180 | } 181 | } 182 | 183 | // notifyErr 向上层抛出错误, 如果error为空表示执行完成.由上层负责关闭channel 184 | func (c *Consumer) consume(opt *ConsumeOption, notifyErr chan<- error) { 185 | for idx, eb := range c.exchangeBinds { 186 | if eb == nil { 187 | notifyErr <- fmt.Errorf("MQ: ExchangeBinds[%d] is nil, consumer(%s)", idx, c.name) 188 | continue 189 | } 190 | for i, b := range eb.Bindings { 191 | if b == nil { 192 | notifyErr <- fmt.Errorf("MQ: Binding[%d] is nil, ExchangeBinds[%d], consumer(%s)", i, idx, c.name) 193 | continue 194 | } 195 | for qi, q := range b.Queues { 196 | if q == nil { 197 | notifyErr <- fmt.Errorf("MQ: Queue[%d] is nil, ExchangeBinds[%d], Biding[%d], consumer(%s)", qi, idx, i, c.name) 198 | continue 199 | } 200 | delivery, err := c.ch.Consume(q.Name, "", opt.AutoAck, opt.Exclusive, opt.NoLocal, opt.NoWait, opt.Args) 201 | if err != nil { 202 | notifyErr <- fmt.Errorf("MQ: Consumer(%s) consume queue(%s) failed, %v", c.name, q.Name, err) 203 | continue 204 | } 205 | go c.deliver(delivery) 206 | } 207 | } 208 | } 209 | notifyErr <- nil 210 | } 211 | 212 | func (c *Consumer) deliver(delivery <-chan amqp.Delivery) { 213 | for d := range delivery { 214 | if c.callback != nil { 215 | c.callback <- Delivery{d} 216 | } 217 | } 218 | } 219 | 220 | func (c *Consumer) State() uint8 { 221 | c.mutex.RLock() 222 | defer c.mutex.RUnlock() 223 | return c.state 224 | } 225 | 226 | func (c *Consumer) keepalive() { 227 | select { 228 | case <-c.stopC: 229 | // 正常关闭 230 | log.Printf("[WARN] Consumer(%s) shutdown normally\n", c.Name()) 231 | c.mutex.Lock() 232 | c.ch.Close() 233 | c.ch = nil 234 | c.state = StateClosed 235 | c.mutex.Unlock() 236 | 237 | case err := <-c.closeC: 238 | if err == nil { 239 | log.Printf("[ERROR] MQ: Consumer(%s)'s channel was closed, but Error detail is nil\n", c.name) 240 | } else { 241 | log.Printf("[ERROR] MQ: Consumer(%s)'s channel was closed, code:%d, reason:%s\n", c.name, err.Code, err.Reason) 242 | } 243 | 244 | // channel被异常关闭了 245 | c.mutex.Lock() 246 | c.state = StateReopening 247 | c.mutex.Unlock() 248 | 249 | maxRetry := 99999999 250 | for i := 0; i < maxRetry; i++ { 251 | time.Sleep(time.Second) 252 | if c.mq.State() != StateOpened { 253 | log.Printf("[WARN] MQ: Consumer(%s) try to recover channel for %d times, but mq's state != StateOpened\n", c.name, i+1) 254 | continue 255 | } 256 | if e := c.Open(); e != nil { 257 | log.Printf("[WARN] MQ: Consumer(%s) recover channel failed for %d times, Err:%v\n", c.name, i+1, e) 258 | continue 259 | } 260 | log.Printf("[INFO] MQ: Consumer(%s) recover channel OK. Total try %d times\n", c.name, i+1) 261 | return 262 | } 263 | log.Printf("[ERROR] MQ: Consumer(%s) try to recover channel over maxRetry(%d), so exit\n", c.name, maxRetry) 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /mq/producer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Hurricanezwf. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mq 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "log" 21 | "sync" 22 | "time" 23 | 24 | "github.com/streadway/amqp" 25 | ) 26 | 27 | type Producer struct { 28 | // Producer的名字, "" is OK 29 | name string 30 | 31 | // MQ实例 32 | mq *MQ 33 | 34 | // channel pool, 重复使用 35 | chPool *sync.Pool 36 | 37 | // publish数据的锁 38 | publishMutex sync.RWMutex 39 | 40 | // 保护数据安全地并发读写 41 | mutex sync.RWMutex 42 | 43 | // MQ的会话channel 44 | ch *amqp.Channel 45 | 46 | // MQ的exchange与其绑定的queues 47 | exchangeBinds []*ExchangeBinds 48 | 49 | // 生产者confirm开关 50 | enableConfirm bool 51 | // 监听publish confirm 52 | confirmC chan amqp.Confirmation 53 | // confirm结果检测 54 | confirm *confirmHelper 55 | 56 | // 监听会话channel关闭 57 | closeC chan *amqp.Error 58 | // Producer关闭控制 59 | stopC chan struct{} 60 | 61 | // Producer 状态 62 | state uint8 63 | } 64 | 65 | func newProducer(name string, mq *MQ) *Producer { 66 | return &Producer{ 67 | name: name, 68 | mq: mq, 69 | chPool: &sync.Pool{ 70 | New: func() interface{} { return make(chan bool, 1) }, 71 | }, 72 | state: StateClosed, 73 | } 74 | } 75 | 76 | func (p Producer) Name() string { 77 | return p.name 78 | } 79 | 80 | // CloseChan 仅用于测试使用,勿手动调用 81 | func (p *Producer) CloseChan() { 82 | p.mutex.Lock() 83 | p.ch.Close() 84 | p.mutex.Unlock() 85 | } 86 | 87 | // Confirm 是否开启生产者confirm功能, 默认为false, 该选项在Open()前设置. 88 | // 说明: 目前仅实现串行化的confirm, 每次的等待confirm额外需要约50ms,建议上层并发调用Publish 89 | func (p *Producer) Confirm(enable bool) *Producer { 90 | p.mutex.Lock() 91 | p.enableConfirm = enable 92 | p.mutex.Unlock() 93 | return p 94 | } 95 | 96 | func (p *Producer) SetExchangeBinds(eb []*ExchangeBinds) *Producer { 97 | p.mutex.Lock() 98 | if p.state != StateOpened { 99 | p.exchangeBinds = eb 100 | } 101 | p.mutex.Unlock() 102 | return p 103 | } 104 | 105 | func (p *Producer) Open() error { 106 | p.mutex.Lock() 107 | defer p.mutex.Unlock() 108 | 109 | // 条件检测 110 | if p.mq == nil { 111 | return errors.New("MQ: Bad producer") 112 | } 113 | if len(p.exchangeBinds) <= 0 { 114 | return errors.New("MQ: No exchangeBinds found. You should SetExchangeBinds before open.") 115 | } 116 | if p.state == StateOpened { 117 | return errors.New("MQ: Producer had been opened") 118 | } 119 | 120 | // 创建并初始化channel 121 | ch, err := p.mq.channel() 122 | if err != nil { 123 | return fmt.Errorf("MQ: Create channel failed, %v", err) 124 | } 125 | if err = applyExchangeBinds(ch, p.exchangeBinds); err != nil { 126 | ch.Close() 127 | return err 128 | } 129 | 130 | p.ch = ch 131 | p.state = StateOpened 132 | 133 | // 初始化发送Confirm 134 | if p.enableConfirm { 135 | p.confirmC = make(chan amqp.Confirmation, 1) // channel关闭时自动关闭 136 | p.ch.Confirm(false) 137 | p.ch.NotifyPublish(p.confirmC) 138 | if p.confirm == nil { 139 | p.confirm = newConfirmHelper() 140 | } else { 141 | p.confirm.Reset() 142 | } 143 | 144 | go p.listenConfirm() 145 | } 146 | 147 | // 初始化Keepalive 148 | if true { 149 | p.stopC = make(chan struct{}) 150 | p.closeC = make(chan *amqp.Error, 1) // channel关闭时自动关闭 151 | p.ch.NotifyClose(p.closeC) 152 | 153 | go p.keepalive() 154 | } 155 | 156 | return nil 157 | } 158 | 159 | func (p *Producer) Close() { 160 | p.mutex.Lock() 161 | defer p.mutex.Unlock() 162 | 163 | select { 164 | case <-p.stopC: 165 | // had been closed 166 | default: 167 | close(p.stopC) 168 | } 169 | } 170 | 171 | // 在同步Publish Confirm模式下, 每次Publish将额外有约50ms的等待时间.如果采用这种模式,建议上层并发publish 172 | func (p *Producer) Publish(exchange, routeKey string, msg *PublishMsg) error { 173 | if msg == nil { 174 | return errors.New("MQ: Nil publish msg") 175 | } 176 | 177 | if st := p.State(); st != StateOpened { 178 | return fmt.Errorf("MQ: Producer unopened, now state is %d", p.state) 179 | } 180 | 181 | pub := amqp.Publishing{ 182 | ContentType: msg.ContentType, 183 | ContentEncoding: msg.ContentEncoding, 184 | DeliveryMode: msg.DeliveryMode, 185 | Priority: msg.Priority, 186 | Timestamp: msg.Timestamp, 187 | Body: msg.Body, 188 | } 189 | 190 | // 非confirm模式 191 | if p.enableConfirm == false { 192 | return p.ch.Publish(exchange, routeKey, false, false, pub) 193 | } 194 | 195 | // confirm模式 196 | // 这里加锁保证消息发送顺序与接收ack的channel的编号一致 197 | p.publishMutex.Lock() 198 | if err := p.ch.Publish(exchange, routeKey, false, false, pub); err != nil { 199 | p.publishMutex.Unlock() 200 | return fmt.Errorf("MQ: Producer publish failed, %v", err) 201 | } 202 | ch := p.chPool.Get().(chan bool) 203 | p.confirm.Listen(ch) 204 | p.publishMutex.Unlock() 205 | 206 | ack, ok := <-ch 207 | p.chPool.Put(ch) 208 | if !ack || !ok { 209 | return fmt.Errorf("MQ: Producer publish failed, confirm ack is false. ack:%t, ok:%t", ack, ok) 210 | } 211 | return nil 212 | } 213 | 214 | func (p *Producer) State() uint8 { 215 | p.mutex.RLock() 216 | defer p.mutex.RUnlock() 217 | return p.state 218 | } 219 | 220 | func (p *Producer) keepalive() { 221 | select { 222 | case <-p.stopC: 223 | // 正常关闭 224 | log.Printf("[WARN] MQ: Producer(%s) shutdown normally.\n", p.name) 225 | p.mutex.Lock() 226 | p.ch.Close() 227 | p.state = StateClosed 228 | p.mutex.Unlock() 229 | 230 | case err := <-p.closeC: 231 | if err == nil { 232 | log.Printf("[ERROR] MQ: Producer(%s)'s channel was closed, but Error detail is nil\n", p.name) 233 | } else { 234 | log.Printf("[ERROR] MQ: Producer(%s)'s channel was closed, code:%d, reason:%s\n", p.name, err.Code, err.Reason) 235 | } 236 | 237 | // channel被异常关闭了 238 | p.mutex.Lock() 239 | p.state = StateReopening 240 | p.mutex.Unlock() 241 | 242 | maxRetry := 99999999 243 | for i := 0; i < maxRetry; i++ { 244 | time.Sleep(time.Second) 245 | if p.mq.State() != StateOpened { 246 | log.Printf("[WARN] MQ: Producer(%s) try to recover channel for %d times, but mq's state != StateOpened\n", p.name, i+1) 247 | continue 248 | } 249 | if e := p.Open(); e != nil { 250 | log.Printf("[WARN] MQ: Producer(%s) recover channel failed for %d times, Err:%v\n", p.name, i+1, e) 251 | continue 252 | } 253 | log.Printf("[INFO] MQ: Producer(%s) recover channel OK. Total try %d times\n", p.name, i+1) 254 | return 255 | } 256 | log.Printf("[ERROR] MQ: Producer(%s) try to recover channel over maxRetry(%d), so exit\n", p.name, maxRetry) 257 | } 258 | } 259 | 260 | func (p *Producer) listenConfirm() { 261 | for c := range p.confirmC { 262 | // TODO: 可以做个并发控制 263 | go p.confirm.Callback(c.DeliveryTag, c.Ack) 264 | } 265 | } 266 | 267 | func applyExchangeBinds(ch *amqp.Channel, exchangeBinds []*ExchangeBinds) (err error) { 268 | if ch == nil { 269 | return errors.New("MQ: Nil producer channel") 270 | } 271 | if len(exchangeBinds) <= 0 { 272 | return errors.New("MQ: Empty exchangeBinds") 273 | } 274 | 275 | for _, eb := range exchangeBinds { 276 | if eb.Exch == nil { 277 | return errors.New("MQ: Nil exchange found.") 278 | } 279 | if len(eb.Bindings) <= 0 { 280 | return fmt.Errorf("MQ: No bindings queue found for exchange(%s)", eb.Exch.Name) 281 | } 282 | // declare exchange 283 | ex := eb.Exch 284 | if err = ch.ExchangeDeclare(ex.Name, ex.Kind, ex.Durable, ex.AutoDelete, ex.Internal, ex.NoWait, ex.Args); err != nil { 285 | return fmt.Errorf("MQ: Declare exchange(%s) failed, %v", ex.Name, err) 286 | } 287 | // declare and bind queues 288 | for _, b := range eb.Bindings { 289 | if b == nil { 290 | return fmt.Errorf("MQ: Nil binding found, exchange:%s", ex.Name) 291 | } 292 | if len(b.Queues) <= 0 { 293 | return fmt.Errorf("MQ: No queues found for exchange(%s)", ex.Name) 294 | } 295 | for _, q := range b.Queues { 296 | if q == nil { 297 | return fmt.Errorf("MQ: Nil queue found, exchange:%s", ex.Name) 298 | } 299 | if _, err = ch.QueueDeclare(q.Name, q.Durable, q.AutoDelete, q.Exclusive, q.NoWait, q.Args); err != nil { 300 | return fmt.Errorf("MQ: Declare queue(%s) failed, %v", q.Name, err) 301 | } 302 | if err = ch.QueueBind(q.Name, b.RouteKey, ex.Name, b.NoWait, b.Args); err != nil { 303 | return fmt.Errorf("MQ: Bind exchange(%s) <--> queue(%s) failed, %v", ex.Name, q.Name, err) 304 | } 305 | } 306 | } 307 | } 308 | return nil 309 | } 310 | 311 | type confirmHelper struct { 312 | mutex sync.RWMutex 313 | listeners map[uint64]chan<- bool 314 | count uint64 315 | } 316 | 317 | func newConfirmHelper() *confirmHelper { 318 | h := confirmHelper{} 319 | return h.Reset() 320 | } 321 | 322 | func (h *confirmHelper) Reset() *confirmHelper { 323 | h.mutex.Lock() 324 | defer h.mutex.Unlock() 325 | 326 | // 解除所有等待listener返回ACK的阻塞的地方 327 | for _, ch := range h.listeners { 328 | close(ch) 329 | } 330 | 331 | // Reset 332 | h.count = uint64(0) 333 | h.listeners = make(map[uint64]chan<- bool) 334 | return h 335 | } 336 | 337 | func (h *confirmHelper) Listen(ch chan<- bool) { 338 | h.mutex.Lock() 339 | h.count++ 340 | h.listeners[h.count] = ch 341 | h.mutex.Unlock() 342 | } 343 | 344 | func (h *confirmHelper) Callback(idx uint64, ack bool) { 345 | h.mutex.Lock() 346 | ch := h.listeners[idx] 347 | delete(h.listeners, idx) 348 | h.mutex.Unlock() 349 | ch <- ack 350 | } 351 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/write.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "bufio" 10 | "bytes" 11 | "encoding/binary" 12 | "errors" 13 | "io" 14 | "math" 15 | "time" 16 | ) 17 | 18 | func (w *writer) WriteFrame(frame frame) (err error) { 19 | if err = frame.write(w.w); err != nil { 20 | return 21 | } 22 | 23 | if buf, ok := w.w.(*bufio.Writer); ok { 24 | err = buf.Flush() 25 | } 26 | 27 | return 28 | } 29 | 30 | func (f *methodFrame) write(w io.Writer) (err error) { 31 | var payload bytes.Buffer 32 | 33 | if f.Method == nil { 34 | return errors.New("malformed frame: missing method") 35 | } 36 | 37 | class, method := f.Method.id() 38 | 39 | if err = binary.Write(&payload, binary.BigEndian, class); err != nil { 40 | return 41 | } 42 | 43 | if err = binary.Write(&payload, binary.BigEndian, method); err != nil { 44 | return 45 | } 46 | 47 | if err = f.Method.write(&payload); err != nil { 48 | return 49 | } 50 | 51 | return writeFrame(w, frameMethod, f.ChannelId, payload.Bytes()) 52 | } 53 | 54 | // Heartbeat 55 | // 56 | // Payload is empty 57 | func (f *heartbeatFrame) write(w io.Writer) (err error) { 58 | return writeFrame(w, frameHeartbeat, f.ChannelId, []byte{}) 59 | } 60 | 61 | // CONTENT HEADER 62 | // 0 2 4 12 14 63 | // +----------+--------+-----------+----------------+------------- - - 64 | // | class-id | weight | body size | property flags | property list... 65 | // +----------+--------+-----------+----------------+------------- - - 66 | // short short long long short remainder... 67 | // 68 | func (f *headerFrame) write(w io.Writer) (err error) { 69 | var payload bytes.Buffer 70 | var zeroTime time.Time 71 | 72 | if err = binary.Write(&payload, binary.BigEndian, f.ClassId); err != nil { 73 | return 74 | } 75 | 76 | if err = binary.Write(&payload, binary.BigEndian, f.weight); err != nil { 77 | return 78 | } 79 | 80 | if err = binary.Write(&payload, binary.BigEndian, f.Size); err != nil { 81 | return 82 | } 83 | 84 | // First pass will build the mask to be serialized, second pass will serialize 85 | // each of the fields that appear in the mask. 86 | 87 | var mask uint16 88 | 89 | if len(f.Properties.ContentType) > 0 { 90 | mask = mask | flagContentType 91 | } 92 | if len(f.Properties.ContentEncoding) > 0 { 93 | mask = mask | flagContentEncoding 94 | } 95 | if f.Properties.Headers != nil && len(f.Properties.Headers) > 0 { 96 | mask = mask | flagHeaders 97 | } 98 | if f.Properties.DeliveryMode > 0 { 99 | mask = mask | flagDeliveryMode 100 | } 101 | if f.Properties.Priority > 0 { 102 | mask = mask | flagPriority 103 | } 104 | if len(f.Properties.CorrelationId) > 0 { 105 | mask = mask | flagCorrelationId 106 | } 107 | if len(f.Properties.ReplyTo) > 0 { 108 | mask = mask | flagReplyTo 109 | } 110 | if len(f.Properties.Expiration) > 0 { 111 | mask = mask | flagExpiration 112 | } 113 | if len(f.Properties.MessageId) > 0 { 114 | mask = mask | flagMessageId 115 | } 116 | if f.Properties.Timestamp != zeroTime { 117 | mask = mask | flagTimestamp 118 | } 119 | if len(f.Properties.Type) > 0 { 120 | mask = mask | flagType 121 | } 122 | if len(f.Properties.UserId) > 0 { 123 | mask = mask | flagUserId 124 | } 125 | if len(f.Properties.AppId) > 0 { 126 | mask = mask | flagAppId 127 | } 128 | 129 | if err = binary.Write(&payload, binary.BigEndian, mask); err != nil { 130 | return 131 | } 132 | 133 | if hasProperty(mask, flagContentType) { 134 | if err = writeShortstr(&payload, f.Properties.ContentType); err != nil { 135 | return 136 | } 137 | } 138 | if hasProperty(mask, flagContentEncoding) { 139 | if err = writeShortstr(&payload, f.Properties.ContentEncoding); err != nil { 140 | return 141 | } 142 | } 143 | if hasProperty(mask, flagHeaders) { 144 | if err = writeTable(&payload, f.Properties.Headers); err != nil { 145 | return 146 | } 147 | } 148 | if hasProperty(mask, flagDeliveryMode) { 149 | if err = binary.Write(&payload, binary.BigEndian, f.Properties.DeliveryMode); err != nil { 150 | return 151 | } 152 | } 153 | if hasProperty(mask, flagPriority) { 154 | if err = binary.Write(&payload, binary.BigEndian, f.Properties.Priority); err != nil { 155 | return 156 | } 157 | } 158 | if hasProperty(mask, flagCorrelationId) { 159 | if err = writeShortstr(&payload, f.Properties.CorrelationId); err != nil { 160 | return 161 | } 162 | } 163 | if hasProperty(mask, flagReplyTo) { 164 | if err = writeShortstr(&payload, f.Properties.ReplyTo); err != nil { 165 | return 166 | } 167 | } 168 | if hasProperty(mask, flagExpiration) { 169 | if err = writeShortstr(&payload, f.Properties.Expiration); err != nil { 170 | return 171 | } 172 | } 173 | if hasProperty(mask, flagMessageId) { 174 | if err = writeShortstr(&payload, f.Properties.MessageId); err != nil { 175 | return 176 | } 177 | } 178 | if hasProperty(mask, flagTimestamp) { 179 | if err = binary.Write(&payload, binary.BigEndian, uint64(f.Properties.Timestamp.Unix())); err != nil { 180 | return 181 | } 182 | } 183 | if hasProperty(mask, flagType) { 184 | if err = writeShortstr(&payload, f.Properties.Type); err != nil { 185 | return 186 | } 187 | } 188 | if hasProperty(mask, flagUserId) { 189 | if err = writeShortstr(&payload, f.Properties.UserId); err != nil { 190 | return 191 | } 192 | } 193 | if hasProperty(mask, flagAppId) { 194 | if err = writeShortstr(&payload, f.Properties.AppId); err != nil { 195 | return 196 | } 197 | } 198 | 199 | return writeFrame(w, frameHeader, f.ChannelId, payload.Bytes()) 200 | } 201 | 202 | // Body 203 | // 204 | // Payload is one byterange from the full body who's size is declared in the 205 | // Header frame 206 | func (f *bodyFrame) write(w io.Writer) (err error) { 207 | return writeFrame(w, frameBody, f.ChannelId, f.Body) 208 | } 209 | 210 | func writeFrame(w io.Writer, typ uint8, channel uint16, payload []byte) (err error) { 211 | end := []byte{frameEnd} 212 | size := uint(len(payload)) 213 | 214 | _, err = w.Write([]byte{ 215 | byte(typ), 216 | byte((channel & 0xff00) >> 8), 217 | byte((channel & 0x00ff) >> 0), 218 | byte((size & 0xff000000) >> 24), 219 | byte((size & 0x00ff0000) >> 16), 220 | byte((size & 0x0000ff00) >> 8), 221 | byte((size & 0x000000ff) >> 0), 222 | }) 223 | 224 | if err != nil { 225 | return 226 | } 227 | 228 | if _, err = w.Write(payload); err != nil { 229 | return 230 | } 231 | 232 | if _, err = w.Write(end); err != nil { 233 | return 234 | } 235 | 236 | return 237 | } 238 | 239 | func writeShortstr(w io.Writer, s string) (err error) { 240 | b := []byte(s) 241 | 242 | var length = uint8(len(b)) 243 | 244 | if err = binary.Write(w, binary.BigEndian, length); err != nil { 245 | return 246 | } 247 | 248 | if _, err = w.Write(b[:length]); err != nil { 249 | return 250 | } 251 | 252 | return 253 | } 254 | 255 | func writeLongstr(w io.Writer, s string) (err error) { 256 | b := []byte(s) 257 | 258 | var length = uint32(len(b)) 259 | 260 | if err = binary.Write(w, binary.BigEndian, length); err != nil { 261 | return 262 | } 263 | 264 | if _, err = w.Write(b[:length]); err != nil { 265 | return 266 | } 267 | 268 | return 269 | } 270 | 271 | /* 272 | 'A': []interface{} 273 | 'D': Decimal 274 | 'F': Table 275 | 'I': int32 276 | 'S': string 277 | 'T': time.Time 278 | 'V': nil 279 | 'b': byte 280 | 'd': float64 281 | 'f': float32 282 | 'l': int64 283 | 's': int16 284 | 't': bool 285 | 'x': []byte 286 | */ 287 | func writeField(w io.Writer, value interface{}) (err error) { 288 | var buf [9]byte 289 | var enc []byte 290 | 291 | switch v := value.(type) { 292 | case bool: 293 | buf[0] = 't' 294 | if v { 295 | buf[1] = byte(1) 296 | } else { 297 | buf[1] = byte(0) 298 | } 299 | enc = buf[:2] 300 | 301 | case byte: 302 | buf[0] = 'b' 303 | buf[1] = byte(v) 304 | enc = buf[:2] 305 | 306 | case int16: 307 | buf[0] = 's' 308 | binary.BigEndian.PutUint16(buf[1:3], uint16(v)) 309 | enc = buf[:3] 310 | 311 | case int32: 312 | buf[0] = 'I' 313 | binary.BigEndian.PutUint32(buf[1:5], uint32(v)) 314 | enc = buf[:5] 315 | 316 | case int64: 317 | buf[0] = 'l' 318 | binary.BigEndian.PutUint64(buf[1:9], uint64(v)) 319 | enc = buf[:9] 320 | 321 | case float32: 322 | buf[0] = 'f' 323 | binary.BigEndian.PutUint32(buf[1:5], math.Float32bits(v)) 324 | enc = buf[:5] 325 | 326 | case float64: 327 | buf[0] = 'd' 328 | binary.BigEndian.PutUint64(buf[1:9], math.Float64bits(v)) 329 | enc = buf[:9] 330 | 331 | case Decimal: 332 | buf[0] = 'D' 333 | buf[1] = byte(v.Scale) 334 | binary.BigEndian.PutUint32(buf[2:6], uint32(v.Value)) 335 | enc = buf[:6] 336 | 337 | case string: 338 | buf[0] = 'S' 339 | binary.BigEndian.PutUint32(buf[1:5], uint32(len(v))) 340 | enc = append(buf[:5], []byte(v)...) 341 | 342 | case []interface{}: // field-array 343 | buf[0] = 'A' 344 | 345 | sec := new(bytes.Buffer) 346 | for _, val := range v { 347 | if err = writeField(sec, val); err != nil { 348 | return 349 | } 350 | } 351 | 352 | binary.BigEndian.PutUint32(buf[1:5], uint32(sec.Len())) 353 | if _, err = w.Write(buf[:5]); err != nil { 354 | return 355 | } 356 | 357 | if _, err = w.Write(sec.Bytes()); err != nil { 358 | return 359 | } 360 | 361 | return 362 | 363 | case time.Time: 364 | buf[0] = 'T' 365 | binary.BigEndian.PutUint64(buf[1:9], uint64(v.Unix())) 366 | enc = buf[:9] 367 | 368 | case Table: 369 | if _, err = w.Write([]byte{'F'}); err != nil { 370 | return 371 | } 372 | return writeTable(w, v) 373 | 374 | case []byte: 375 | buf[0] = 'x' 376 | binary.BigEndian.PutUint32(buf[1:5], uint32(len(v))) 377 | if _, err = w.Write(buf[0:5]); err != nil { 378 | return 379 | } 380 | if _, err = w.Write(v); err != nil { 381 | return 382 | } 383 | return 384 | 385 | case nil: 386 | buf[0] = 'V' 387 | enc = buf[:1] 388 | 389 | default: 390 | return ErrFieldType 391 | } 392 | 393 | _, err = w.Write(enc) 394 | 395 | return 396 | } 397 | 398 | func writeTable(w io.Writer, table Table) (err error) { 399 | var buf bytes.Buffer 400 | 401 | for key, val := range table { 402 | if err = writeShortstr(&buf, key); err != nil { 403 | return 404 | } 405 | if err = writeField(&buf, val); err != nil { 406 | return 407 | } 408 | } 409 | 410 | return writeLongstr(w, string(buf.Bytes())) 411 | } 412 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/read.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "bytes" 10 | "encoding/binary" 11 | "errors" 12 | "io" 13 | "time" 14 | ) 15 | 16 | /* 17 | Reads a frame from an input stream and returns an interface that can be cast into 18 | one of the following: 19 | 20 | methodFrame 21 | PropertiesFrame 22 | bodyFrame 23 | heartbeatFrame 24 | 25 | 2.3.5 frame Details 26 | 27 | All frames consist of a header (7 octets), a payload of arbitrary size, and a 28 | 'frame-end' octet that detects malformed frames: 29 | 30 | 0 1 3 7 size+7 size+8 31 | +------+---------+-------------+ +------------+ +-----------+ 32 | | type | channel | size | | payload | | frame-end | 33 | +------+---------+-------------+ +------------+ +-----------+ 34 | octet short long size octets octet 35 | 36 | To read a frame, we: 37 | 1. Read the header and check the frame type and channel. 38 | 2. Depending on the frame type, we read the payload and process it. 39 | 3. Read the frame end octet. 40 | 41 | In realistic implementations where performance is a concern, we would use 42 | “read-ahead buffering” or 43 | 44 | “gathering reads” to avoid doing three separate system calls to read a frame. 45 | */ 46 | func (r *reader) ReadFrame() (frame frame, err error) { 47 | var scratch [7]byte 48 | 49 | if _, err = io.ReadFull(r.r, scratch[:7]); err != nil { 50 | return 51 | } 52 | 53 | typ := uint8(scratch[0]) 54 | channel := binary.BigEndian.Uint16(scratch[1:3]) 55 | size := binary.BigEndian.Uint32(scratch[3:7]) 56 | 57 | switch typ { 58 | case frameMethod: 59 | if frame, err = r.parseMethodFrame(channel, size); err != nil { 60 | return 61 | } 62 | 63 | case frameHeader: 64 | if frame, err = r.parseHeaderFrame(channel, size); err != nil { 65 | return 66 | } 67 | 68 | case frameBody: 69 | if frame, err = r.parseBodyFrame(channel, size); err != nil { 70 | return nil, err 71 | } 72 | 73 | case frameHeartbeat: 74 | if frame, err = r.parseHeartbeatFrame(channel, size); err != nil { 75 | return 76 | } 77 | 78 | default: 79 | return nil, ErrFrame 80 | } 81 | 82 | if _, err = io.ReadFull(r.r, scratch[:1]); err != nil { 83 | return nil, err 84 | } 85 | 86 | if scratch[0] != frameEnd { 87 | return nil, ErrFrame 88 | } 89 | 90 | return 91 | } 92 | 93 | func readShortstr(r io.Reader) (v string, err error) { 94 | var length uint8 95 | if err = binary.Read(r, binary.BigEndian, &length); err != nil { 96 | return 97 | } 98 | 99 | bytes := make([]byte, length) 100 | if _, err = io.ReadFull(r, bytes); err != nil { 101 | return 102 | } 103 | return string(bytes), nil 104 | } 105 | 106 | func readLongstr(r io.Reader) (v string, err error) { 107 | var length uint32 108 | if err = binary.Read(r, binary.BigEndian, &length); err != nil { 109 | return 110 | } 111 | 112 | // slices can't be longer than max int32 value 113 | if length > (^uint32(0) >> 1) { 114 | return 115 | } 116 | 117 | bytes := make([]byte, length) 118 | if _, err = io.ReadFull(r, bytes); err != nil { 119 | return 120 | } 121 | return string(bytes), nil 122 | } 123 | 124 | func readDecimal(r io.Reader) (v Decimal, err error) { 125 | if err = binary.Read(r, binary.BigEndian, &v.Scale); err != nil { 126 | return 127 | } 128 | if err = binary.Read(r, binary.BigEndian, &v.Value); err != nil { 129 | return 130 | } 131 | return 132 | } 133 | 134 | func readFloat32(r io.Reader) (v float32, err error) { 135 | if err = binary.Read(r, binary.BigEndian, &v); err != nil { 136 | return 137 | } 138 | return 139 | } 140 | 141 | func readFloat64(r io.Reader) (v float64, err error) { 142 | if err = binary.Read(r, binary.BigEndian, &v); err != nil { 143 | return 144 | } 145 | return 146 | } 147 | 148 | func readTimestamp(r io.Reader) (v time.Time, err error) { 149 | var sec int64 150 | if err = binary.Read(r, binary.BigEndian, &sec); err != nil { 151 | return 152 | } 153 | return time.Unix(sec, 0), nil 154 | } 155 | 156 | /* 157 | 'A': []interface{} 158 | 'D': Decimal 159 | 'F': Table 160 | 'I': int32 161 | 'S': string 162 | 'T': time.Time 163 | 'V': nil 164 | 'b': byte 165 | 'd': float64 166 | 'f': float32 167 | 'l': int64 168 | 's': int16 169 | 't': bool 170 | 'x': []byte 171 | */ 172 | func readField(r io.Reader) (v interface{}, err error) { 173 | var typ byte 174 | if err = binary.Read(r, binary.BigEndian, &typ); err != nil { 175 | return 176 | } 177 | 178 | switch typ { 179 | case 't': 180 | var value uint8 181 | if err = binary.Read(r, binary.BigEndian, &value); err != nil { 182 | return 183 | } 184 | return (value != 0), nil 185 | 186 | case 'b': 187 | var value [1]byte 188 | if _, err = io.ReadFull(r, value[0:1]); err != nil { 189 | return 190 | } 191 | return value[0], nil 192 | 193 | case 's': 194 | var value int16 195 | if err = binary.Read(r, binary.BigEndian, &value); err != nil { 196 | return 197 | } 198 | return value, nil 199 | 200 | case 'I': 201 | var value int32 202 | if err = binary.Read(r, binary.BigEndian, &value); err != nil { 203 | return 204 | } 205 | return value, nil 206 | 207 | case 'l': 208 | var value int64 209 | if err = binary.Read(r, binary.BigEndian, &value); err != nil { 210 | return 211 | } 212 | return value, nil 213 | 214 | case 'f': 215 | var value float32 216 | if err = binary.Read(r, binary.BigEndian, &value); err != nil { 217 | return 218 | } 219 | return value, nil 220 | 221 | case 'd': 222 | var value float64 223 | if err = binary.Read(r, binary.BigEndian, &value); err != nil { 224 | return 225 | } 226 | return value, nil 227 | 228 | case 'D': 229 | return readDecimal(r) 230 | 231 | case 'S': 232 | return readLongstr(r) 233 | 234 | case 'A': 235 | return readArray(r) 236 | 237 | case 'T': 238 | return readTimestamp(r) 239 | 240 | case 'F': 241 | return readTable(r) 242 | 243 | case 'x': 244 | var len int32 245 | if err = binary.Read(r, binary.BigEndian, &len); err != nil { 246 | return nil, err 247 | } 248 | 249 | value := make([]byte, len) 250 | if _, err = io.ReadFull(r, value); err != nil { 251 | return nil, err 252 | } 253 | return value, err 254 | 255 | case 'V': 256 | return nil, nil 257 | } 258 | 259 | return nil, ErrSyntax 260 | } 261 | 262 | /* 263 | Field tables are long strings that contain packed name-value pairs. The 264 | name-value pairs are encoded as short string defining the name, and octet 265 | defining the values type and then the value itself. The valid field types for 266 | tables are an extension of the native integer, bit, string, and timestamp 267 | types, and are shown in the grammar. Multi-octet integer fields are always 268 | held in network byte order. 269 | */ 270 | func readTable(r io.Reader) (table Table, err error) { 271 | var nested bytes.Buffer 272 | var str string 273 | 274 | if str, err = readLongstr(r); err != nil { 275 | return 276 | } 277 | 278 | nested.Write([]byte(str)) 279 | 280 | table = make(Table) 281 | 282 | for nested.Len() > 0 { 283 | var key string 284 | var value interface{} 285 | 286 | if key, err = readShortstr(&nested); err != nil { 287 | return 288 | } 289 | 290 | if value, err = readField(&nested); err != nil { 291 | return 292 | } 293 | 294 | table[key] = value 295 | } 296 | 297 | return 298 | } 299 | 300 | func readArray(r io.Reader) ([]interface{}, error) { 301 | var ( 302 | size uint32 303 | err error 304 | ) 305 | 306 | if err = binary.Read(r, binary.BigEndian, &size); err != nil { 307 | return nil, err 308 | } 309 | 310 | var ( 311 | lim = &io.LimitedReader{R: r, N: int64(size)} 312 | arr = []interface{}{} 313 | field interface{} 314 | ) 315 | 316 | for { 317 | if field, err = readField(lim); err != nil { 318 | if err == io.EOF { 319 | break 320 | } 321 | return nil, err 322 | } 323 | arr = append(arr, field) 324 | } 325 | 326 | return arr, nil 327 | } 328 | 329 | // Checks if this bit mask matches the flags bitset 330 | func hasProperty(mask uint16, prop int) bool { 331 | return int(mask)&prop > 0 332 | } 333 | 334 | func (r *reader) parseHeaderFrame(channel uint16, size uint32) (frame frame, err error) { 335 | hf := &headerFrame{ 336 | ChannelId: channel, 337 | } 338 | 339 | if err = binary.Read(r.r, binary.BigEndian, &hf.ClassId); err != nil { 340 | return 341 | } 342 | 343 | if err = binary.Read(r.r, binary.BigEndian, &hf.weight); err != nil { 344 | return 345 | } 346 | 347 | if err = binary.Read(r.r, binary.BigEndian, &hf.Size); err != nil { 348 | return 349 | } 350 | 351 | var flags uint16 352 | 353 | if err = binary.Read(r.r, binary.BigEndian, &flags); err != nil { 354 | return 355 | } 356 | 357 | if hasProperty(flags, flagContentType) { 358 | if hf.Properties.ContentType, err = readShortstr(r.r); err != nil { 359 | return 360 | } 361 | } 362 | if hasProperty(flags, flagContentEncoding) { 363 | if hf.Properties.ContentEncoding, err = readShortstr(r.r); err != nil { 364 | return 365 | } 366 | } 367 | if hasProperty(flags, flagHeaders) { 368 | if hf.Properties.Headers, err = readTable(r.r); err != nil { 369 | return 370 | } 371 | } 372 | if hasProperty(flags, flagDeliveryMode) { 373 | if err = binary.Read(r.r, binary.BigEndian, &hf.Properties.DeliveryMode); err != nil { 374 | return 375 | } 376 | } 377 | if hasProperty(flags, flagPriority) { 378 | if err = binary.Read(r.r, binary.BigEndian, &hf.Properties.Priority); err != nil { 379 | return 380 | } 381 | } 382 | if hasProperty(flags, flagCorrelationId) { 383 | if hf.Properties.CorrelationId, err = readShortstr(r.r); err != nil { 384 | return 385 | } 386 | } 387 | if hasProperty(flags, flagReplyTo) { 388 | if hf.Properties.ReplyTo, err = readShortstr(r.r); err != nil { 389 | return 390 | } 391 | } 392 | if hasProperty(flags, flagExpiration) { 393 | if hf.Properties.Expiration, err = readShortstr(r.r); err != nil { 394 | return 395 | } 396 | } 397 | if hasProperty(flags, flagMessageId) { 398 | if hf.Properties.MessageId, err = readShortstr(r.r); err != nil { 399 | return 400 | } 401 | } 402 | if hasProperty(flags, flagTimestamp) { 403 | if hf.Properties.Timestamp, err = readTimestamp(r.r); err != nil { 404 | return 405 | } 406 | } 407 | if hasProperty(flags, flagType) { 408 | if hf.Properties.Type, err = readShortstr(r.r); err != nil { 409 | return 410 | } 411 | } 412 | if hasProperty(flags, flagUserId) { 413 | if hf.Properties.UserId, err = readShortstr(r.r); err != nil { 414 | return 415 | } 416 | } 417 | if hasProperty(flags, flagAppId) { 418 | if hf.Properties.AppId, err = readShortstr(r.r); err != nil { 419 | return 420 | } 421 | } 422 | if hasProperty(flags, flagReserved1) { 423 | if hf.Properties.reserved1, err = readShortstr(r.r); err != nil { 424 | return 425 | } 426 | } 427 | 428 | return hf, nil 429 | } 430 | 431 | func (r *reader) parseBodyFrame(channel uint16, size uint32) (frame frame, err error) { 432 | bf := &bodyFrame{ 433 | ChannelId: channel, 434 | Body: make([]byte, size), 435 | } 436 | 437 | if _, err = io.ReadFull(r.r, bf.Body); err != nil { 438 | return nil, err 439 | } 440 | 441 | return bf, nil 442 | } 443 | 444 | var errHeartbeatPayload = errors.New("Heartbeats should not have a payload") 445 | 446 | func (r *reader) parseHeartbeatFrame(channel uint16, size uint32) (frame frame, err error) { 447 | hf := &heartbeatFrame{ 448 | ChannelId: channel, 449 | } 450 | 451 | if size > 0 { 452 | return nil, errHeartbeatPayload 453 | } 454 | 455 | return hf, nil 456 | } 457 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "fmt" 10 | "io" 11 | "time" 12 | ) 13 | 14 | // Constants for standard AMQP 0-9-1 exchange types. 15 | const ( 16 | ExchangeDirect = "direct" 17 | ExchangeFanout = "fanout" 18 | ExchangeTopic = "topic" 19 | ExchangeHeaders = "headers" 20 | ) 21 | 22 | var ( 23 | // ErrClosed is returned when the channel or connection is not open 24 | ErrClosed = &Error{Code: ChannelError, Reason: "channel/connection is not open"} 25 | 26 | // ErrChannelMax is returned when Connection.Channel has been called enough 27 | // times that all channel IDs have been exhausted in the client or the 28 | // server. 29 | ErrChannelMax = &Error{Code: ChannelError, Reason: "channel id space exhausted"} 30 | 31 | // ErrSASL is returned from Dial when the authentication mechanism could not 32 | // be negoated. 33 | ErrSASL = &Error{Code: AccessRefused, Reason: "SASL could not negotiate a shared mechanism"} 34 | 35 | // ErrCredentials is returned when the authenticated client is not authorized 36 | // to any vhost. 37 | ErrCredentials = &Error{Code: AccessRefused, Reason: "username or password not allowed"} 38 | 39 | // ErrVhost is returned when the authenticated user is not permitted to 40 | // access the requested Vhost. 41 | ErrVhost = &Error{Code: AccessRefused, Reason: "no access to this vhost"} 42 | 43 | // ErrSyntax is hard protocol error, indicating an unsupported protocol, 44 | // implementation or encoding. 45 | ErrSyntax = &Error{Code: SyntaxError, Reason: "invalid field or value inside of a frame"} 46 | 47 | // ErrFrame is returned when the protocol frame cannot be read from the 48 | // server, indicating an unsupported protocol or unsupported frame type. 49 | ErrFrame = &Error{Code: FrameError, Reason: "frame could not be parsed"} 50 | 51 | // ErrCommandInvalid is returned when the server sends an unexpected response 52 | // to this requested message type. This indicates a bug in this client. 53 | ErrCommandInvalid = &Error{Code: CommandInvalid, Reason: "unexpected command received"} 54 | 55 | // ErrUnexpectedFrame is returned when something other than a method or 56 | // heartbeat frame is delivered to the Connection, indicating a bug in the 57 | // client. 58 | ErrUnexpectedFrame = &Error{Code: UnexpectedFrame, Reason: "unexpected frame received"} 59 | 60 | // ErrFieldType is returned when writing a message containing a Go type unsupported by AMQP. 61 | ErrFieldType = &Error{Code: SyntaxError, Reason: "unsupported table field type"} 62 | ) 63 | 64 | // Error captures the code and reason a channel or connection has been closed 65 | // by the server. 66 | type Error struct { 67 | Code int // constant code from the specification 68 | Reason string // description of the error 69 | Server bool // true when initiated from the server, false when from this library 70 | Recover bool // true when this error can be recovered by retrying later or with different parameters 71 | } 72 | 73 | func newError(code uint16, text string) *Error { 74 | return &Error{ 75 | Code: int(code), 76 | Reason: text, 77 | Recover: isSoftExceptionCode(int(code)), 78 | Server: true, 79 | } 80 | } 81 | 82 | func (e Error) Error() string { 83 | return fmt.Sprintf("Exception (%d) Reason: %q", e.Code, e.Reason) 84 | } 85 | 86 | // Used by header frames to capture routing and header information 87 | type properties struct { 88 | ContentType string // MIME content type 89 | ContentEncoding string // MIME content encoding 90 | Headers Table // Application or header exchange table 91 | DeliveryMode uint8 // queue implementation use - Transient (1) or Persistent (2) 92 | Priority uint8 // queue implementation use - 0 to 9 93 | CorrelationId string // application use - correlation identifier 94 | ReplyTo string // application use - address to to reply to (ex: RPC) 95 | Expiration string // implementation use - message expiration spec 96 | MessageId string // application use - message identifier 97 | Timestamp time.Time // application use - message timestamp 98 | Type string // application use - message type name 99 | UserId string // application use - creating user id 100 | AppId string // application use - creating application 101 | reserved1 string // was cluster-id - process for buffer consumption 102 | } 103 | 104 | // DeliveryMode. Transient means higher throughput but messages will not be 105 | // restored on broker restart. The delivery mode of publishings is unrelated 106 | // to the durability of the queues they reside on. Transient messages will 107 | // not be restored to durable queues, persistent messages will be restored to 108 | // durable queues and lost on non-durable queues during server restart. 109 | // 110 | // This remains typed as uint8 to match Publishing.DeliveryMode. Other 111 | // delivery modes specific to custom queue implementations are not enumerated 112 | // here. 113 | const ( 114 | Transient uint8 = 1 115 | Persistent uint8 = 2 116 | ) 117 | 118 | // The property flags are an array of bits that indicate the presence or 119 | // absence of each property value in sequence. The bits are ordered from most 120 | // high to low - bit 15 indicates the first property. 121 | const ( 122 | flagContentType = 0x8000 123 | flagContentEncoding = 0x4000 124 | flagHeaders = 0x2000 125 | flagDeliveryMode = 0x1000 126 | flagPriority = 0x0800 127 | flagCorrelationId = 0x0400 128 | flagReplyTo = 0x0200 129 | flagExpiration = 0x0100 130 | flagMessageId = 0x0080 131 | flagTimestamp = 0x0040 132 | flagType = 0x0020 133 | flagUserId = 0x0010 134 | flagAppId = 0x0008 135 | flagReserved1 = 0x0004 136 | ) 137 | 138 | // Queue captures the current server state of the queue on the server returned 139 | // from Channel.QueueDeclare or Channel.QueueInspect. 140 | type Queue struct { 141 | Name string // server confirmed or generated name 142 | Messages int // count of messages not awaiting acknowledgment 143 | Consumers int // number of consumers receiving deliveries 144 | } 145 | 146 | // Publishing captures the client message sent to the server. The fields 147 | // outside of the Headers table included in this struct mirror the underlying 148 | // fields in the content frame. They use native types for convenience and 149 | // efficiency. 150 | type Publishing struct { 151 | // Application or exchange specific fields, 152 | // the headers exchange will inspect this field. 153 | Headers Table 154 | 155 | // Properties 156 | ContentType string // MIME content type 157 | ContentEncoding string // MIME content encoding 158 | DeliveryMode uint8 // Transient (0 or 1) or Persistent (2) 159 | Priority uint8 // 0 to 9 160 | CorrelationId string // correlation identifier 161 | ReplyTo string // address to to reply to (ex: RPC) 162 | Expiration string // message expiration spec 163 | MessageId string // message identifier 164 | Timestamp time.Time // message timestamp 165 | Type string // message type name 166 | UserId string // creating user id - ex: "guest" 167 | AppId string // creating application id 168 | 169 | // The application specific payload of the message 170 | Body []byte 171 | } 172 | 173 | // Blocking notifies the server's TCP flow control of the Connection. When a 174 | // server hits a memory or disk alarm it will block all connections until the 175 | // resources are reclaimed. Use NotifyBlock on the Connection to receive these 176 | // events. 177 | type Blocking struct { 178 | Active bool // TCP pushback active/inactive on server 179 | Reason string // Server reason for activation 180 | } 181 | 182 | // Confirmation notifies the acknowledgment or negative acknowledgement of a 183 | // publishing identified by its delivery tag. Use NotifyPublish on the Channel 184 | // to consume these events. 185 | type Confirmation struct { 186 | DeliveryTag uint64 // A 1 based counter of publishings from when the channel was put in Confirm mode 187 | Ack bool // True when the server successfully received the publishing 188 | } 189 | 190 | // Decimal matches the AMQP decimal type. Scale is the number of decimal 191 | // digits Scale == 2, Value == 12345, Decimal == 123.45 192 | type Decimal struct { 193 | Scale uint8 194 | Value int32 195 | } 196 | 197 | // Table stores user supplied fields of the following types: 198 | // 199 | // bool 200 | // byte 201 | // float32 202 | // float64 203 | // int16 204 | // int32 205 | // int64 206 | // nil 207 | // string 208 | // time.Time 209 | // amqp.Decimal 210 | // amqp.Table 211 | // []byte 212 | // []interface{} - containing above types 213 | // 214 | // Functions taking a table will immediately fail when the table contains a 215 | // value of an unsupported type. 216 | // 217 | // The caller must be specific in which precision of integer it wishes to 218 | // encode. 219 | // 220 | // Use a type assertion when reading values from a table for type conversion. 221 | // 222 | // RabbitMQ expects int32 for integer values. 223 | // 224 | type Table map[string]interface{} 225 | 226 | func validateField(f interface{}) error { 227 | switch fv := f.(type) { 228 | case nil, bool, byte, int16, int32, int64, float32, float64, string, []byte, Decimal, time.Time: 229 | return nil 230 | 231 | case []interface{}: 232 | for _, v := range fv { 233 | if err := validateField(v); err != nil { 234 | return fmt.Errorf("in array %s", err) 235 | } 236 | } 237 | return nil 238 | 239 | case Table: 240 | for k, v := range fv { 241 | if err := validateField(v); err != nil { 242 | return fmt.Errorf("table field %q %s", k, err) 243 | } 244 | } 245 | return nil 246 | } 247 | 248 | return fmt.Errorf("value %t not supported", f) 249 | } 250 | 251 | // Validate returns and error if any Go types in the table are incompatible with AMQP types. 252 | func (t Table) Validate() error { 253 | return validateField(t) 254 | } 255 | 256 | // Heap interface for maintaining delivery tags 257 | type tagSet []uint64 258 | 259 | func (set tagSet) Len() int { return len(set) } 260 | func (set tagSet) Less(i, j int) bool { return (set)[i] < (set)[j] } 261 | func (set tagSet) Swap(i, j int) { (set)[i], (set)[j] = (set)[j], (set)[i] } 262 | func (set *tagSet) Push(tag interface{}) { *set = append(*set, tag.(uint64)) } 263 | func (set *tagSet) Pop() interface{} { 264 | val := (*set)[len(*set)-1] 265 | *set = (*set)[:len(*set)-1] 266 | return val 267 | } 268 | 269 | type message interface { 270 | id() (uint16, uint16) 271 | wait() bool 272 | read(io.Reader) error 273 | write(io.Writer) error 274 | } 275 | 276 | type messageWithContent interface { 277 | message 278 | getContent() (properties, []byte) 279 | setContent(properties, []byte) 280 | } 281 | 282 | /* 283 | The base interface implemented as: 284 | 285 | 2.3.5 frame Details 286 | 287 | All frames consist of a header (7 octets), a payload of arbitrary size, and a 'frame-end' octet that detects 288 | malformed frames: 289 | 290 | 0 1 3 7 size+7 size+8 291 | +------+---------+-------------+ +------------+ +-----------+ 292 | | type | channel | size | | payload | | frame-end | 293 | +------+---------+-------------+ +------------+ +-----------+ 294 | octet short long size octets octet 295 | 296 | To read a frame, we: 297 | 298 | 1. Read the header and check the frame type and channel. 299 | 2. Depending on the frame type, we read the payload and process it. 300 | 3. Read the frame end octet. 301 | 302 | In realistic implementations where performance is a concern, we would use 303 | “read-ahead buffering” or “gathering reads” to avoid doing three separate 304 | system calls to read a frame. 305 | 306 | */ 307 | type frame interface { 308 | write(io.Writer) error 309 | channel() uint16 310 | } 311 | 312 | type reader struct { 313 | r io.Reader 314 | } 315 | 316 | type writer struct { 317 | w io.Writer 318 | } 319 | 320 | // Implements the frame interface for Connection RPC 321 | type protocolHeader struct{} 322 | 323 | func (protocolHeader) write(w io.Writer) error { 324 | _, err := w.Write([]byte{'A', 'M', 'Q', 'P', 0, 0, 9, 1}) 325 | return err 326 | } 327 | 328 | func (protocolHeader) channel() uint16 { 329 | panic("only valid as initial handshake") 330 | } 331 | 332 | /* 333 | Method frames carry the high-level protocol commands (which we call "methods"). 334 | One method frame carries one command. The method frame payload has this format: 335 | 336 | 0 2 4 337 | +----------+-----------+-------------- - - 338 | | class-id | method-id | arguments... 339 | +----------+-----------+-------------- - - 340 | short short ... 341 | 342 | To process a method frame, we: 343 | 1. Read the method frame payload. 344 | 2. Unpack it into a structure. A given method always has the same structure, 345 | so we can unpack the method rapidly. 3. Check that the method is allowed in 346 | the current context. 347 | 4. Check that the method arguments are valid. 348 | 5. Execute the method. 349 | 350 | Method frame bodies are constructed as a list of AMQP data fields (bits, 351 | integers, strings and string tables). The marshalling code is trivially 352 | generated directly from the protocol specifications, and can be very rapid. 353 | */ 354 | type methodFrame struct { 355 | ChannelId uint16 356 | ClassId uint16 357 | MethodId uint16 358 | Method message 359 | } 360 | 361 | func (f *methodFrame) channel() uint16 { return f.ChannelId } 362 | 363 | /* 364 | Heartbeating is a technique designed to undo one of TCP/IP's features, namely 365 | its ability to recover from a broken physical connection by closing only after 366 | a quite long time-out. In some scenarios we need to know very rapidly if a 367 | peer is disconnected or not responding for other reasons (e.g. it is looping). 368 | Since heartbeating can be done at a low level, we implement this as a special 369 | type of frame that peers exchange at the transport level, rather than as a 370 | class method. 371 | */ 372 | type heartbeatFrame struct { 373 | ChannelId uint16 374 | } 375 | 376 | func (f *heartbeatFrame) channel() uint16 { return f.ChannelId } 377 | 378 | /* 379 | Certain methods (such as Basic.Publish, Basic.Deliver, etc.) are formally 380 | defined as carrying content. When a peer sends such a method frame, it always 381 | follows it with a content header and zero or more content body frames. 382 | 383 | A content header frame has this format: 384 | 385 | 0 2 4 12 14 386 | +----------+--------+-----------+----------------+------------- - - 387 | | class-id | weight | body size | property flags | property list... 388 | +----------+--------+-----------+----------------+------------- - - 389 | short short long long short remainder... 390 | 391 | We place content body in distinct frames (rather than including it in the 392 | method) so that AMQP may support "zero copy" techniques in which content is 393 | never marshalled or encoded. We place the content properties in their own 394 | frame so that recipients can selectively discard contents they do not want to 395 | process 396 | */ 397 | type headerFrame struct { 398 | ChannelId uint16 399 | ClassId uint16 400 | weight uint16 401 | Size uint64 402 | Properties properties 403 | } 404 | 405 | func (f *headerFrame) channel() uint16 { return f.ChannelId } 406 | 407 | /* 408 | Content is the application data we carry from client-to-client via the AMQP 409 | server. Content is, roughly speaking, a set of properties plus a binary data 410 | part. The set of allowed properties are defined by the Basic class, and these 411 | form the "content header frame". The data can be any size, and MAY be broken 412 | into several (or many) chunks, each forming a "content body frame". 413 | 414 | Looking at the frames for a specific channel, as they pass on the wire, we 415 | might see something like this: 416 | 417 | [method] 418 | [method] [header] [body] [body] 419 | [method] 420 | ... 421 | */ 422 | type bodyFrame struct { 423 | ChannelId uint16 424 | Body []byte 425 | } 426 | 427 | func (f *bodyFrame) channel() uint16 { return f.ChannelId } 428 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/connection.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "bufio" 10 | "crypto/tls" 11 | "io" 12 | "net" 13 | "reflect" 14 | "strconv" 15 | "strings" 16 | "sync" 17 | "sync/atomic" 18 | "time" 19 | ) 20 | 21 | const ( 22 | maxChannelMax = (2 << 15) - 1 23 | 24 | defaultHeartbeat = 10 * time.Second 25 | defaultConnectionTimeout = 30 * time.Second 26 | defaultProduct = "https://github.com/streadway/amqp" 27 | defaultVersion = "β" 28 | defaultChannelMax = maxChannelMax 29 | defaultLocale = "en_US" 30 | ) 31 | 32 | // Config is used in DialConfig and Open to specify the desired tuning 33 | // parameters used during a connection open handshake. The negotiated tuning 34 | // will be stored in the returned connection's Config field. 35 | type Config struct { 36 | // The SASL mechanisms to try in the client request, and the successful 37 | // mechanism used on the Connection object. 38 | // If SASL is nil, PlainAuth from the URL is used. 39 | SASL []Authentication 40 | 41 | // Vhost specifies the namespace of permissions, exchanges, queues and 42 | // bindings on the server. Dial sets this to the path parsed from the URL. 43 | Vhost string 44 | 45 | ChannelMax int // 0 max channels means 2^16 - 1 46 | FrameSize int // 0 max bytes means unlimited 47 | Heartbeat time.Duration // less than 1s uses the server's interval 48 | 49 | // TLSClientConfig specifies the client configuration of the TLS connection 50 | // when establishing a tls transport. 51 | // If the URL uses an amqps scheme, then an empty tls.Config with the 52 | // ServerName from the URL is used. 53 | TLSClientConfig *tls.Config 54 | 55 | // Properties is table of properties that the client advertises to the server. 56 | // This is an optional setting - if the application does not set this, 57 | // the underlying library will use a generic set of client properties. 58 | Properties Table 59 | 60 | // Connection locale that we expect to always be en_US 61 | // Even though servers must return it as per the AMQP 0-9-1 spec, 62 | // we are not aware of it being used other than to satisfy the spec requirements 63 | Locale string 64 | 65 | // Dial returns a net.Conn prepared for a TLS handshake with TSLClientConfig, 66 | // then an AMQP connection handshake. 67 | // If Dial is nil, net.DialTimeout with a 30s connection and 30s deadline is 68 | // used during TLS and AMQP handshaking. 69 | Dial func(network, addr string) (net.Conn, error) 70 | } 71 | 72 | // Connection manages the serialization and deserialization of frames from IO 73 | // and dispatches the frames to the appropriate channel. All RPC methods and 74 | // asynchronous Publishing, Delivery, Ack, Nack and Return messages are 75 | // multiplexed on this channel. There must always be active receivers for 76 | // every asynchronous message on this connection. 77 | type Connection struct { 78 | destructor sync.Once // shutdown once 79 | sendM sync.Mutex // conn writer mutex 80 | m sync.Mutex // struct field mutex 81 | 82 | conn io.ReadWriteCloser 83 | 84 | rpc chan message 85 | writer *writer 86 | sends chan time.Time // timestamps of each frame sent 87 | deadlines chan readDeadliner // heartbeater updates read deadlines 88 | 89 | allocator *allocator // id generator valid after openTune 90 | channels map[uint16]*Channel 91 | 92 | noNotify bool // true when we will never notify again 93 | closes []chan *Error 94 | blocks []chan Blocking 95 | 96 | errors chan *Error 97 | 98 | Config Config // The negotiated Config after connection.open 99 | 100 | Major int // Server's major version 101 | Minor int // Server's minor version 102 | Properties Table // Server properties 103 | Locales []string // Server locales 104 | 105 | closed int32 // Will be 1 if the connection is closed, 0 otherwise. Should only be accessed as atomic 106 | } 107 | 108 | type readDeadliner interface { 109 | SetReadDeadline(time.Time) error 110 | } 111 | 112 | // defaultDial establishes a connection when config.Dial is not provided 113 | func defaultDial(network, addr string) (net.Conn, error) { 114 | conn, err := net.DialTimeout(network, addr, defaultConnectionTimeout) 115 | if err != nil { 116 | return nil, err 117 | } 118 | 119 | // Heartbeating hasn't started yet, don't stall forever on a dead server. 120 | // A deadline is set for TLS and AMQP handshaking. After AMQP is established, 121 | // the deadline is cleared in openComplete. 122 | if err := conn.SetDeadline(time.Now().Add(defaultConnectionTimeout)); err != nil { 123 | return nil, err 124 | } 125 | 126 | return conn, nil 127 | } 128 | 129 | // Dial accepts a string in the AMQP URI format and returns a new Connection 130 | // over TCP using PlainAuth. Defaults to a server heartbeat interval of 10 131 | // seconds and sets the handshake deadline to 30 seconds. After handshake, 132 | // deadlines are cleared. 133 | // 134 | // Dial uses the zero value of tls.Config when it encounters an amqps:// 135 | // scheme. It is equivalent to calling DialTLS(amqp, nil). 136 | func Dial(url string) (*Connection, error) { 137 | return DialConfig(url, Config{ 138 | Heartbeat: defaultHeartbeat, 139 | Locale: defaultLocale, 140 | }) 141 | } 142 | 143 | // DialTLS accepts a string in the AMQP URI format and returns a new Connection 144 | // over TCP using PlainAuth. Defaults to a server heartbeat interval of 10 145 | // seconds and sets the initial read deadline to 30 seconds. 146 | // 147 | // DialTLS uses the provided tls.Config when encountering an amqps:// scheme. 148 | func DialTLS(url string, amqps *tls.Config) (*Connection, error) { 149 | return DialConfig(url, Config{ 150 | Heartbeat: defaultHeartbeat, 151 | TLSClientConfig: amqps, 152 | Locale: defaultLocale, 153 | }) 154 | } 155 | 156 | // DialConfig accepts a string in the AMQP URI format and a configuration for 157 | // the transport and connection setup, returning a new Connection. Defaults to 158 | // a server heartbeat interval of 10 seconds and sets the initial read deadline 159 | // to 30 seconds. 160 | func DialConfig(url string, config Config) (*Connection, error) { 161 | var err error 162 | var conn net.Conn 163 | 164 | uri, err := ParseURI(url) 165 | if err != nil { 166 | return nil, err 167 | } 168 | 169 | if config.SASL == nil { 170 | config.SASL = []Authentication{uri.PlainAuth()} 171 | } 172 | 173 | if config.Vhost == "" { 174 | config.Vhost = uri.Vhost 175 | } 176 | 177 | addr := net.JoinHostPort(uri.Host, strconv.FormatInt(int64(uri.Port), 10)) 178 | 179 | dialer := config.Dial 180 | if dialer == nil { 181 | dialer = defaultDial 182 | } 183 | 184 | conn, err = dialer("tcp", addr) 185 | if err != nil { 186 | return nil, err 187 | } 188 | 189 | if uri.Scheme == "amqps" { 190 | if config.TLSClientConfig == nil { 191 | config.TLSClientConfig = new(tls.Config) 192 | } 193 | 194 | // If ServerName has not been specified in TLSClientConfig, 195 | // set it to the URI host used for this connection. 196 | if config.TLSClientConfig.ServerName == "" { 197 | config.TLSClientConfig.ServerName = uri.Host 198 | } 199 | 200 | client := tls.Client(conn, config.TLSClientConfig) 201 | if err := client.Handshake(); err != nil { 202 | conn.Close() 203 | return nil, err 204 | } 205 | 206 | conn = client 207 | } 208 | 209 | return Open(conn, config) 210 | } 211 | 212 | /* 213 | Open accepts an already established connection, or other io.ReadWriteCloser as 214 | a transport. Use this method if you have established a TLS connection or wish 215 | to use your own custom transport. 216 | 217 | */ 218 | func Open(conn io.ReadWriteCloser, config Config) (*Connection, error) { 219 | c := &Connection{ 220 | conn: conn, 221 | writer: &writer{bufio.NewWriter(conn)}, 222 | channels: make(map[uint16]*Channel), 223 | rpc: make(chan message), 224 | sends: make(chan time.Time), 225 | errors: make(chan *Error, 1), 226 | deadlines: make(chan readDeadliner, 1), 227 | } 228 | go c.reader(conn) 229 | return c, c.open(config) 230 | } 231 | 232 | /* 233 | LocalAddr returns the local TCP peer address, or ":0" (the zero value of net.TCPAddr) 234 | as a fallback default value if the underlying transport does not support LocalAddr(). 235 | */ 236 | func (c *Connection) LocalAddr() net.Addr { 237 | if conn, ok := c.conn.(interface { 238 | LocalAddr() net.Addr 239 | }); ok { 240 | return conn.LocalAddr() 241 | } 242 | return &net.TCPAddr{} 243 | } 244 | 245 | // ConnectionState returns basic TLS details of the underlying transport. 246 | // Returns a zero value when the underlying connection does not implement 247 | // ConnectionState() tls.ConnectionState. 248 | func (c *Connection) ConnectionState() tls.ConnectionState { 249 | if conn, ok := c.conn.(interface { 250 | ConnectionState() tls.ConnectionState 251 | }); ok { 252 | return conn.ConnectionState() 253 | } 254 | return tls.ConnectionState{} 255 | } 256 | 257 | /* 258 | NotifyClose registers a listener for close events either initiated by an error 259 | accompanying a connection.close method or by a normal shutdown. 260 | 261 | On normal shutdowns, the chan will be closed. 262 | 263 | To reconnect after a transport or protocol error, register a listener here and 264 | re-run your setup process. 265 | 266 | */ 267 | func (c *Connection) NotifyClose(receiver chan *Error) chan *Error { 268 | c.m.Lock() 269 | defer c.m.Unlock() 270 | 271 | if c.noNotify { 272 | close(receiver) 273 | } else { 274 | c.closes = append(c.closes, receiver) 275 | } 276 | 277 | return receiver 278 | } 279 | 280 | /* 281 | NotifyBlocked registers a listener for RabbitMQ specific TCP flow control 282 | method extensions connection.blocked and connection.unblocked. Flow control is 283 | active with a reason when Blocking.Blocked is true. When a Connection is 284 | blocked, all methods will block across all connections until server resources 285 | become free again. 286 | 287 | This optional extension is supported by the server when the 288 | "connection.blocked" server capability key is true. 289 | 290 | */ 291 | func (c *Connection) NotifyBlocked(receiver chan Blocking) chan Blocking { 292 | c.m.Lock() 293 | defer c.m.Unlock() 294 | 295 | if c.noNotify { 296 | close(receiver) 297 | } else { 298 | c.blocks = append(c.blocks, receiver) 299 | } 300 | 301 | return receiver 302 | } 303 | 304 | /* 305 | Close requests and waits for the response to close the AMQP connection. 306 | 307 | It's advisable to use this message when publishing to ensure all kernel buffers 308 | have been flushed on the server and client before exiting. 309 | 310 | An error indicates that server may not have received this request to close but 311 | the connection should be treated as closed regardless. 312 | 313 | After returning from this call, all resources associated with this connection, 314 | including the underlying io, Channels, Notify listeners and Channel consumers 315 | will also be closed. 316 | */ 317 | func (c *Connection) Close() error { 318 | if c.isClosed() { 319 | return ErrClosed 320 | } 321 | 322 | defer c.shutdown(nil) 323 | return c.call( 324 | &connectionClose{ 325 | ReplyCode: replySuccess, 326 | ReplyText: "kthxbai", 327 | }, 328 | &connectionCloseOk{}, 329 | ) 330 | } 331 | 332 | func (c *Connection) closeWith(err *Error) error { 333 | if c.isClosed() { 334 | return ErrClosed 335 | } 336 | 337 | defer c.shutdown(err) 338 | return c.call( 339 | &connectionClose{ 340 | ReplyCode: uint16(err.Code), 341 | ReplyText: err.Reason, 342 | }, 343 | &connectionCloseOk{}, 344 | ) 345 | } 346 | 347 | func (c *Connection) isClosed() bool { 348 | return (atomic.LoadInt32(&c.closed) == 1) 349 | } 350 | 351 | func (c *Connection) send(f frame) error { 352 | if c.isClosed() { 353 | return ErrClosed 354 | } 355 | 356 | c.sendM.Lock() 357 | err := c.writer.WriteFrame(f) 358 | c.sendM.Unlock() 359 | 360 | if err != nil { 361 | // shutdown could be re-entrant from signaling notify chans 362 | go c.shutdown(&Error{ 363 | Code: FrameError, 364 | Reason: err.Error(), 365 | }) 366 | } else { 367 | // Broadcast we sent a frame, reducing heartbeats, only 368 | // if there is something that can receive - like a non-reentrant 369 | // call or if the heartbeater isn't running 370 | select { 371 | case c.sends <- time.Now(): 372 | default: 373 | } 374 | } 375 | 376 | return err 377 | } 378 | 379 | func (c *Connection) shutdown(err *Error) { 380 | atomic.StoreInt32(&c.closed, 1) 381 | 382 | c.destructor.Do(func() { 383 | c.m.Lock() 384 | defer c.m.Unlock() 385 | 386 | if err != nil { 387 | for _, c := range c.closes { 388 | c <- err 389 | } 390 | } 391 | 392 | if err != nil { 393 | c.errors <- err 394 | } 395 | // Shutdown handler goroutine can still receive the result. 396 | close(c.errors) 397 | 398 | for _, c := range c.closes { 399 | close(c) 400 | } 401 | 402 | for _, c := range c.blocks { 403 | close(c) 404 | } 405 | 406 | // Shutdown the channel, but do not use closeChannel() as it calls 407 | // releaseChannel() which requires the connection lock. 408 | // 409 | // Ranging over c.channels and calling releaseChannel() that mutates 410 | // c.channels is racy - see commit 6063341 for an example. 411 | for _, ch := range c.channels { 412 | ch.shutdown(err) 413 | } 414 | 415 | c.conn.Close() 416 | 417 | c.channels = map[uint16]*Channel{} 418 | c.allocator = newAllocator(1, c.Config.ChannelMax) 419 | c.noNotify = true 420 | }) 421 | } 422 | 423 | // All methods sent to the connection channel should be synchronous so we 424 | // can handle them directly without a framing component 425 | func (c *Connection) demux(f frame) { 426 | if f.channel() == 0 { 427 | c.dispatch0(f) 428 | } else { 429 | c.dispatchN(f) 430 | } 431 | } 432 | 433 | func (c *Connection) dispatch0(f frame) { 434 | switch mf := f.(type) { 435 | case *methodFrame: 436 | switch m := mf.Method.(type) { 437 | case *connectionClose: 438 | // Send immediately as shutdown will close our side of the writer. 439 | c.send(&methodFrame{ 440 | ChannelId: 0, 441 | Method: &connectionCloseOk{}, 442 | }) 443 | 444 | c.shutdown(newError(m.ReplyCode, m.ReplyText)) 445 | case *connectionBlocked: 446 | for _, c := range c.blocks { 447 | c <- Blocking{Active: true, Reason: m.Reason} 448 | } 449 | case *connectionUnblocked: 450 | for _, c := range c.blocks { 451 | c <- Blocking{Active: false} 452 | } 453 | default: 454 | c.rpc <- m 455 | } 456 | case *heartbeatFrame: 457 | // kthx - all reads reset our deadline. so we can drop this 458 | default: 459 | // lolwat - channel0 only responds to methods and heartbeats 460 | c.closeWith(ErrUnexpectedFrame) 461 | } 462 | } 463 | 464 | func (c *Connection) dispatchN(f frame) { 465 | c.m.Lock() 466 | channel := c.channels[f.channel()] 467 | c.m.Unlock() 468 | 469 | if channel != nil { 470 | channel.recv(channel, f) 471 | } else { 472 | c.dispatchClosed(f) 473 | } 474 | } 475 | 476 | // section 2.3.7: "When a peer decides to close a channel or connection, it 477 | // sends a Close method. The receiving peer MUST respond to a Close with a 478 | // Close-Ok, and then both parties can close their channel or connection. Note 479 | // that if peers ignore Close, deadlock can happen when both peers send Close 480 | // at the same time." 481 | // 482 | // When we don't have a channel, so we must respond with close-ok on a close 483 | // method. This can happen between a channel exception on an asynchronous 484 | // method like basic.publish and a synchronous close with channel.close. 485 | // In that case, we'll get both a channel.close and channel.close-ok in any 486 | // order. 487 | func (c *Connection) dispatchClosed(f frame) { 488 | // Only consider method frames, drop content/header frames 489 | if mf, ok := f.(*methodFrame); ok { 490 | switch mf.Method.(type) { 491 | case *channelClose: 492 | c.send(&methodFrame{ 493 | ChannelId: f.channel(), 494 | Method: &channelCloseOk{}, 495 | }) 496 | case *channelCloseOk: 497 | // we are already closed, so do nothing 498 | default: 499 | // unexpected method on closed channel 500 | c.closeWith(ErrClosed) 501 | } 502 | } 503 | } 504 | 505 | // Reads each frame off the IO and hand off to the connection object that 506 | // will demux the streams and dispatch to one of the opened channels or 507 | // handle on channel 0 (the connection channel). 508 | func (c *Connection) reader(r io.Reader) { 509 | buf := bufio.NewReader(r) 510 | frames := &reader{buf} 511 | conn, haveDeadliner := r.(readDeadliner) 512 | 513 | for { 514 | frame, err := frames.ReadFrame() 515 | 516 | if err != nil { 517 | c.shutdown(&Error{Code: FrameError, Reason: err.Error()}) 518 | return 519 | } 520 | 521 | c.demux(frame) 522 | 523 | if haveDeadliner { 524 | c.deadlines <- conn 525 | } 526 | } 527 | } 528 | 529 | // Ensures that at least one frame is being sent at the tuned interval with a 530 | // jitter tolerance of 1s 531 | func (c *Connection) heartbeater(interval time.Duration, done chan *Error) { 532 | const maxServerHeartbeatsInFlight = 3 533 | 534 | var sendTicks <-chan time.Time 535 | if interval > 0 { 536 | ticker := time.NewTicker(interval) 537 | defer ticker.Stop() 538 | sendTicks = ticker.C 539 | } 540 | 541 | lastSent := time.Now() 542 | 543 | for { 544 | select { 545 | case at, stillSending := <-c.sends: 546 | // When actively sending, depend on sent frames to reset server timer 547 | if stillSending { 548 | lastSent = at 549 | } else { 550 | return 551 | } 552 | 553 | case at := <-sendTicks: 554 | // When idle, fill the space with a heartbeat frame 555 | if at.Sub(lastSent) > interval-time.Second { 556 | if err := c.send(&heartbeatFrame{}); err != nil { 557 | // send heartbeats even after close/closeOk so we 558 | // tick until the connection starts erroring 559 | return 560 | } 561 | } 562 | 563 | case conn := <-c.deadlines: 564 | // When reading, reset our side of the deadline, if we've negotiated one with 565 | // a deadline that covers at least 2 server heartbeats 566 | if interval > 0 { 567 | conn.SetReadDeadline(time.Now().Add(maxServerHeartbeatsInFlight * interval)) 568 | } 569 | 570 | case <-done: 571 | return 572 | } 573 | } 574 | } 575 | 576 | // Convenience method to inspect the Connection.Properties["capabilities"] 577 | // Table for server identified capabilities like "basic.ack" or 578 | // "confirm.select". 579 | func (c *Connection) isCapable(featureName string) bool { 580 | capabilities, _ := c.Properties["capabilities"].(Table) 581 | hasFeature, _ := capabilities[featureName].(bool) 582 | return hasFeature 583 | } 584 | 585 | // allocateChannel records but does not open a new channel with a unique id. 586 | // This method is the initial part of the channel lifecycle and paired with 587 | // releaseChannel 588 | func (c *Connection) allocateChannel() (*Channel, error) { 589 | c.m.Lock() 590 | defer c.m.Unlock() 591 | 592 | if c.isClosed() { 593 | return nil, ErrClosed 594 | } 595 | 596 | id, ok := c.allocator.next() 597 | if !ok { 598 | return nil, ErrChannelMax 599 | } 600 | 601 | ch := newChannel(c, uint16(id)) 602 | c.channels[uint16(id)] = ch 603 | 604 | return ch, nil 605 | } 606 | 607 | // releaseChannel removes a channel from the registry as the final part of the 608 | // channel lifecycle 609 | func (c *Connection) releaseChannel(id uint16) { 610 | c.m.Lock() 611 | defer c.m.Unlock() 612 | 613 | delete(c.channels, id) 614 | c.allocator.release(int(id)) 615 | } 616 | 617 | // openChannel allocates and opens a channel, must be paired with closeChannel 618 | func (c *Connection) openChannel() (*Channel, error) { 619 | ch, err := c.allocateChannel() 620 | if err != nil { 621 | return nil, err 622 | } 623 | 624 | if err := ch.open(); err != nil { 625 | c.releaseChannel(ch.id) 626 | return nil, err 627 | } 628 | return ch, nil 629 | } 630 | 631 | // closeChannel releases and initiates a shutdown of the channel. All channel 632 | // closures should be initiated here for proper channel lifecycle management on 633 | // this connection. 634 | func (c *Connection) closeChannel(ch *Channel, e *Error) { 635 | ch.shutdown(e) 636 | c.releaseChannel(ch.id) 637 | } 638 | 639 | /* 640 | Channel opens a unique, concurrent server channel to process the bulk of AMQP 641 | messages. Any error from methods on this receiver will render the receiver 642 | invalid and a new Channel should be opened. 643 | 644 | */ 645 | func (c *Connection) Channel() (*Channel, error) { 646 | return c.openChannel() 647 | } 648 | 649 | func (c *Connection) call(req message, res ...message) error { 650 | // Special case for when the protocol header frame is sent insted of a 651 | // request method 652 | if req != nil { 653 | if err := c.send(&methodFrame{ChannelId: 0, Method: req}); err != nil { 654 | return err 655 | } 656 | } 657 | 658 | select { 659 | case err, ok := <-c.errors: 660 | if !ok { 661 | return ErrClosed 662 | } 663 | return err 664 | 665 | case msg := <-c.rpc: 666 | // Try to match one of the result types 667 | for _, try := range res { 668 | if reflect.TypeOf(msg) == reflect.TypeOf(try) { 669 | // *res = *msg 670 | vres := reflect.ValueOf(try).Elem() 671 | vmsg := reflect.ValueOf(msg).Elem() 672 | vres.Set(vmsg) 673 | return nil 674 | } 675 | } 676 | return ErrCommandInvalid 677 | } 678 | // unreachable 679 | } 680 | 681 | // Connection = open-Connection *use-Connection close-Connection 682 | // open-Connection = C:protocol-header 683 | // S:START C:START-OK 684 | // *challenge 685 | // S:TUNE C:TUNE-OK 686 | // C:OPEN S:OPEN-OK 687 | // challenge = S:SECURE C:SECURE-OK 688 | // use-Connection = *channel 689 | // close-Connection = C:CLOSE S:CLOSE-OK 690 | // / S:CLOSE C:CLOSE-OK 691 | func (c *Connection) open(config Config) error { 692 | if err := c.send(&protocolHeader{}); err != nil { 693 | return err 694 | } 695 | 696 | return c.openStart(config) 697 | } 698 | 699 | func (c *Connection) openStart(config Config) error { 700 | start := &connectionStart{} 701 | 702 | if err := c.call(nil, start); err != nil { 703 | return err 704 | } 705 | 706 | c.Major = int(start.VersionMajor) 707 | c.Minor = int(start.VersionMinor) 708 | c.Properties = Table(start.ServerProperties) 709 | c.Locales = strings.Split(start.Locales, " ") 710 | 711 | // eventually support challenge/response here by also responding to 712 | // connectionSecure. 713 | auth, ok := pickSASLMechanism(config.SASL, strings.Split(start.Mechanisms, " ")) 714 | if !ok { 715 | return ErrSASL 716 | } 717 | 718 | // Save this mechanism off as the one we chose 719 | c.Config.SASL = []Authentication{auth} 720 | 721 | // Set the connection locale to client locale 722 | c.Config.Locale = config.Locale 723 | 724 | return c.openTune(config, auth) 725 | } 726 | 727 | func (c *Connection) openTune(config Config, auth Authentication) error { 728 | if len(config.Properties) == 0 { 729 | config.Properties = Table{ 730 | "product": defaultProduct, 731 | "version": defaultVersion, 732 | } 733 | } 734 | 735 | config.Properties["capabilities"] = Table{ 736 | "connection.blocked": true, 737 | "consumer_cancel_notify": true, 738 | } 739 | 740 | ok := &connectionStartOk{ 741 | ClientProperties: config.Properties, 742 | Mechanism: auth.Mechanism(), 743 | Response: auth.Response(), 744 | Locale: config.Locale, 745 | } 746 | tune := &connectionTune{} 747 | 748 | if err := c.call(ok, tune); err != nil { 749 | // per spec, a connection can only be closed when it has been opened 750 | // so at this point, we know it's an auth error, but the socket 751 | // was closed instead. Return a meaningful error. 752 | return ErrCredentials 753 | } 754 | 755 | // When the server and client both use default 0, then the max channel is 756 | // only limited by uint16. 757 | c.Config.ChannelMax = pick(config.ChannelMax, int(tune.ChannelMax)) 758 | if c.Config.ChannelMax == 0 { 759 | c.Config.ChannelMax = defaultChannelMax 760 | } 761 | c.Config.ChannelMax = min(c.Config.ChannelMax, maxChannelMax) 762 | 763 | // Frame size includes headers and end byte (len(payload)+8), even if 764 | // this is less than FrameMinSize, use what the server sends because the 765 | // alternative is to stop the handshake here. 766 | c.Config.FrameSize = pick(config.FrameSize, int(tune.FrameMax)) 767 | 768 | // Save this off for resetDeadline() 769 | c.Config.Heartbeat = time.Second * time.Duration(pick( 770 | int(config.Heartbeat/time.Second), 771 | int(tune.Heartbeat))) 772 | 773 | // "The client should start sending heartbeats after receiving a 774 | // Connection.Tune method" 775 | go c.heartbeater(c.Config.Heartbeat, c.NotifyClose(make(chan *Error, 1))) 776 | 777 | if err := c.send(&methodFrame{ 778 | ChannelId: 0, 779 | Method: &connectionTuneOk{ 780 | ChannelMax: uint16(c.Config.ChannelMax), 781 | FrameMax: uint32(c.Config.FrameSize), 782 | Heartbeat: uint16(c.Config.Heartbeat / time.Second), 783 | }, 784 | }); err != nil { 785 | return err 786 | } 787 | 788 | return c.openVhost(config) 789 | } 790 | 791 | func (c *Connection) openVhost(config Config) error { 792 | req := &connectionOpen{VirtualHost: config.Vhost} 793 | res := &connectionOpenOk{} 794 | 795 | if err := c.call(req, res); err != nil { 796 | // Cannot be closed yet, but we know it's a vhost problem 797 | return ErrVhost 798 | } 799 | 800 | c.Config.Vhost = config.Vhost 801 | 802 | return c.openComplete() 803 | } 804 | 805 | // openComplete performs any final Connection initialization dependent on the 806 | // connection handshake and clears any state needed for TLS and AMQP handshaking. 807 | func (c *Connection) openComplete() error { 808 | // We clear the deadlines and let the heartbeater reset the read deadline if requested. 809 | // RabbitMQ uses TCP flow control at this point for pushback so Writes can 810 | // intentionally block. 811 | if deadliner, ok := c.conn.(interface { 812 | SetDeadline(time.Time) error 813 | }); ok { 814 | _ = deadliner.SetDeadline(time.Time{}) 815 | } 816 | 817 | c.allocator = newAllocator(1, c.Config.ChannelMax) 818 | return nil 819 | } 820 | 821 | func max(a, b int) int { 822 | if a > b { 823 | return a 824 | } 825 | return b 826 | } 827 | 828 | func min(a, b int) int { 829 | if a < b { 830 | return a 831 | } 832 | return b 833 | } 834 | 835 | func pick(client, server int) int { 836 | if client == 0 || server == 0 { 837 | return max(client, server) 838 | } 839 | return min(client, server) 840 | } 841 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/channel.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "reflect" 10 | "sync" 11 | "sync/atomic" 12 | ) 13 | 14 | // 0 1 3 7 size+7 size+8 15 | // +------+---------+-------------+ +------------+ +-----------+ 16 | // | type | channel | size | | payload | | frame-end | 17 | // +------+---------+-------------+ +------------+ +-----------+ 18 | // octet short long size octets octet 19 | const frameHeaderSize = 1 + 2 + 4 + 1 20 | 21 | /* 22 | Channel represents an AMQP channel. Used as a context for valid message 23 | exchange. Errors on methods with this Channel as a receiver means this channel 24 | should be discarded and a new channel established. 25 | 26 | */ 27 | type Channel struct { 28 | destructor sync.Once 29 | m sync.Mutex // struct field mutex 30 | confirmM sync.Mutex // publisher confirms state mutex 31 | notifyM sync.RWMutex 32 | 33 | connection *Connection 34 | 35 | rpc chan message 36 | consumers *consumers 37 | 38 | id uint16 39 | 40 | // closed is set to 1 when the channel has been closed - see Channel.send() 41 | closed int32 42 | 43 | // true when we will never notify again 44 | noNotify bool 45 | 46 | // Channel and Connection exceptions will be broadcast on these listeners. 47 | closes []chan *Error 48 | 49 | // Listeners for active=true flow control. When true is sent to a listener, 50 | // publishing should pause until false is sent to listeners. 51 | flows []chan bool 52 | 53 | // Listeners for returned publishings for unroutable messages on mandatory 54 | // publishings or undeliverable messages on immediate publishings. 55 | returns []chan Return 56 | 57 | // Listeners for when the server notifies the client that 58 | // a consumer has been cancelled. 59 | cancels []chan string 60 | 61 | // Allocated when in confirm mode in order to track publish counter and order confirms 62 | confirms *confirms 63 | confirming bool 64 | 65 | // Selects on any errors from shutdown during RPC 66 | errors chan *Error 67 | 68 | // State machine that manages frame order, must only be mutated by the connection 69 | recv func(*Channel, frame) error 70 | 71 | // Current state for frame re-assembly, only mutated from recv 72 | message messageWithContent 73 | header *headerFrame 74 | body []byte 75 | } 76 | 77 | // Constructs a new channel with the given framing rules 78 | func newChannel(c *Connection, id uint16) *Channel { 79 | return &Channel{ 80 | connection: c, 81 | id: id, 82 | rpc: make(chan message), 83 | consumers: makeConsumers(), 84 | confirms: newConfirms(), 85 | recv: (*Channel).recvMethod, 86 | errors: make(chan *Error, 1), 87 | } 88 | } 89 | 90 | // shutdown is called by Connection after the channel has been removed from the 91 | // connection registry. 92 | func (ch *Channel) shutdown(e *Error) { 93 | ch.destructor.Do(func() { 94 | ch.m.Lock() 95 | defer ch.m.Unlock() 96 | 97 | // Grab an exclusive lock for the notify channels 98 | ch.notifyM.Lock() 99 | defer ch.notifyM.Unlock() 100 | 101 | // Broadcast abnormal shutdown 102 | if e != nil { 103 | for _, c := range ch.closes { 104 | c <- e 105 | } 106 | } 107 | 108 | // Signal that from now on, Channel.send() should call 109 | // Channel.sendClosed() 110 | atomic.StoreInt32(&ch.closed, 1) 111 | 112 | // Notify RPC if we're selecting 113 | if e != nil { 114 | ch.errors <- e 115 | } 116 | 117 | ch.consumers.close() 118 | 119 | for _, c := range ch.closes { 120 | close(c) 121 | } 122 | 123 | for _, c := range ch.flows { 124 | close(c) 125 | } 126 | 127 | for _, c := range ch.returns { 128 | close(c) 129 | } 130 | 131 | for _, c := range ch.cancels { 132 | close(c) 133 | } 134 | 135 | // Set the slices to nil to prevent the dispatch() range from sending on 136 | // the now closed channels after we release the notifyM mutex 137 | ch.flows = nil 138 | ch.closes = nil 139 | ch.returns = nil 140 | ch.cancels = nil 141 | 142 | if ch.confirms != nil { 143 | ch.confirms.Close() 144 | } 145 | 146 | close(ch.errors) 147 | ch.noNotify = true 148 | }) 149 | } 150 | 151 | // send calls Channel.sendOpen() during normal operation. 152 | // 153 | // After the channel has been closed, send calls Channel.sendClosed(), ensuring 154 | // only 'channel.close' is sent to the server. 155 | func (ch *Channel) send(msg message) (err error) { 156 | // If the channel is closed, use Channel.sendClosed() 157 | if atomic.LoadInt32(&ch.closed) == 1 { 158 | return ch.sendClosed(msg) 159 | } 160 | 161 | return ch.sendOpen(msg) 162 | } 163 | 164 | func (ch *Channel) open() error { 165 | return ch.call(&channelOpen{}, &channelOpenOk{}) 166 | } 167 | 168 | // Performs a request/response call for when the message is not NoWait and is 169 | // specified as Synchronous. 170 | func (ch *Channel) call(req message, res ...message) error { 171 | if err := ch.send(req); err != nil { 172 | return err 173 | } 174 | 175 | if req.wait() { 176 | select { 177 | case e, ok := <-ch.errors: 178 | if ok { 179 | return e 180 | } 181 | return ErrClosed 182 | 183 | case msg := <-ch.rpc: 184 | if msg != nil { 185 | for _, try := range res { 186 | if reflect.TypeOf(msg) == reflect.TypeOf(try) { 187 | // *res = *msg 188 | vres := reflect.ValueOf(try).Elem() 189 | vmsg := reflect.ValueOf(msg).Elem() 190 | vres.Set(vmsg) 191 | return nil 192 | } 193 | } 194 | return ErrCommandInvalid 195 | } 196 | // RPC channel has been closed without an error, likely due to a hard 197 | // error on the Connection. This indicates we have already been 198 | // shutdown and if were waiting, will have returned from the errors chan. 199 | return ErrClosed 200 | } 201 | } 202 | 203 | return nil 204 | } 205 | 206 | func (ch *Channel) sendClosed(msg message) (err error) { 207 | // After a 'channel.close' is sent or received the only valid response is 208 | // channel.close-ok 209 | if _, ok := msg.(*channelCloseOk); ok { 210 | return ch.connection.send(&methodFrame{ 211 | ChannelId: ch.id, 212 | Method: msg, 213 | }) 214 | } 215 | 216 | return ErrClosed 217 | } 218 | 219 | func (ch *Channel) sendOpen(msg message) (err error) { 220 | if content, ok := msg.(messageWithContent); ok { 221 | props, body := content.getContent() 222 | class, _ := content.id() 223 | 224 | // catch client max frame size==0 and server max frame size==0 225 | // set size to length of what we're trying to publish 226 | var size int 227 | if ch.connection.Config.FrameSize > 0 { 228 | size = ch.connection.Config.FrameSize - frameHeaderSize 229 | } else { 230 | size = len(body) 231 | } 232 | 233 | if err = ch.connection.send(&methodFrame{ 234 | ChannelId: ch.id, 235 | Method: content, 236 | }); err != nil { 237 | return 238 | } 239 | 240 | if err = ch.connection.send(&headerFrame{ 241 | ChannelId: ch.id, 242 | ClassId: class, 243 | Size: uint64(len(body)), 244 | Properties: props, 245 | }); err != nil { 246 | return 247 | } 248 | 249 | // chunk body into size (max frame size - frame header size) 250 | for i, j := 0, size; i < len(body); i, j = j, j+size { 251 | if j > len(body) { 252 | j = len(body) 253 | } 254 | 255 | if err = ch.connection.send(&bodyFrame{ 256 | ChannelId: ch.id, 257 | Body: body[i:j], 258 | }); err != nil { 259 | return 260 | } 261 | } 262 | } else { 263 | err = ch.connection.send(&methodFrame{ 264 | ChannelId: ch.id, 265 | Method: msg, 266 | }) 267 | } 268 | 269 | return 270 | } 271 | 272 | // Eventually called via the state machine from the connection's reader 273 | // goroutine, so assumes serialized access. 274 | func (ch *Channel) dispatch(msg message) { 275 | switch m := msg.(type) { 276 | case *channelClose: 277 | // lock before sending connection.close-ok 278 | // to avoid unexpected interleaving with basic.publish frames if 279 | // publishing is happening concurrently 280 | ch.m.Lock() 281 | ch.send(&channelCloseOk{}) 282 | ch.m.Unlock() 283 | ch.connection.closeChannel(ch, newError(m.ReplyCode, m.ReplyText)) 284 | 285 | case *channelFlow: 286 | ch.notifyM.RLock() 287 | for _, c := range ch.flows { 288 | c <- m.Active 289 | } 290 | ch.notifyM.RUnlock() 291 | ch.send(&channelFlowOk{Active: m.Active}) 292 | 293 | case *basicCancel: 294 | ch.notifyM.RLock() 295 | for _, c := range ch.cancels { 296 | c <- m.ConsumerTag 297 | } 298 | ch.notifyM.RUnlock() 299 | ch.consumers.cancel(m.ConsumerTag) 300 | 301 | case *basicReturn: 302 | ret := newReturn(*m) 303 | ch.notifyM.RLock() 304 | for _, c := range ch.returns { 305 | c <- *ret 306 | } 307 | ch.notifyM.RUnlock() 308 | 309 | case *basicAck: 310 | if ch.confirming { 311 | if m.Multiple { 312 | ch.confirms.Multiple(Confirmation{m.DeliveryTag, true}) 313 | } else { 314 | ch.confirms.One(Confirmation{m.DeliveryTag, true}) 315 | } 316 | } 317 | 318 | case *basicNack: 319 | if ch.confirming { 320 | if m.Multiple { 321 | ch.confirms.Multiple(Confirmation{m.DeliveryTag, false}) 322 | } else { 323 | ch.confirms.One(Confirmation{m.DeliveryTag, false}) 324 | } 325 | } 326 | 327 | case *basicDeliver: 328 | ch.consumers.send(m.ConsumerTag, newDelivery(ch, m)) 329 | // TODO log failed consumer and close channel, this can happen when 330 | // deliveries are in flight and a no-wait cancel has happened 331 | 332 | default: 333 | ch.rpc <- msg 334 | } 335 | } 336 | 337 | func (ch *Channel) transition(f func(*Channel, frame) error) error { 338 | ch.recv = f 339 | return nil 340 | } 341 | 342 | func (ch *Channel) recvMethod(f frame) error { 343 | switch frame := f.(type) { 344 | case *methodFrame: 345 | if msg, ok := frame.Method.(messageWithContent); ok { 346 | ch.body = make([]byte, 0) 347 | ch.message = msg 348 | return ch.transition((*Channel).recvHeader) 349 | } 350 | 351 | ch.dispatch(frame.Method) // termination state 352 | return ch.transition((*Channel).recvMethod) 353 | 354 | case *headerFrame: 355 | // drop 356 | return ch.transition((*Channel).recvMethod) 357 | 358 | case *bodyFrame: 359 | // drop 360 | return ch.transition((*Channel).recvMethod) 361 | } 362 | 363 | panic("unexpected frame type") 364 | } 365 | 366 | func (ch *Channel) recvHeader(f frame) error { 367 | switch frame := f.(type) { 368 | case *methodFrame: 369 | // interrupt content and handle method 370 | return ch.recvMethod(f) 371 | 372 | case *headerFrame: 373 | // start collecting if we expect body frames 374 | ch.header = frame 375 | 376 | if frame.Size == 0 { 377 | ch.message.setContent(ch.header.Properties, ch.body) 378 | ch.dispatch(ch.message) // termination state 379 | return ch.transition((*Channel).recvMethod) 380 | } 381 | return ch.transition((*Channel).recvContent) 382 | 383 | case *bodyFrame: 384 | // drop and reset 385 | return ch.transition((*Channel).recvMethod) 386 | } 387 | 388 | panic("unexpected frame type") 389 | } 390 | 391 | // state after method + header and before the length 392 | // defined by the header has been reached 393 | func (ch *Channel) recvContent(f frame) error { 394 | switch frame := f.(type) { 395 | case *methodFrame: 396 | // interrupt content and handle method 397 | return ch.recvMethod(f) 398 | 399 | case *headerFrame: 400 | // drop and reset 401 | return ch.transition((*Channel).recvMethod) 402 | 403 | case *bodyFrame: 404 | ch.body = append(ch.body, frame.Body...) 405 | 406 | if uint64(len(ch.body)) >= ch.header.Size { 407 | ch.message.setContent(ch.header.Properties, ch.body) 408 | ch.dispatch(ch.message) // termination state 409 | return ch.transition((*Channel).recvMethod) 410 | } 411 | 412 | return ch.transition((*Channel).recvContent) 413 | } 414 | 415 | panic("unexpected frame type") 416 | } 417 | 418 | /* 419 | Close initiate a clean channel closure by sending a close message with the error 420 | code set to '200'. 421 | 422 | It is safe to call this method multiple times. 423 | 424 | */ 425 | func (ch *Channel) Close() error { 426 | defer ch.connection.closeChannel(ch, nil) 427 | return ch.call( 428 | &channelClose{ReplyCode: replySuccess}, 429 | &channelCloseOk{}, 430 | ) 431 | } 432 | 433 | /* 434 | NotifyClose registers a listener for when the server sends a channel or 435 | connection exception in the form of a Connection.Close or Channel.Close method. 436 | Connection exceptions will be broadcast to all open channels and all channels 437 | will be closed, where channel exceptions will only be broadcast to listeners to 438 | this channel. 439 | 440 | The chan provided will be closed when the Channel is closed and on a 441 | graceful close, no error will be sent. 442 | 443 | */ 444 | func (ch *Channel) NotifyClose(c chan *Error) chan *Error { 445 | ch.notifyM.Lock() 446 | defer ch.notifyM.Unlock() 447 | 448 | if ch.noNotify { 449 | close(c) 450 | } else { 451 | ch.closes = append(ch.closes, c) 452 | } 453 | 454 | return c 455 | } 456 | 457 | /* 458 | NotifyFlow registers a listener for basic.flow methods sent by the server. 459 | When `true` is sent on one of the listener channels, all publishers should 460 | pause until a `false` is sent. 461 | 462 | The server may ask the producer to pause or restart the flow of Publishings 463 | sent by on a channel. This is a simple flow-control mechanism that a server can 464 | use to avoid overflowing its queues or otherwise finding itself receiving more 465 | messages than it can process. Note that this method is not intended for window 466 | control. It does not affect contents returned by basic.get-ok methods. 467 | 468 | When a new channel is opened, it is active (flow is active). Some 469 | applications assume that channels are inactive until started. To emulate 470 | this behavior a client MAY open the channel, then pause it. 471 | 472 | Publishers should respond to a flow messages as rapidly as possible and the 473 | server may disconnect over producing channels that do not respect these 474 | messages. 475 | 476 | basic.flow-ok methods will always be returned to the server regardless of 477 | the number of listeners there are. 478 | 479 | To control the flow of deliveries from the server, use the Channel.Flow() 480 | method instead. 481 | 482 | Note: RabbitMQ will rather use TCP pushback on the network connection instead 483 | of sending basic.flow. This means that if a single channel is producing too 484 | much on the same connection, all channels using that connection will suffer, 485 | including acknowledgments from deliveries. Use different Connections if you 486 | desire to interleave consumers and producers in the same process to avoid your 487 | basic.ack messages from getting rate limited with your basic.publish messages. 488 | 489 | */ 490 | func (ch *Channel) NotifyFlow(c chan bool) chan bool { 491 | ch.notifyM.Lock() 492 | defer ch.notifyM.Unlock() 493 | 494 | if ch.noNotify { 495 | close(c) 496 | } else { 497 | ch.flows = append(ch.flows, c) 498 | } 499 | 500 | return c 501 | } 502 | 503 | /* 504 | NotifyReturn registers a listener for basic.return methods. These can be sent 505 | from the server when a publish is undeliverable either from the mandatory or 506 | immediate flags. 507 | 508 | A return struct has a copy of the Publishing along with some error 509 | information about why the publishing failed. 510 | 511 | */ 512 | func (ch *Channel) NotifyReturn(c chan Return) chan Return { 513 | ch.notifyM.Lock() 514 | defer ch.notifyM.Unlock() 515 | 516 | if ch.noNotify { 517 | close(c) 518 | } else { 519 | ch.returns = append(ch.returns, c) 520 | } 521 | 522 | return c 523 | } 524 | 525 | /* 526 | NotifyCancel registers a listener for basic.cancel methods. These can be sent 527 | from the server when a queue is deleted or when consuming from a mirrored queue 528 | where the master has just failed (and was moved to another node). 529 | 530 | The subscription tag is returned to the listener. 531 | 532 | */ 533 | func (ch *Channel) NotifyCancel(c chan string) chan string { 534 | ch.notifyM.Lock() 535 | defer ch.notifyM.Unlock() 536 | 537 | if ch.noNotify { 538 | close(c) 539 | } else { 540 | ch.cancels = append(ch.cancels, c) 541 | } 542 | 543 | return c 544 | } 545 | 546 | /* 547 | NotifyConfirm calls NotifyPublish and starts a goroutine sending 548 | ordered Ack and Nack DeliveryTag to the respective channels. 549 | 550 | For strict ordering, use NotifyPublish instead. 551 | */ 552 | func (ch *Channel) NotifyConfirm(ack, nack chan uint64) (chan uint64, chan uint64) { 553 | confirms := ch.NotifyPublish(make(chan Confirmation, len(ack)+len(nack))) 554 | 555 | go func() { 556 | for c := range confirms { 557 | if c.Ack { 558 | ack <- c.DeliveryTag 559 | } else { 560 | nack <- c.DeliveryTag 561 | } 562 | } 563 | close(ack) 564 | if nack != ack { 565 | close(nack) 566 | } 567 | }() 568 | 569 | return ack, nack 570 | } 571 | 572 | /* 573 | NotifyPublish registers a listener for reliable publishing. Receives from this 574 | chan for every publish after Channel.Confirm will be in order starting with 575 | DeliveryTag 1. 576 | 577 | There will be one and only one Confirmation Publishing starting with the 578 | delivery tag of 1 and progressing sequentially until the total number of 579 | Publishings have been seen by the server. 580 | 581 | Acknowledgments will be received in the order of delivery from the 582 | NotifyPublish channels even if the server acknowledges them out of order. 583 | 584 | The listener chan will be closed when the Channel is closed. 585 | 586 | The capacity of the chan Confirmation must be at least as large as the 587 | number of outstanding publishings. Not having enough buffered chans will 588 | create a deadlock if you attempt to perform other operations on the Connection 589 | or Channel while confirms are in-flight. 590 | 591 | It's advisable to wait for all Confirmations to arrive before calling 592 | Channel.Close() or Connection.Close(). 593 | 594 | */ 595 | func (ch *Channel) NotifyPublish(confirm chan Confirmation) chan Confirmation { 596 | ch.notifyM.Lock() 597 | defer ch.notifyM.Unlock() 598 | 599 | if ch.noNotify { 600 | close(confirm) 601 | } else { 602 | ch.confirms.Listen(confirm) 603 | } 604 | 605 | return confirm 606 | 607 | } 608 | 609 | /* 610 | Qos controls how many messages or how many bytes the server will try to keep on 611 | the network for consumers before receiving delivery acks. The intent of Qos is 612 | to make sure the network buffers stay full between the server and client. 613 | 614 | With a prefetch count greater than zero, the server will deliver that many 615 | messages to consumers before acknowledgments are received. The server ignores 616 | this option when consumers are started with noAck because no acknowledgments 617 | are expected or sent. 618 | 619 | With a prefetch size greater than zero, the server will try to keep at least 620 | that many bytes of deliveries flushed to the network before receiving 621 | acknowledgments from the consumers. This option is ignored when consumers are 622 | started with noAck. 623 | 624 | When global is true, these Qos settings apply to all existing and future 625 | consumers on all channels on the same connection. When false, the Channel.Qos 626 | settings will apply to all existing and future consumers on this channel. 627 | 628 | Please see the RabbitMQ Consumer Prefetch documentation for an explanation of 629 | how the global flag is implemented in RabbitMQ, as it differs from the 630 | AMQP 0.9.1 specification in that global Qos settings are limited in scope to 631 | channels, not connections (https://www.rabbitmq.com/consumer-prefetch.html). 632 | 633 | To get round-robin behavior between consumers consuming from the same queue on 634 | different connections, set the prefetch count to 1, and the next available 635 | message on the server will be delivered to the next available consumer. 636 | 637 | If your consumer work time is reasonably consistent and not much greater 638 | than two times your network round trip time, you will see significant 639 | throughput improvements starting with a prefetch count of 2 or slightly 640 | greater as described by benchmarks on RabbitMQ. 641 | 642 | http://www.rabbitmq.com/blog/2012/04/25/rabbitmq-performance-measurements-part-2/ 643 | */ 644 | func (ch *Channel) Qos(prefetchCount, prefetchSize int, global bool) error { 645 | return ch.call( 646 | &basicQos{ 647 | PrefetchCount: uint16(prefetchCount), 648 | PrefetchSize: uint32(prefetchSize), 649 | Global: global, 650 | }, 651 | &basicQosOk{}, 652 | ) 653 | } 654 | 655 | /* 656 | Cancel stops deliveries to the consumer chan established in Channel.Consume and 657 | identified by consumer. 658 | 659 | Only use this method to cleanly stop receiving deliveries from the server and 660 | cleanly shut down the consumer chan identified by this tag. Using this method 661 | and waiting for remaining messages to flush from the consumer chan will ensure 662 | all messages received on the network will be delivered to the receiver of your 663 | consumer chan. 664 | 665 | Continue consuming from the chan Delivery provided by Channel.Consume until the 666 | chan closes. 667 | 668 | When noWait is true, do not wait for the server to acknowledge the cancel. 669 | Only use this when you are certain there are no deliveries in flight that 670 | require an acknowledgment, otherwise they will arrive and be dropped in the 671 | client without an ack, and will not be redelivered to other consumers. 672 | 673 | */ 674 | func (ch *Channel) Cancel(consumer string, noWait bool) error { 675 | req := &basicCancel{ 676 | ConsumerTag: consumer, 677 | NoWait: noWait, 678 | } 679 | res := &basicCancelOk{} 680 | 681 | if err := ch.call(req, res); err != nil { 682 | return err 683 | } 684 | 685 | if req.wait() { 686 | ch.consumers.cancel(res.ConsumerTag) 687 | } else { 688 | // Potentially could drop deliveries in flight 689 | ch.consumers.cancel(consumer) 690 | } 691 | 692 | return nil 693 | } 694 | 695 | /* 696 | QueueDeclare declares a queue to hold messages and deliver to consumers. 697 | Declaring creates a queue if it doesn't already exist, or ensures that an 698 | existing queue matches the same parameters. 699 | 700 | Every queue declared gets a default binding to the empty exchange "" which has 701 | the type "direct" with the routing key matching the queue's name. With this 702 | default binding, it is possible to publish messages that route directly to 703 | this queue by publishing to "" with the routing key of the queue name. 704 | 705 | QueueDeclare("alerts", true, false, false, false, nil) 706 | Publish("", "alerts", false, false, Publishing{Body: []byte("...")}) 707 | 708 | Delivery Exchange Key Queue 709 | ----------------------------------------------- 710 | key: alerts -> "" -> alerts -> alerts 711 | 712 | The queue name may be empty, in which case the server will generate a unique name 713 | which will be returned in the Name field of Queue struct. 714 | 715 | Durable and Non-Auto-Deleted queues will survive server restarts and remain 716 | when there are no remaining consumers or bindings. Persistent publishings will 717 | be restored in this queue on server restart. These queues are only able to be 718 | bound to durable exchanges. 719 | 720 | Non-Durable and Auto-Deleted queues will not be redeclared on server restart 721 | and will be deleted by the server after a short time when the last consumer is 722 | canceled or the last consumer's channel is closed. Queues with this lifetime 723 | can also be deleted normally with QueueDelete. These durable queues can only 724 | be bound to non-durable exchanges. 725 | 726 | Non-Durable and Non-Auto-Deleted queues will remain declared as long as the 727 | server is running regardless of how many consumers. This lifetime is useful 728 | for temporary topologies that may have long delays between consumer activity. 729 | These queues can only be bound to non-durable exchanges. 730 | 731 | Durable and Auto-Deleted queues will be restored on server restart, but without 732 | active consumers will not survive and be removed. This Lifetime is unlikely 733 | to be useful. 734 | 735 | Exclusive queues are only accessible by the connection that declares them and 736 | will be deleted when the connection closes. Channels on other connections 737 | will receive an error when attempting to declare, bind, consume, purge or 738 | delete a queue with the same name. 739 | 740 | When noWait is true, the queue will assume to be declared on the server. A 741 | channel exception will arrive if the conditions are met for existing queues 742 | or attempting to modify an existing queue from a different connection. 743 | 744 | When the error return value is not nil, you can assume the queue could not be 745 | declared with these parameters, and the channel will be closed. 746 | 747 | */ 748 | func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) { 749 | if err := args.Validate(); err != nil { 750 | return Queue{}, err 751 | } 752 | 753 | req := &queueDeclare{ 754 | Queue: name, 755 | Passive: false, 756 | Durable: durable, 757 | AutoDelete: autoDelete, 758 | Exclusive: exclusive, 759 | NoWait: noWait, 760 | Arguments: args, 761 | } 762 | res := &queueDeclareOk{} 763 | 764 | if err := ch.call(req, res); err != nil { 765 | return Queue{}, err 766 | } 767 | 768 | if req.wait() { 769 | return Queue{ 770 | Name: res.Queue, 771 | Messages: int(res.MessageCount), 772 | Consumers: int(res.ConsumerCount), 773 | }, nil 774 | } 775 | 776 | return Queue{Name: name}, nil 777 | } 778 | 779 | /* 780 | 781 | QueueDeclarePassive is functionally and parametrically equivalent to 782 | QueueDeclare, except that it sets the "passive" attribute to true. A passive 783 | queue is assumed by RabbitMQ to already exist, and attempting to connect to a 784 | non-existent queue will cause RabbitMQ to throw an exception. This function 785 | can be used to test for the existence of a queue. 786 | 787 | */ 788 | func (ch *Channel) QueueDeclarePassive(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) { 789 | if err := args.Validate(); err != nil { 790 | return Queue{}, err 791 | } 792 | 793 | req := &queueDeclare{ 794 | Queue: name, 795 | Passive: true, 796 | Durable: durable, 797 | AutoDelete: autoDelete, 798 | Exclusive: exclusive, 799 | NoWait: noWait, 800 | Arguments: args, 801 | } 802 | res := &queueDeclareOk{} 803 | 804 | if err := ch.call(req, res); err != nil { 805 | return Queue{}, err 806 | } 807 | 808 | if req.wait() { 809 | return Queue{ 810 | Name: res.Queue, 811 | Messages: int(res.MessageCount), 812 | Consumers: int(res.ConsumerCount), 813 | }, nil 814 | } 815 | 816 | return Queue{Name: name}, nil 817 | } 818 | 819 | /* 820 | QueueInspect passively declares a queue by name to inspect the current message 821 | count and consumer count. 822 | 823 | Use this method to check how many messages ready for delivery reside in the queue, 824 | how many consumers are receiving deliveries, and whether a queue by this 825 | name already exists. 826 | 827 | If the queue by this name exists, use Channel.QueueDeclare check if it is 828 | declared with specific parameters. 829 | 830 | If a queue by this name does not exist, an error will be returned and the 831 | channel will be closed. 832 | 833 | */ 834 | func (ch *Channel) QueueInspect(name string) (Queue, error) { 835 | req := &queueDeclare{ 836 | Queue: name, 837 | Passive: true, 838 | } 839 | res := &queueDeclareOk{} 840 | 841 | err := ch.call(req, res) 842 | 843 | state := Queue{ 844 | Name: name, 845 | Messages: int(res.MessageCount), 846 | Consumers: int(res.ConsumerCount), 847 | } 848 | 849 | return state, err 850 | } 851 | 852 | /* 853 | QueueBind binds an exchange to a queue so that publishings to the exchange will 854 | be routed to the queue when the publishing routing key matches the binding 855 | routing key. 856 | 857 | QueueBind("pagers", "alert", "log", false, nil) 858 | QueueBind("emails", "info", "log", false, nil) 859 | 860 | Delivery Exchange Key Queue 861 | ----------------------------------------------- 862 | key: alert --> log ----> alert --> pagers 863 | key: info ---> log ----> info ---> emails 864 | key: debug --> log (none) (dropped) 865 | 866 | If a binding with the same key and arguments already exists between the 867 | exchange and queue, the attempt to rebind will be ignored and the existing 868 | binding will be retained. 869 | 870 | In the case that multiple bindings may cause the message to be routed to the 871 | same queue, the server will only route the publishing once. This is possible 872 | with topic exchanges. 873 | 874 | QueueBind("pagers", "alert", "amq.topic", false, nil) 875 | QueueBind("emails", "info", "amq.topic", false, nil) 876 | QueueBind("emails", "#", "amq.topic", false, nil) // match everything 877 | 878 | Delivery Exchange Key Queue 879 | ----------------------------------------------- 880 | key: alert --> amq.topic ----> alert --> pagers 881 | key: info ---> amq.topic ----> # ------> emails 882 | \---> info ---/ 883 | key: debug --> amq.topic ----> # ------> emails 884 | 885 | It is only possible to bind a durable queue to a durable exchange regardless of 886 | whether the queue or exchange is auto-deleted. Bindings between durable queues 887 | and exchanges will also be restored on server restart. 888 | 889 | If the binding could not complete, an error will be returned and the channel 890 | will be closed. 891 | 892 | When noWait is true and the queue could not be bound, the channel will be 893 | closed with an error. 894 | 895 | */ 896 | func (ch *Channel) QueueBind(name, key, exchange string, noWait bool, args Table) error { 897 | if err := args.Validate(); err != nil { 898 | return err 899 | } 900 | 901 | return ch.call( 902 | &queueBind{ 903 | Queue: name, 904 | Exchange: exchange, 905 | RoutingKey: key, 906 | NoWait: noWait, 907 | Arguments: args, 908 | }, 909 | &queueBindOk{}, 910 | ) 911 | } 912 | 913 | /* 914 | QueueUnbind removes a binding between an exchange and queue matching the key and 915 | arguments. 916 | 917 | It is possible to send and empty string for the exchange name which means to 918 | unbind the queue from the default exchange. 919 | 920 | */ 921 | func (ch *Channel) QueueUnbind(name, key, exchange string, args Table) error { 922 | if err := args.Validate(); err != nil { 923 | return err 924 | } 925 | 926 | return ch.call( 927 | &queueUnbind{ 928 | Queue: name, 929 | Exchange: exchange, 930 | RoutingKey: key, 931 | Arguments: args, 932 | }, 933 | &queueUnbindOk{}, 934 | ) 935 | } 936 | 937 | /* 938 | QueuePurge removes all messages from the named queue which are not waiting to 939 | be acknowledged. Messages that have been delivered but have not yet been 940 | acknowledged will not be removed. 941 | 942 | When successful, returns the number of messages purged. 943 | 944 | If noWait is true, do not wait for the server response and the number of 945 | messages purged will not be meaningful. 946 | */ 947 | func (ch *Channel) QueuePurge(name string, noWait bool) (int, error) { 948 | req := &queuePurge{ 949 | Queue: name, 950 | NoWait: noWait, 951 | } 952 | res := &queuePurgeOk{} 953 | 954 | err := ch.call(req, res) 955 | 956 | return int(res.MessageCount), err 957 | } 958 | 959 | /* 960 | QueueDelete removes the queue from the server including all bindings then 961 | purges the messages based on server configuration, returning the number of 962 | messages purged. 963 | 964 | When ifUnused is true, the queue will not be deleted if there are any 965 | consumers on the queue. If there are consumers, an error will be returned and 966 | the channel will be closed. 967 | 968 | When ifEmpty is true, the queue will not be deleted if there are any messages 969 | remaining on the queue. If there are messages, an error will be returned and 970 | the channel will be closed. 971 | 972 | When noWait is true, the queue will be deleted without waiting for a response 973 | from the server. The purged message count will not be meaningful. If the queue 974 | could not be deleted, a channel exception will be raised and the channel will 975 | be closed. 976 | 977 | */ 978 | func (ch *Channel) QueueDelete(name string, ifUnused, ifEmpty, noWait bool) (int, error) { 979 | req := &queueDelete{ 980 | Queue: name, 981 | IfUnused: ifUnused, 982 | IfEmpty: ifEmpty, 983 | NoWait: noWait, 984 | } 985 | res := &queueDeleteOk{} 986 | 987 | err := ch.call(req, res) 988 | 989 | return int(res.MessageCount), err 990 | } 991 | 992 | /* 993 | Consume immediately starts delivering queued messages. 994 | 995 | Begin receiving on the returned chan Delivery before any other operation on the 996 | Connection or Channel. 997 | 998 | Continues deliveries to the returned chan Delivery until Channel.Cancel, 999 | Connection.Close, Channel.Close, or an AMQP exception occurs. Consumers must 1000 | range over the chan to ensure all deliveries are received. Unreceived 1001 | deliveries will block all methods on the same connection. 1002 | 1003 | All deliveries in AMQP must be acknowledged. It is expected of the consumer to 1004 | call Delivery.Ack after it has successfully processed the delivery. If the 1005 | consumer is cancelled or the channel or connection is closed any unacknowledged 1006 | deliveries will be requeued at the end of the same queue. 1007 | 1008 | The consumer is identified by a string that is unique and scoped for all 1009 | consumers on this channel. If you wish to eventually cancel the consumer, use 1010 | the same non-empty identifier in Channel.Cancel. An empty string will cause 1011 | the library to generate a unique identity. The consumer identity will be 1012 | included in every Delivery in the ConsumerTag field 1013 | 1014 | When autoAck (also known as noAck) is true, the server will acknowledge 1015 | deliveries to this consumer prior to writing the delivery to the network. When 1016 | autoAck is true, the consumer should not call Delivery.Ack. Automatically 1017 | acknowledging deliveries means that some deliveries may get lost if the 1018 | consumer is unable to process them after the server delivers them. 1019 | See http://www.rabbitmq.com/confirms.html for more details. 1020 | 1021 | When exclusive is true, the server will ensure that this is the sole consumer 1022 | from this queue. When exclusive is false, the server will fairly distribute 1023 | deliveries across multiple consumers. 1024 | 1025 | The noLocal flag is not supported by RabbitMQ. 1026 | 1027 | It's advisable to use separate connections for 1028 | Channel.Publish and Channel.Consume so not to have TCP pushback on publishing 1029 | affect the ability to consume messages, so this parameter is here mostly for 1030 | completeness. 1031 | 1032 | When noWait is true, do not wait for the server to confirm the request and 1033 | immediately begin deliveries. If it is not possible to consume, a channel 1034 | exception will be raised and the channel will be closed. 1035 | 1036 | Optional arguments can be provided that have specific semantics for the queue 1037 | or server. 1038 | 1039 | Inflight messages, limited by Channel.Qos will be buffered until received from 1040 | the returned chan. 1041 | 1042 | When the Channel or Connection is closed, all buffered and inflight messages will 1043 | be dropped. 1044 | 1045 | When the consumer tag is cancelled, all inflight messages will be delivered until 1046 | the returned chan is closed. 1047 | 1048 | */ 1049 | func (ch *Channel) Consume(queue, consumer string, autoAck, exclusive, noLocal, noWait bool, args Table) (<-chan Delivery, error) { 1050 | // When we return from ch.call, there may be a delivery already for the 1051 | // consumer that hasn't been added to the consumer hash yet. Because of 1052 | // this, we never rely on the server picking a consumer tag for us. 1053 | 1054 | if err := args.Validate(); err != nil { 1055 | return nil, err 1056 | } 1057 | 1058 | if consumer == "" { 1059 | consumer = uniqueConsumerTag() 1060 | } 1061 | 1062 | req := &basicConsume{ 1063 | Queue: queue, 1064 | ConsumerTag: consumer, 1065 | NoLocal: noLocal, 1066 | NoAck: autoAck, 1067 | Exclusive: exclusive, 1068 | NoWait: noWait, 1069 | Arguments: args, 1070 | } 1071 | res := &basicConsumeOk{} 1072 | 1073 | deliveries := make(chan Delivery) 1074 | 1075 | ch.consumers.add(consumer, deliveries) 1076 | 1077 | if err := ch.call(req, res); err != nil { 1078 | ch.consumers.cancel(consumer) 1079 | return nil, err 1080 | } 1081 | 1082 | return (<-chan Delivery)(deliveries), nil 1083 | } 1084 | 1085 | /* 1086 | ExchangeDeclare declares an exchange on the server. If the exchange does not 1087 | already exist, the server will create it. If the exchange exists, the server 1088 | verifies that it is of the provided type, durability and auto-delete flags. 1089 | 1090 | Errors returned from this method will close the channel. 1091 | 1092 | Exchange names starting with "amq." are reserved for pre-declared and 1093 | standardized exchanges. The client MAY declare an exchange starting with 1094 | "amq." if the passive option is set, or the exchange already exists. Names can 1095 | consist of a non-empty sequence of letters, digits, hyphen, underscore, 1096 | period, or colon. 1097 | 1098 | Each exchange belongs to one of a set of exchange kinds/types implemented by 1099 | the server. The exchange types define the functionality of the exchange - i.e. 1100 | how messages are routed through it. Once an exchange is declared, its type 1101 | cannot be changed. The common types are "direct", "fanout", "topic" and 1102 | "headers". 1103 | 1104 | Durable and Non-Auto-Deleted exchanges will survive server restarts and remain 1105 | declared when there are no remaining bindings. This is the best lifetime for 1106 | long-lived exchange configurations like stable routes and default exchanges. 1107 | 1108 | Non-Durable and Auto-Deleted exchanges will be deleted when there are no 1109 | remaining bindings and not restored on server restart. This lifetime is 1110 | useful for temporary topologies that should not pollute the virtual host on 1111 | failure or after the consumers have completed. 1112 | 1113 | Non-Durable and Non-Auto-deleted exchanges will remain as long as the server is 1114 | running including when there are no remaining bindings. This is useful for 1115 | temporary topologies that may have long delays between bindings. 1116 | 1117 | Durable and Auto-Deleted exchanges will survive server restarts and will be 1118 | removed before and after server restarts when there are no remaining bindings. 1119 | These exchanges are useful for robust temporary topologies or when you require 1120 | binding durable queues to auto-deleted exchanges. 1121 | 1122 | Note: RabbitMQ declares the default exchange types like 'amq.fanout' as 1123 | durable, so queues that bind to these pre-declared exchanges must also be 1124 | durable. 1125 | 1126 | Exchanges declared as `internal` do not accept accept publishings. Internal 1127 | exchanges are useful when you wish to implement inter-exchange topologies 1128 | that should not be exposed to users of the broker. 1129 | 1130 | When noWait is true, declare without waiting for a confirmation from the server. 1131 | The channel may be closed as a result of an error. Add a NotifyClose listener 1132 | to respond to any exceptions. 1133 | 1134 | Optional amqp.Table of arguments that are specific to the server's implementation of 1135 | the exchange can be sent for exchange types that require extra parameters. 1136 | */ 1137 | func (ch *Channel) ExchangeDeclare(name, kind string, durable, autoDelete, internal, noWait bool, args Table) error { 1138 | if err := args.Validate(); err != nil { 1139 | return err 1140 | } 1141 | 1142 | return ch.call( 1143 | &exchangeDeclare{ 1144 | Exchange: name, 1145 | Type: kind, 1146 | Passive: false, 1147 | Durable: durable, 1148 | AutoDelete: autoDelete, 1149 | Internal: internal, 1150 | NoWait: noWait, 1151 | Arguments: args, 1152 | }, 1153 | &exchangeDeclareOk{}, 1154 | ) 1155 | } 1156 | 1157 | /* 1158 | 1159 | ExchangeDeclarePassive is functionally and parametrically equivalent to 1160 | ExchangeDeclare, except that it sets the "passive" attribute to true. A passive 1161 | exchange is assumed by RabbitMQ to already exist, and attempting to connect to a 1162 | non-existent exchange will cause RabbitMQ to throw an exception. This function 1163 | can be used to detect the existence of an exchange. 1164 | 1165 | */ 1166 | func (ch *Channel) ExchangeDeclarePassive(name, kind string, durable, autoDelete, internal, noWait bool, args Table) error { 1167 | if err := args.Validate(); err != nil { 1168 | return err 1169 | } 1170 | 1171 | return ch.call( 1172 | &exchangeDeclare{ 1173 | Exchange: name, 1174 | Type: kind, 1175 | Passive: true, 1176 | Durable: durable, 1177 | AutoDelete: autoDelete, 1178 | Internal: internal, 1179 | NoWait: noWait, 1180 | Arguments: args, 1181 | }, 1182 | &exchangeDeclareOk{}, 1183 | ) 1184 | } 1185 | 1186 | /* 1187 | ExchangeDelete removes the named exchange from the server. When an exchange is 1188 | deleted all queue bindings on the exchange are also deleted. If this exchange 1189 | does not exist, the channel will be closed with an error. 1190 | 1191 | When ifUnused is true, the server will only delete the exchange if it has no queue 1192 | bindings. If the exchange has queue bindings the server does not delete it 1193 | but close the channel with an exception instead. Set this to true if you are 1194 | not the sole owner of the exchange. 1195 | 1196 | When noWait is true, do not wait for a server confirmation that the exchange has 1197 | been deleted. Failing to delete the channel could close the channel. Add a 1198 | NotifyClose listener to respond to these channel exceptions. 1199 | */ 1200 | func (ch *Channel) ExchangeDelete(name string, ifUnused, noWait bool) error { 1201 | return ch.call( 1202 | &exchangeDelete{ 1203 | Exchange: name, 1204 | IfUnused: ifUnused, 1205 | NoWait: noWait, 1206 | }, 1207 | &exchangeDeleteOk{}, 1208 | ) 1209 | } 1210 | 1211 | /* 1212 | ExchangeBind binds an exchange to another exchange to create inter-exchange 1213 | routing topologies on the server. This can decouple the private topology and 1214 | routing exchanges from exchanges intended solely for publishing endpoints. 1215 | 1216 | Binding two exchanges with identical arguments will not create duplicate 1217 | bindings. 1218 | 1219 | Binding one exchange to another with multiple bindings will only deliver a 1220 | message once. For example if you bind your exchange to `amq.fanout` with two 1221 | different binding keys, only a single message will be delivered to your 1222 | exchange even though multiple bindings will match. 1223 | 1224 | Given a message delivered to the source exchange, the message will be forwarded 1225 | to the destination exchange when the routing key is matched. 1226 | 1227 | ExchangeBind("sell", "MSFT", "trade", false, nil) 1228 | ExchangeBind("buy", "AAPL", "trade", false, nil) 1229 | 1230 | Delivery Source Key Destination 1231 | example exchange exchange 1232 | ----------------------------------------------- 1233 | key: AAPL --> trade ----> MSFT sell 1234 | \---> AAPL --> buy 1235 | 1236 | When noWait is true, do not wait for the server to confirm the binding. If any 1237 | error occurs the channel will be closed. Add a listener to NotifyClose to 1238 | handle these errors. 1239 | 1240 | Optional arguments specific to the exchanges bound can also be specified. 1241 | */ 1242 | func (ch *Channel) ExchangeBind(destination, key, source string, noWait bool, args Table) error { 1243 | if err := args.Validate(); err != nil { 1244 | return err 1245 | } 1246 | 1247 | return ch.call( 1248 | &exchangeBind{ 1249 | Destination: destination, 1250 | Source: source, 1251 | RoutingKey: key, 1252 | NoWait: noWait, 1253 | Arguments: args, 1254 | }, 1255 | &exchangeBindOk{}, 1256 | ) 1257 | } 1258 | 1259 | /* 1260 | ExchangeUnbind unbinds the destination exchange from the source exchange on the 1261 | server by removing the routing key between them. This is the inverse of 1262 | ExchangeBind. If the binding does not currently exist, an error will be 1263 | returned. 1264 | 1265 | When noWait is true, do not wait for the server to confirm the deletion of the 1266 | binding. If any error occurs the channel will be closed. Add a listener to 1267 | NotifyClose to handle these errors. 1268 | 1269 | Optional arguments that are specific to the type of exchanges bound can also be 1270 | provided. These must match the same arguments specified in ExchangeBind to 1271 | identify the binding. 1272 | */ 1273 | func (ch *Channel) ExchangeUnbind(destination, key, source string, noWait bool, args Table) error { 1274 | if err := args.Validate(); err != nil { 1275 | return err 1276 | } 1277 | 1278 | return ch.call( 1279 | &exchangeUnbind{ 1280 | Destination: destination, 1281 | Source: source, 1282 | RoutingKey: key, 1283 | NoWait: noWait, 1284 | Arguments: args, 1285 | }, 1286 | &exchangeUnbindOk{}, 1287 | ) 1288 | } 1289 | 1290 | /* 1291 | Publish sends a Publishing from the client to an exchange on the server. 1292 | 1293 | When you want a single message to be delivered to a single queue, you can 1294 | publish to the default exchange with the routingKey of the queue name. This is 1295 | because every declared queue gets an implicit route to the default exchange. 1296 | 1297 | Since publishings are asynchronous, any undeliverable message will get returned 1298 | by the server. Add a listener with Channel.NotifyReturn to handle any 1299 | undeliverable message when calling publish with either the mandatory or 1300 | immediate parameters as true. 1301 | 1302 | Publishings can be undeliverable when the mandatory flag is true and no queue is 1303 | bound that matches the routing key, or when the immediate flag is true and no 1304 | consumer on the matched queue is ready to accept the delivery. 1305 | 1306 | This can return an error when the channel, connection or socket is closed. The 1307 | error or lack of an error does not indicate whether the server has received this 1308 | publishing. 1309 | 1310 | It is possible for publishing to not reach the broker if the underlying socket 1311 | is shut down without pending publishing packets being flushed from the kernel 1312 | buffers. The easy way of making it probable that all publishings reach the 1313 | server is to always call Connection.Close before terminating your publishing 1314 | application. The way to ensure that all publishings reach the server is to add 1315 | a listener to Channel.NotifyPublish and put the channel in confirm mode with 1316 | Channel.Confirm. Publishing delivery tags and their corresponding 1317 | confirmations start at 1. Exit when all publishings are confirmed. 1318 | 1319 | When Publish does not return an error and the channel is in confirm mode, the 1320 | internal counter for DeliveryTags with the first confirmation starts at 1. 1321 | 1322 | */ 1323 | func (ch *Channel) Publish(exchange, key string, mandatory, immediate bool, msg Publishing) error { 1324 | if err := msg.Headers.Validate(); err != nil { 1325 | return err 1326 | } 1327 | 1328 | ch.m.Lock() 1329 | defer ch.m.Unlock() 1330 | 1331 | if err := ch.send(&basicPublish{ 1332 | Exchange: exchange, 1333 | RoutingKey: key, 1334 | Mandatory: mandatory, 1335 | Immediate: immediate, 1336 | Body: msg.Body, 1337 | Properties: properties{ 1338 | Headers: msg.Headers, 1339 | ContentType: msg.ContentType, 1340 | ContentEncoding: msg.ContentEncoding, 1341 | DeliveryMode: msg.DeliveryMode, 1342 | Priority: msg.Priority, 1343 | CorrelationId: msg.CorrelationId, 1344 | ReplyTo: msg.ReplyTo, 1345 | Expiration: msg.Expiration, 1346 | MessageId: msg.MessageId, 1347 | Timestamp: msg.Timestamp, 1348 | Type: msg.Type, 1349 | UserId: msg.UserId, 1350 | AppId: msg.AppId, 1351 | }, 1352 | }); err != nil { 1353 | return err 1354 | } 1355 | 1356 | if ch.confirming { 1357 | ch.confirms.Publish() 1358 | } 1359 | 1360 | return nil 1361 | } 1362 | 1363 | /* 1364 | Get synchronously receives a single Delivery from the head of a queue from the 1365 | server to the client. In almost all cases, using Channel.Consume will be 1366 | preferred. 1367 | 1368 | If there was a delivery waiting on the queue and that delivery was received, the 1369 | second return value will be true. If there was no delivery waiting or an error 1370 | occurred, the ok bool will be false. 1371 | 1372 | All deliveries must be acknowledged including those from Channel.Get. Call 1373 | Delivery.Ack on the returned delivery when you have fully processed this 1374 | delivery. 1375 | 1376 | When autoAck is true, the server will automatically acknowledge this message so 1377 | you don't have to. But if you are unable to fully process this message before 1378 | the channel or connection is closed, the message will not get requeued. 1379 | 1380 | */ 1381 | func (ch *Channel) Get(queue string, autoAck bool) (msg Delivery, ok bool, err error) { 1382 | req := &basicGet{Queue: queue, NoAck: autoAck} 1383 | res := &basicGetOk{} 1384 | empty := &basicGetEmpty{} 1385 | 1386 | if err := ch.call(req, res, empty); err != nil { 1387 | return Delivery{}, false, err 1388 | } 1389 | 1390 | if res.DeliveryTag > 0 { 1391 | return *(newDelivery(ch, res)), true, nil 1392 | } 1393 | 1394 | return Delivery{}, false, nil 1395 | } 1396 | 1397 | /* 1398 | Tx puts the channel into transaction mode on the server. All publishings and 1399 | acknowledgments following this method will be atomically committed or rolled 1400 | back for a single queue. Call either Channel.TxCommit or Channel.TxRollback to 1401 | leave a this transaction and immediately start a new transaction. 1402 | 1403 | The atomicity across multiple queues is not defined as queue declarations and 1404 | bindings are not included in the transaction. 1405 | 1406 | The behavior of publishings that are delivered as mandatory or immediate while 1407 | the channel is in a transaction is not defined. 1408 | 1409 | Once a channel has been put into transaction mode, it cannot be taken out of 1410 | transaction mode. Use a different channel for non-transactional semantics. 1411 | 1412 | */ 1413 | func (ch *Channel) Tx() error { 1414 | return ch.call( 1415 | &txSelect{}, 1416 | &txSelectOk{}, 1417 | ) 1418 | } 1419 | 1420 | /* 1421 | TxCommit atomically commits all publishings and acknowledgments for a single 1422 | queue and immediately start a new transaction. 1423 | 1424 | Calling this method without having called Channel.Tx is an error. 1425 | 1426 | */ 1427 | func (ch *Channel) TxCommit() error { 1428 | return ch.call( 1429 | &txCommit{}, 1430 | &txCommitOk{}, 1431 | ) 1432 | } 1433 | 1434 | /* 1435 | TxRollback atomically rolls back all publishings and acknowledgments for a 1436 | single queue and immediately start a new transaction. 1437 | 1438 | Calling this method without having called Channel.Tx is an error. 1439 | 1440 | */ 1441 | func (ch *Channel) TxRollback() error { 1442 | return ch.call( 1443 | &txRollback{}, 1444 | &txRollbackOk{}, 1445 | ) 1446 | } 1447 | 1448 | /* 1449 | Flow pauses the delivery of messages to consumers on this channel. Channels 1450 | are opened with flow control not active, to open a channel with paused 1451 | deliveries immediately call this method with true after calling 1452 | Connection.Channel. 1453 | 1454 | When active is true, this method asks the server to temporarily pause deliveries 1455 | until called again with active as false. 1456 | 1457 | Channel.Get methods will not be affected by flow control. 1458 | 1459 | This method is not intended to act as window control. Use Channel.Qos to limit 1460 | the number of unacknowledged messages or bytes in flight instead. 1461 | 1462 | The server may also send us flow methods to throttle our publishings. A well 1463 | behaving publishing client should add a listener with Channel.NotifyFlow and 1464 | pause its publishings when true is sent on that channel. 1465 | 1466 | Note: RabbitMQ prefers to use TCP push back to control flow for all channels on 1467 | a connection, so under high volume scenarios, it's wise to open separate 1468 | Connections for publishings and deliveries. 1469 | 1470 | */ 1471 | func (ch *Channel) Flow(active bool) error { 1472 | return ch.call( 1473 | &channelFlow{Active: active}, 1474 | &channelFlowOk{}, 1475 | ) 1476 | } 1477 | 1478 | /* 1479 | Confirm puts this channel into confirm mode so that the client can ensure all 1480 | publishings have successfully been received by the server. After entering this 1481 | mode, the server will send a basic.ack or basic.nack message with the deliver 1482 | tag set to a 1 based incremental index corresponding to every publishing 1483 | received after the this method returns. 1484 | 1485 | Add a listener to Channel.NotifyPublish to respond to the Confirmations. If 1486 | Channel.NotifyPublish is not called, the Confirmations will be silently 1487 | ignored. 1488 | 1489 | The order of acknowledgments is not bound to the order of deliveries. 1490 | 1491 | Ack and Nack confirmations will arrive at some point in the future. 1492 | 1493 | Unroutable mandatory or immediate messages are acknowledged immediately after 1494 | any Channel.NotifyReturn listeners have been notified. Other messages are 1495 | acknowledged when all queues that should have the message routed to them have 1496 | either received acknowledgment of delivery or have enqueued the message, 1497 | persisting the message if necessary. 1498 | 1499 | When noWait is true, the client will not wait for a response. A channel 1500 | exception could occur if the server does not support this method. 1501 | 1502 | */ 1503 | func (ch *Channel) Confirm(noWait bool) error { 1504 | if err := ch.call( 1505 | &confirmSelect{Nowait: noWait}, 1506 | &confirmSelectOk{}, 1507 | ); err != nil { 1508 | return err 1509 | } 1510 | 1511 | ch.confirmM.Lock() 1512 | ch.confirming = true 1513 | ch.confirmM.Unlock() 1514 | 1515 | return nil 1516 | } 1517 | 1518 | /* 1519 | Recover redelivers all unacknowledged deliveries on this channel. 1520 | 1521 | When requeue is false, messages will be redelivered to the original consumer. 1522 | 1523 | When requeue is true, messages will be redelivered to any available consumer, 1524 | potentially including the original. 1525 | 1526 | If the deliveries cannot be recovered, an error will be returned and the channel 1527 | will be closed. 1528 | 1529 | Note: this method is not implemented on RabbitMQ, use Delivery.Nack instead 1530 | */ 1531 | func (ch *Channel) Recover(requeue bool) error { 1532 | return ch.call( 1533 | &basicRecover{Requeue: requeue}, 1534 | &basicRecoverOk{}, 1535 | ) 1536 | } 1537 | 1538 | /* 1539 | Ack acknowledges a delivery by its delivery tag when having been consumed with 1540 | Channel.Consume or Channel.Get. 1541 | 1542 | Ack acknowledges all message received prior to the delivery tag when multiple 1543 | is true. 1544 | 1545 | See also Delivery.Ack 1546 | */ 1547 | func (ch *Channel) Ack(tag uint64, multiple bool) error { 1548 | return ch.send(&basicAck{ 1549 | DeliveryTag: tag, 1550 | Multiple: multiple, 1551 | }) 1552 | } 1553 | 1554 | /* 1555 | Nack negatively acknowledges a delivery by its delivery tag. Prefer this 1556 | method to notify the server that you were not able to process this delivery and 1557 | it must be redelivered or dropped. 1558 | 1559 | See also Delivery.Nack 1560 | */ 1561 | func (ch *Channel) Nack(tag uint64, multiple bool, requeue bool) error { 1562 | return ch.send(&basicNack{ 1563 | DeliveryTag: tag, 1564 | Multiple: multiple, 1565 | Requeue: requeue, 1566 | }) 1567 | } 1568 | 1569 | /* 1570 | Reject negatively acknowledges a delivery by its delivery tag. Prefer Nack 1571 | over Reject when communicating with a RabbitMQ server because you can Nack 1572 | multiple messages, reducing the amount of protocol messages to exchange. 1573 | 1574 | See also Delivery.Reject 1575 | */ 1576 | func (ch *Channel) Reject(tag uint64, requeue bool) error { 1577 | return ch.send(&basicReject{ 1578 | DeliveryTag: tag, 1579 | Requeue: requeue, 1580 | }) 1581 | } 1582 | --------------------------------------------------------------------------------