Go学习笔记(day15) | 青训营笔记

86 阅读4分钟

这是我参与「第五届青训营」伴学笔记创作活动的第15天

笨人纯小白,笔记包括一些上课学到的知识和课外总结的内容,如有错误请指正!

十八、Golang消息队列(模式)

明确两个概念,exchange(路由)queue(队列)

工作模式:

image.png

以下用p 代指生产者,用 c 代指消费者,用 x 代指 exchange

18.1 简单模式

p发给队列,单个c消费,这里用的默认exchange,收发模式是direct

image.png

18.2 工作队列模式

p发给队列,多个c消费,这里用的默认exchange,收发模式是direct

image.png

18.3 发布订阅模式(扇出模式)

fandout模式:p将消息发给x,x将同一个消息发给所有q,c 按 1,2方式消费q的消息

image.png

18.4 direct(路由)模式

p 按照路由Routing Key将消息发给q,(一个消息可能发给多个q),c按1,2方式消费q的消息

image.png

18.5 topic模式

p 按照路由Routing Key将消息发给q,(一个消息可能发给多个q),c 按 1,2方式消费q的消息,与4的区别topic可以有通配符匹配

image.png

十九、Golang操作rabbitmq

19.1 写代码的思路

在初始化中完成

  • 声明exchange
  • 声明queue
  • 将queue与key、exchange绑定

然后用conn.Channel()和rabbitmq交互

    go get github.com/rabbitmq/amqp091-go

19.2 收发模式示例

    package main

    import (
            "fmt"
            "github.com/streadway/amqp"
            "time"
    )

    func main() {
            conn, err := amqp.Dial("amqp://用户名:密码@IP:端口/")
            if err != nil {
                    panic(err)
            }

            ch, err := conn.Channel()
            if err != nil {
                    panic(err)
            }
            //durable 服务器重启还有queue  autoDelete 自动删除 exclusive 独占连接,这个q别人连不上 noWait 是否等待返回的一些状态结果
            //关于queue的一些设置
            q, err := ch.QueueDeclare("go_q1", true, false, false, false, nil)
            if err != nil {
                    panic(err)
            }

            // 开启消费者
            go consume("c1",conn, q.Name)
            go consume("c2",conn, q.Name)

            i := 0
            for {
                    i++
                    err := ch.Publish("", q.Name, false, false, amqp.Publishing{
                            Body: []byte(fmt.Sprintf("message %d", i)),
                    })
                    if err != nil {
                            panic(err)
                    }
                    time.Sleep(200 * time.Millisecond)
            }
    }

    func consume(name string,conn *amqp.Connection, q string)  {
            ch, err :=  conn.Channel()
            if err != nil {
                    panic(err)
            }
            msgs, err := ch.Consume(q,name,true, false,false,false,nil)
            if err != nil {
                    panic(err)
            }

            for msg := range msgs {
                    fmt.Printf("%s:%s\n",name,msg.Body)
            }
    }

19.3 fanout模式示例

    package main

    import (
            "fmt"
            "github.com/streadway/amqp"
            "time"
    )

    func main() {
            conn, err := amqp.Dial("amqp://用户名:密码@IP:端口/")
            if err != nil {
                    panic(err)
            }

            ch, err := conn.Channel()
            if err != nil {
                    panic(err)
            }

            err = ch.ExchangeDeclare("ex","fanout",true,false,false,false,nil)
            if err != nil {
                    panic(err)
            }

            go subscribe(conn,"ex")
            go subscribe(conn,"ex")

            i := 0
            for {
                    i++
                    err := ch.Publish("ex", "", false, false, amqp.Publishing{
                            Body: []byte(fmt.Sprintf("message %d", i)),
                    })
                    if err != nil {
                            panic(err)
                    }
                    time.Sleep(200 * time.Millisecond)
            }
    }

    func subscribe(conn *amqp.Connection, ex string) {
            ch, err :=  conn.Channel()
            if err != nil {
                    panic(err)
            }
            defer ch.Close()

            q, err := ch.QueueDeclare("", false, true, false, false, nil)
            if err != nil {
                    panic(err)
            }
            defer ch.QueueDelete(q.Name, false,false,false)
            err = ch.QueueBind(q.Name,"",ex,false,nil)
            if err != nil {
                    panic(err)
            }
            consume("c3",ch,q.Name)

    }

    func consume(name string,ch *amqp.Channel, q string)  {
            msgs, err := ch.Consume(q,name,true, false,false,false,nil)
            if err != nil {
                    panic(err)
            }

            for msg := range msgs {
                    fmt.Printf("%s:%s\n",name,msg.Body)
            }
    }

写代码的时候注意,收发消息,一定要在不同的channel进行,大家可以把channel认为是一个tcp连接的分割。建立exchang的channel可以进行发消息,不可以进行收消息

可以看到有一个exchange,对应2个queue。对应一条tcp连接(分成3个channel,1个向exchange发,2个从queue收)

image.png

image.png

