消息ACK

1.消息ACK

如果不进行ACK,当消费端挂掉,比如channel关闭、connection关闭、TCPconnection关闭等都会使得消息丢失,而不进行重发。所以需要ACK,为了测试,关掉自动ACK选项,自己手动ACK,当接受到消息,sleep几秒再ACK

[plain] view plain  copy
 
  1. msgs, err := ch.Consume(  
  2.         q.Name, // queue  
  3.         "",     // consumer  
  4.         false,  // auto-ack   不进行自动ACK  
  5.         false,  // exclusive  
  6.         false,  // no-local  
  7.         false,  // no-wait  
  8.         nil,    // args  
  9.     )  
  10.     failOnError(err, "Failed to register a consumer")  
  11.   
  12.     forever := make(chan bool)  
  13.   
  14.     go func() {  
  15.         for d := range msgs {  // msgs 是一个channel,从中取东西  
  16.             log.Printf("Received a message: %s", d.Body)  
  17.             dot_count := bytes.Count(d.Body, []byte("."))  // 统计d.Body中的"."的个数  
  18.             t := time.Duration(dot_count)  
  19.             time.Sleep(t * time.Second) // 有几个点就sleep几秒  
  20.             log.Printf("Done")  
  21.             d.Ack(false)  // 手动ACK,如果不ACK的话,那么无法保证这个消息被处理,可能它已经丢失了(比如消息队列挂了)  
  22.         }  
  23.     }()  

2.消息持久化

消息ACK保证了消息不会丢失,但是当rabbitMQ Server停止(不是consumer 挂掉)的时候,我们的所有消息都会丢失。

针对这种情况,我们先确保消息队列的持久化,设置消息队列的durable选项为true

[plain] view plain  copy
 
  1. q, err := ch.QueueDeclare(  
  2.         "task_queue", // name  
  3.         true,         // durable 队列持久化  
  4.         false,        // delete when unused  
  5.         false,        // exclusive  
  6.         false,        // no-wait  
  7.         nil,          // arguments  
  8.     )  
  9.     failOnError(err, "Failed to declare a queue")  

队列持久化了,接下来就是消息持久化,使用amqp.Persistent选项

[plain] view plain  copy
 
  1. body := bodyFrom(os.Args)  
  2.     err = ch.Publish(  
  3.         "",     // exchange  
  4.         q.Name, // routing key  
  5.         false,  // mandatory  
  6.         false,  
  7.         amqp.Publishing{  
  8.             DeliveryMode: amqp.Persistent, // 消息持久化,虽然消息设置持久化了,但是并不能保证一定会  
  9.             ContentType:  "text/plain",  
  10.             Body:         []byte(body),  
  11.         })  
  12.     failOnError(err, "Failed to publish a message")  
  13.     log.Printf(" [x] Sent %s", body)  

这种方式并不能保证消息一定持久化到硬盘中,可以还未来的及写入硬盘

3.消息的公平分派

设置Qos,设置预取大小prefetch,当prefetch=1时,表示在没收到consumer的ACK消息之前,只会为其consumer分派一个消息

[plain] view plain  copy
 
  1. // 为了保证公平分发,不至于其中某个consumer一直处理,而其他不处理  
  2.     err = ch.Qos(  
  3.         1,     // prefetch count  在server收到consumer的ACK之前,预取的数量。为1,表示在没收到consumer的ACK之前,只会为其分发一个消息  
  4.         0,     // prefetch size 大于0时,表示在收到consumer确认消息之前,将size个字节保留在网络中  
  5.         false, // global  true:Qos对同一个connection的所有channel有效; false:Qos对同一个channel上的所有consumer有效  
  6.     )  

4.producer端完整程序

