Golang RabbitMQ - go

I followed the tutorial examples, but it reads all message from queue once, how can I read only one message from a queue? Appreciate!
messages, err := channelRabbitMQ.Consume(
"QueueService1", // queue name
"", // consumer
true, // auto-ack
false, // exclusive
false, // no local
false, // no wait
nil, // arguments
)
if err != nil {
log.Println(err)
}
msg := <-messages
fmt.Println(string(msg.Body))

You can use Qos to configure channel to only receive 1 message at a time
url := "..."
queue := "..."
conn, err := amqp.Dial(url)
if err != nil {
log.Fatal("Cannot connect to rabbitmq")
}
ch, err := conn.Channel()
if err != nil {
log.Fatal("Cannot create channel")
}
if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil {
log.Fatal("Cannot create queue")
}
// Indicate we only want 1 message to acknowledge at a time.
if err := ch.Qos(1, 0, false); err != nil {
log.Fatal("Qos Setting was unsuccessfull")
}
// Exclusive consumer
messages, err := ch.Consume(queue, "", false, true, false, false, nil)
msg := <-messages
fmt.Println(string(msg.Body))

Related

How to know when the consumer stop consuming

I wanted to create a client that pings to notify the server that it is still online, and when the client stops pinging, the server notifies that there is a problem with the client by sending a message to the terminal.
code for illustrate :
Server :
import (
"log"
"github.com/streadway/amqp"
)
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
func main() {
conn, err := amqp.Dial("amqp://guest:guest#localhost:5672/")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
err = ch.ExchangeDeclare(
"logs", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare an exchange")
q, err := ch.QueueDeclare(
"", // name
false, // durable
false, // delete when unused
true, // exclusive
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare a queue")
err = ch.QueueBind(
q.Name, // queue name
"", // routing key
"logs", // exchange
false,
nil,
)
failOnError(err, "Failed to bind a queue")
msgs, err := ch.Consume(
q.Name, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
failOnError(err, "Failed to register a consumer")
forever := make(chan bool)
go func() {
for d := range msgs {
log.Printf(" [x] %s", d.Body)
}
}()
log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
<-forever
}
Client :
package main
import (
"github.com/streadway/amqp"
"log"
"time"
)
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
func main() {
conn, err := amqp.Dial("amqp://guest:guest#localhost:5672/")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
err = ch.ExchangeDeclare(
"logs", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare an exchange")
body := "ping"
for i := 0; i < 4; i++ {
err = ch.Publish(
"logs", // exchange
"", // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(body),
})
time.Sleep(3 * time.Second)
failOnError(err, "Failed to publish a message")
log.Printf(" [x] %s sent to the server", body)
}
}
I was wondering, how can we know when the consumer stop consuming. Maybe with this information i will be able to know when the client is dead ?

golang rabbitmq channel.consume SIGSEGV

Folks,
Am trying to write a wrapper library for the rabbitmq implementation. For some strange reason, I am not able to consume from an existing queue.
msgs, err := w.AMQP.Channel.Consume( is causing:
2019-10-14T13:58:56.462-0400 info worker/worker.go:27 [Initializing NewWorker]
2019-10-14T13:58:56.462-0400 debug worker/worker.go:43 Starting Worker
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0x14d024e]
The docs example I followed is simple, and the interface to the rabbitmq driver is simple. I dont see why im getting a invalid memory address error.
My implementation is identical to how the article lays it out, for reference, here is all the code i have:
// StartConsumingTlcFHVDrivers subscribes to queue and starts to do it's job
func (w *Worker) StartConsuming() {
queueName := w.options.Rabbit.AMQP.QueueName
w.logger.Debugf("Starting %s Worker", queueName)
msgs, err := w.AMQP.Channel.Consume(
queueName,
"",
false,
false,
false,
false,
nil,
)
if err != nil {
logger.Fatalf("Could not register consumer, err:%s", err)
}
forever := make(chan bool)
go func() {
for d := range msgs {
w.logger.Infof("Received a message: %s", d.Body)
}
}()
w.logger.Infof("Waiting for messages on %s", queueName)
<-forever
}
Before I finish the wrapper with my own interface, im setting up the client this way:
import ""github.com/streadway/amqp""
type Client struct {
options Options
logger ilogger.Logger
metrics imetrics.Client
Channel amqp.Channel
}
func NewRabbitClient(logger ilogger.Logger, metrics imetrics.Client, options Options) *Client {
var err error
conn, err := amqp.Dial(options.AMQPConnectionURL)
if err != nil {
logger.Fatalf("%s: %s", "Can't connect to AMQP", err)
}
defer conn.Close()
// create a dedicated channel per queue
channel, err := conn.Channel()
if err != nil {
logger.Fatalf("%s: %s", "Can't create a amqpChannel", err)
}
defer channel.Close()
// declare an Exchange
err = channel.ExchangeDeclare(options.ExchangeName, options.ExchangeType, true, false, false, false, nil)
// declare a Queue
_, err = channel.QueueDeclare(options.QueueName, true, false, false, false, nil)
if err != nil {
logger.Fatalf("Could not declare %s queue, err:%s", options.QueueName, err)
}
// bind a Queue to the Exchange
err = channel.QueueBind(options.QueueName, "", options.ExchangeName, false, nil)
// Qos on the Channel
err = channel.Qos(1, 0, false)
if err != nil {
logger.Fatalf("Could not configure QoS, err:%s", err)
}
return &Client{
metrics: metrics,
logger: logger,
options: options,
Channel: *channel,
}
}
The error is now obvious to me. In the constructor function, do not close the connection/channel. i.e.
conn, err := amqp.Dial(options.AMQPConnectionURL)
if err != nil {
logger.Fatalf("%s: %s", "Can't connect to AMQP", err)
}
instead of
conn, err := amqp.Dial(options.AMQPConnectionURL)
if err != nil {
logger.Fatalf("%s: %s", "Can't connect to AMQP", err)
}
defer conn.Close()
```

Can't return *amqp.Channel from a function when using the Go RabbitMQ streadway/amqp driver

I'm trying to connect to a RabbitMQ bus using the streadway/amqp driver for Go. I'm working on a reconnection routine and, for it, I have a rabbitMQConsume function call a rabbitMQConnect function.
func rabbitMQConnect(cfg objects.GlobalConfig) (*amqp.Connection, *amqp.Channel, error) {
rabbitConfig := amqp.Config{
Vhost: cfg.RabbitVHost,
Heartbeat: 5,
}
//Open connection to Rabbit
url := fmt.Sprintf("amqp://" + cfg.RabbitUser + ":" + cfg.RabbitPassword + "#" + cfg.RabbitHost + ":" + cfg.RabbitPort + cfg.RabbitVHost)
conn, err := amqp.DialConfig(url, rabbitConfig)
if err == nil {
return nil, nil, err
}
ch, err := conn.Channel()
if err != nil {
return nil, nil, err
}
// Create Exchange if it doesn't exist
err = ch.ExchangeDeclare(
"ali", // name
"direct", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
if err != nil {
return nil, nil, err
}
//Declare queue
_, err = ch.QueueDeclare(
cfg.RabbitQueue, // name
true, // durable
false, // delete when usused
false, // exclusive
false, // no-wait
nil, // arguments
)
if err != nil {
return nil, nil, err
}
//Bind queue
err = ch.QueueBind(
cfg.RabbitQueue, // queue name
cfg.RabbitKey, // routing key
cfg.RabbitExchange, // exchange
false,
nil,
)
if err != nil {
return nil, nil, err
}
return conn, ch, nil
}
//RabbitMQConsume setup the channel/exchange data
func rabbitMQConsume(cfg objects.GlobalConfig) (*amqp.Connection, <-chan amqp.Delivery, error) {
conn, ch, err := rabbitMQConnect(cfg)
if err != nil {
return nil, nil, err
}
consumerID, err := helper.GetConsumerID()
if err != nil {
return nil, nil, err
}
//Start receiving data in the msgs channel
msgs, err := ch.Consume(
cfg.RabbitQueue, // queue
consumerID, // consumer
false, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
if err != nil {
return nil, nil, err
}
return conn, msgs, nil
}
The problem I'm having is that the value of ch and conn when they're returned to rabbitMQConsume from rabbitMQconnect, are nil and the program panics when I run the ch.Consume line.
I'm losely basing this on this example
Any ideas? Thanks!
You have a typo in your error checking after amqp.DialConfig !
Change the err == nil to err != nil
conn, err := amqp.DialConfig(url, rabbitConfig)
if err != nil { // you typed it as err == nil
return nil, nil, err
}

RabbitMQ pub/sub implementation not working

I've converted the RabbitMQ pub/sub tutorial into the below dummy test. Somehow it is not working as expected.
amqpURL is a valid AMQP service (i.e. RabbitMQ) URL. I've tested it with the queue example and it works. Somehow it fails in "exchange"
I'd expect TestDummy to log " [x] Hello World". Somehow it is not happening. Only the sending half is working as expected.
What did I got wrong?
import (
"fmt"
"log"
"testing"
"github.com/streadway/amqp"
)
func TestDummy(t *testing.T) {
done := exchangeReceive()
exchangeSend("Hello World")
<-done
}
func exchangeSend(msg string) {
failOnError := func(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
panic(fmt.Sprintf("%s: %s", msg, err))
}
}
log.Printf("exchangeSend: connect %s", amqpURL)
conn, err := amqp.Dial(amqpURL)
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
err = ch.ExchangeDeclare(
"logs", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare an exchange")
body := []byte(msg)
err = ch.Publish(
"logs", // exchange
"", // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(body),
})
failOnError(err, "Failed to publish a message")
log.Printf(" [x] Sent %s", body)
}
func exchangeReceive() <-chan bool {
done := make(chan bool)
failOnError := func(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
panic(fmt.Sprintf("%s: %s", msg, err))
}
}
log.Printf("exchangeReceive: connect %s", amqpURL)
conn, err := amqp.Dial(amqpURL)
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
err = ch.ExchangeDeclare(
"logs", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare an exchange")
q, err := ch.QueueDeclare(
"", // name
false, // durable
false, // delete when usused
true, // exclusive
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare a queue")
err = ch.QueueBind(
q.Name, // queue name
"", // routing key
"logs", // exchange
false,
nil)
failOnError(err, "Failed to bind a queue")
msgs, err := ch.Consume(
q.Name, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
failOnError(err, "Failed to register a consumer")
go func() {
for d := range msgs {
log.Printf(" [x] %s", d.Body)
done <- true
}
}()
log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
return done
}
Some silly mistake here. When exchangeRecieve ends, the defer statments are triggered and hence closed the connections. That's why my rewrite fails.
I've changed my code this way and it worked:
import (
"fmt"
"os"
"testing"
"time"
"github.com/streadway/amqp"
)
func TestDummy(t *testing.T) {
amqpURL := os.Getenv("CLOUDAMQP_URL")
t.Logf(" [*] amqpURL: %s", amqpURL)
results1 := exchangeReceive(t, "consumer 1", amqpURL)
results2 := exchangeReceive(t, "consumer 2", amqpURL)
time.Sleep(50 * time.Millisecond)
exchangeSend(t, amqpURL, "Hello World")
if want, have := "Hello World", <-results1; want != have {
t.Errorf("expected %#v, got %#v", want, have)
}
if want, have := "Hello World", <-results2; want != have {
t.Errorf("expected %#v, got %#v", want, have)
}
}
func exchangeReceive(t *testing.T, name, amqpURL string) <-chan string {
out := make(chan string)
failOnError := func(err error, msg string) {
if err != nil {
t.Fatalf("%s: %s", msg, err)
panic(fmt.Sprintf("%s: %s", msg, err))
}
}
conn, err := amqp.Dial(amqpURL)
failOnError(err, "Failed to connect to RabbitMQ")
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
err = ch.ExchangeDeclare(
"logs", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare an exchange")
q, err := ch.QueueDeclare(
"", // name
false, // durable
false, // delete when usused
true, // exclusive
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare a queue")
err = ch.QueueBind(
q.Name, // queue name
"", // routing key
"logs", // exchange
false,
nil)
failOnError(err, "Failed to bind a queue")
msgs, err := ch.Consume(
q.Name, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
failOnError(err, "Failed to register a consumer")
go func() {
for d := range msgs {
t.Logf(" [x] %s received: %s", name, d.Body)
out <- string(d.Body)
}
}()
t.Logf(" [*] %s ready to receive", name)
return out
}
func exchangeSend(t *testing.T, amqpURL, msg string) {
failOnError := func(err error, msg string) {
if err != nil {
t.Fatalf("%s: %s", msg, err)
panic(fmt.Sprintf("%s: %s", msg, err))
}
}
conn, err := amqp.Dial(amqpURL)
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
err = ch.ExchangeDeclare(
"logs", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare an exchange")
body := []byte(msg)
err = ch.Publish(
"logs", // exchange
"", // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(body),
})
failOnError(err, "Failed to publish a message")
t.Logf(" [x] Sent %s", body)
}

Could one connection support multiple channels in go api for rabbitmq?

package main
import (
"fmt"
"github.com/streadway/amqp"
"time"
)
// Every connection should declare the topology they expect
func setup(url, queue string) (*amqp.Connection, *amqp.Channel, error) {
//setup connection
conn, err := amqp.Dial(url)
if err != nil {
return nil, nil, err
}
//build channel in the connection
ch, err := conn.Channel()
if err != nil {
return nil, nil, err
}
//queue declare
if _, err := ch.QueueDeclare(queue, false, true, false, false, nil); err != nil {
return nil, nil, err
}
return conn, ch, nil
}
func main() {
//amqp url
url := "amqp://guest:guest#127.0.0.1:5672";
for i := 1; i <= 2; i++ {
fmt.Println("connect ", i)
//two goroutine
go func() {
//queue name
queue := fmt.Sprintf("example.reconnect.%d", i)
//setup channel in the tcp connection
_, pub, err := setup(url, queue)
if err != nil {
fmt.Println("err publisher setup:", err)
return
}
// Purge the queue from the publisher side to establish initial state
if _, err := pub.QueuePurge(queue, false); err != nil {
fmt.Println("err purge:", err)
return
}
//publish msg
if err := pub.Publish("", queue, false, false, amqp.Publishing{
Body: []byte(fmt.Sprintf("%d", i)),
}); err != nil {
fmt.Println("err publish:", err)
return
}
//keep running
for{
time.Sleep(time.Second * 20)
}
}()
}
//keep running
for {
time.Sleep(time.Second * 20)
}
}
I thought there is only one connection between the program and mq-server,
but there are two connection,one connection can only support one channel,why?
can't the two goroutine share the same tcp connection?
Socket descriptor can share in all threads of a process in the theory.
Why the two goroutine don't share one socket but have their own channel?
The model by hand:
The real model in rabbitmq:
Looking at the source for the library it appears as though you can call conn.Channel() as many times as you like and it creates a new stream of communication over the same connection.
Ok, I tried it, here's a working example... One goroutine, one connection, two channels
I setup the receiver, then send a message, then read from the receiver channel
if you wanted multiple queue's bound in one goroutine, you would call rec.Consume twice and then select across the queues.
package main
import (
"fmt"
"github.com/streadway/amqp"
"os"
)
func main() {
conn, err := amqp.Dial("amqp://localhost")
e(err)
defer conn.Close()
fmt.Println("Connected")
rec, err := conn.Channel()
e(err)
fmt.Println("Setup receiver")
rq, err := rec.QueueDeclare("go-test", false, false, false, false, nil)
e(err)
msgs, err := rec.Consume(rq.Name, "", true, false, false, false, nil)
e(err)
fmt.Println("Setup sender")
send, err := conn.Channel()
e(err)
sq, err := send.QueueDeclare("go-test", false, false, false, false, nil)
e(err)
fmt.Println("Send message")
err = send.Publish("", sq.Name, false, false, amqp.Publishing{
ContentType: "text/plain",
Body: []byte("This is a test"),
})
e(err)
msg := <-msgs
fmt.Println("Received from:", rq, "msg:", string(msg.Body))
}
func e(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Output on my box:
$ go run rmq.go
Connected
Setup receiver
Setup sender
Send message
Received from: {go-test 0 0} msg: This is a test

Resources