19.4 routing(路由)模式示例

    package main

    import (
            "fmt"
            "github.com/streadway/amqp"
            "strconv"
            "time"
    )

    const (
            exchangeName = "ex_routing"
            key1     = "key1"
            key2     = "key2"
            queueBindKey1 = "queue1"
            queueBindKey2 = "queue2"
    )

    func main() {
            dsn := fmt.Sprintf("amqp://%s:%s@%s:%d/", "xxxxx", "xxxxx", "xxxxx", "xxxxx")
            conn, err := amqp.Dial(dsn)
            if err != nil {
                    panic(err)
            }

            ch, err := conn.Channel()
            if err != nil {
                    panic(err)
            }
            InitMQ(ch,queueBindKey1,key1,exchangeName)
            InitMQ(ch,queueBindKey2,key2,exchangeName)

            go subscribe(conn, key1,queueBindKey1)
            go subscribe(conn, key2,queueBindKey2)

            i := 0
            for {
                    i++
                    sendMessage(ch,exchangeName,key1,strconv.Itoa(i))
                    sendMessage(ch,exchangeName,key2,strconv.Itoa(i))
                    time.Sleep(500 * time.Millisecond)
            }
    }

    func InitMQ(ch *amqp.Channel, queue,key,exchange string) {
            // 声明 exchange
            err := ch.ExchangeDeclare(exchangeName, "direct", true, false, false, false, nil)
            if err != nil {
                    panic(err)
            }
            // 声明 queue
            _, err = ch.QueueDeclare(queue, false, false, false, false, nil)
            if err != nil {
                    panic(err)
            }
            // 将 queue 与 exchange 和 key 绑定
            err = ch.QueueBind(queue, key, exchange, false, nil)
            if err != nil {
                    panic(err)
            }

    }

    func sendMessage(ch *amqp.Channel, exchange string, key string,message string) {
            err := ch.Publish(exchange, key, false, false, amqp.Publishing{
                    Body: []byte(fmt.Sprintf("send to %s, message: %v", key,message)),
            })
            if err != nil {
                    panic(err)
            }

    }

    func subscribe(conn *amqp.Connection, key string,queue string) {
            ch, err := conn.Channel()
            if err != nil {
                    panic(err)
            }
            defer ch.Close()
            key = fmt.Sprintf("%s haha",key)
            consume(key, ch, queue)
    }

    func consume(name string, ch *amqp.Channel, queue string) {
            msgs, err := ch.Consume(queue, name, true, false, false, false, nil)

            if err != nil {
                    panic(err)
            }

            for msg := range msgs {
                    fmt.Printf("%s:%s\n", name, msg.Body)
            }
    }

绑定图:

image.png

19.5 topic模式

是rabbitmq最高级模式了,没啥说的,重点就是,*匹配1个#匹配0或多个

image.png

    package main

    import (
            "fmt"
            "github.com/streadway/amqp"
            "log"
            "time"
    )

    const (
            TopicExchange = "topicExchange"
            BindingKey1   = "*.*.red"
            BindingKey2   = "*.error.*"
            BindingKey3   = "shanghai.*.*"
            Queue1        = "queue1"
            Queue2        = "queue2"
            Queue3        = "queue3"
            RoutingKey1   = "beijing.error"
            RoutingKey2   = "shanghai.fatal.red"
    )

    func main() {
            dsn := fmt.Sprintf("amqp://%s:%s@%s:%d/", "用户名", "密码", "ip", port)
            conn, err := amqp.Dial(dsn)
            if err != nil {
                    panic(err)
            }

            ch, err := conn.Channel()
            if err != nil {
                    panic(err)
            }
            InitMQ(ch, Queue1, BindingKey1, TopicExchange)
            InitMQ(ch, Queue2, BindingKey2, TopicExchange)
            InitMQ(ch, Queue3, BindingKey3, TopicExchange)
            ch2 := GenChannel(conn)
            go subscribe(ch2, BindingKey1, Queue1, func(msgs <-chan amqp.Delivery, s string) {
                    for msg := range msgs {
                            fmt.Printf("%v 绑定 %v 收到消息:%v\n", Queue1,BindingKey1,string(msg.Body))
                    }
            })
            go subscribe(ch2, BindingKey2, Queue2, func(msgs <-chan amqp.Delivery, s string) {
                    for msg := range msgs {
                            fmt.Printf("%v 绑定 %v 收到消息:%v\n", Queue2,BindingKey2,string(msg.Body))
                    }
            })
            go subscribe(ch2, BindingKey3, Queue3, func(msgs <-chan amqp.Delivery, s string) {
                    for msg := range msgs {
                            fmt.Printf("%v 绑定 %v 收到消息:%v\n", Queue3,BindingKey3,string(msg.Body))
                    }
            })
            for {
                    sendMessage(ch, TopicExchange, RoutingKey1, "beijing.error")
                    sendMessage(ch, TopicExchange, RoutingKey2, "shanghai.fatal.red")
                    time.Sleep(500 * time.Millisecond)
            }
    }

    func GenChannel(conn *amqp.Connection) *amqp.Channel {
            ch, err := conn.Channel()
            if err != nil {
                    log.Fatal(err)
            }
            return ch
    }

    func InitMQ(ch *amqp.Channel, queue, key, exchange string) {
            // 声明 exchange
            err := ch.ExchangeDeclare(exchange, "topic", true, false, false, false, nil)
            if err != nil {
                    panic(err)
            }
            // 声明 queue
            _, err = ch.QueueDeclare(queue, false, false, false, false, nil)
            if err != nil {
                    panic(err)
            }
            // 将 queue 与 exchange 和 key 绑定
            err = ch.QueueBind(queue, key, exchange, false, nil)
            if err != nil {
                    panic(err)
            }

    }

    func sendMessage(ch *amqp.Channel, exchange string, key string, message string) {
            err := ch.Publish(exchange, key, false, false, amqp.Publishing{
                    Body: []byte(fmt.Sprintf("send to %s, message: %v", key, message)),
            })
            if err != nil {
                    panic(err)
            }

    }

    func subscribe(ch *amqp.Channel, key string, queue string, callback func(<-chan amqp.Delivery, string)) {
            msgs, err := ch.Consume(queue, key, true, false, false, false, nil)

            if err != nil {
                    panic(err)
            }
            callback(msgs, key)
    }