[plain] view plain  copy
 
  1. package main  
  2.   
  3. import (  
  4.     "log"  
  5.     "os"  
  6.     "strings"  
  7.   
  8.     "github.com/streadway/amqp"  
  9. )  
  10.   
  11. func failOnError(err error, msg string) {  
  12.     if err != nil {  
  13.         log.Fatalf("%s: %s", msg, err)  
  14.     }  
  15. }  
  16.   
  17. func main() {  
  18.     conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")  
  19.     failOnError(err, "Failed to connect to RabbitMQ")  
  20.     defer conn.Close()  
  21.   
  22.     ch, err := conn.Channel()  
  23.     failOnError(err, "Failed to open a channel")  
  24.     defer ch.Close()  
  25.   
  26.     q, err := ch.QueueDeclare(  
  27.         "task_queue", // name  
  28.         true,         // durable  发送与接送两端队列都要持久化,队列持久化了,但是并不能保证消息也持久化  
  29.         false,        // delete when unused  
  30.         false,        // exclusive  
  31.         false,        // no-wait  
  32.         nil,          // arguments  
  33.     )  
  34.     failOnError(err, "Failed to declare a queue")  
  35.   
  36.     body := bodyFrom(os.Args)  
  37.     err = ch.Publish(  
  38.         "",     // exchange  
  39.         q.Name, // routing key  
  40.         false,  // mandatory  
  41.         false,  
  42.         amqp.Publishing{  
  43.             DeliveryMode: amqp.Persistent, // 消息持久化,虽然消息设置持久化了,但是并不能保证一定会  
  44.             ContentType:  "text/plain",  
  45.             Body:         []byte(body),  
  46.         })  
  47.     failOnError(err, "Failed to publish a message")  
  48.     log.Printf(" [x] Sent %s", body)  
  49. }  
  50.   
  51. func bodyFrom(args []string) string {  
  52.     var s string  
  53.     if (len(args) < 2) || os.Args[1] == "" {  
  54.         s = "hello"  
  55.     } else {  
  56.         s = strings.Join(args[1:], " ")  
  57.     }  
  58.     return s  
  59. }  

5.consumer端完整代码

[plain] view plain  copy
 
  1. package main  
  2.   
  3. import (  
  4.     "bytes"  
  5.     "log"  
  6.     "time"  
  7.   
  8.     "github.com/streadway/amqp"  
  9. )  
  10.   
  11. func failOnError(err error, msg string) {  
  12.     if err != nil {  
  13.         log.Fatalf("%s: %s", msg, err)  
  14.     }  
  15. }  
  16.   
  17. func main() {  
  18.     conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")  
  19.     failOnError(err, "Failed to connect to RabbitMQ")  
  20.     defer conn.Close()  
  21.   
  22.     ch, err := conn.Channel()  
  23.     failOnError(err, "Failed to open a channel")  
  24.     defer ch.Close()  
  25.   
  26.     q, err := ch.QueueDeclare(  
  27.         "task_queue", // name  
  28.         true,         // durable 队列持久化  
  29.         false,        // delete when unused  
  30.         false,        // exclusive  
  31.         false,        // no-wait  
  32.         nil,          // arguments  
  33.     )  
  34.     failOnError(err, "Failed www.yongshiyule178.com to declare a queue")  
  35.   
  36.     // 为了保证公平分发,不至于其中某个consumer一直处理,而其他不处理  
  37.     err = ch.Qos(  
  38.         1,     // prefetch count  在server收到consumer的ACK之前,预取的数量。为1,表示在没收到consumer的ACK之前,只会为其分发一个消息  
  39.         0,     // prefetch size 大于0时,表示在收到consumer确认消息之前,将size个字节保留在网络中  
  40.         false, // global  true:Qos对同一个connection的所有channel有效; false:Qos对同一个channel上的所有consumer有效  
  41.     )  
  42.     failOnError(err, "Failed to set www.078881.cn  QoS")  
  43.   
  44.     msgs, err := ch.Consume(  
  45.         q.Name, // queue  
  46.         "",     // consumer  
  47.         false,  // auto-ack   不进行自动ACK  
  48.         false,  // exclusive  
  49.         false,  // no-local  
  50.         false,  // no-wait  
  51.         nil,    // args  
  52.     )  
  53.     failOnError(err, "Failed to register a consumer")  
  54.   
  55.     forever := make(chan bool)  
  56.   
  57.     go func() {  
  58.         for d := range msgs {  www.meiwanyule.cn  // msgs 是一个channel,从中取东西  
  59.             log.Printf("Received a message:www.huachengj1980.com %s", d.Body)  
  60.             dot_count := bytes.Count(d.Body, []byte("."))  // 统计d.Body中的"."的个数  
  61.             t := time.Duration(dot_count)  
  62.             time.Sleep(t * time.Second) // 有几个点就sleep几秒  
  63.             log.Printf("Done")  
  64.             d.Ack(false)  www.wanmeiyuele.cn  // 手动ACK,如果不ACK的话,那么无法保证这个消息被处理,可能它已经丢失了(比如消息队列挂了)  
  65.         }  
  66.     }()  
  67.   
  68.     log.Printf(" [*] Waiting for messages. To exit press CTRL+C")  
  69.     <-forever  
  70. }  

6.运行

producer:

consumer:

consumer1 consumer2

猜你喜欢

转载自www.cnblogs.com/qwangxiao/p/9194569.html
ACK