1.介绍
直连交换机背后的路由算法很简单,消息被发送到绑定密钥与消息的路由密钥完全匹配的队列。为了说明这一点,请考虑以下设置:
我们可以看到直连交换机 X,它绑定了两个队列。第一个队列用 orange 键绑定,第二个队列有两个绑定,一个绑定 black,另一个绑定 green。在这种设置中,使用路由键 orange 发布到 exc
Multiple bindings
使用相同的 binging key 绑定多个队列是完全合法的。在我们的示例中,我们可以添加一个绑定,在 X 和 Q1 之间,绑定键为 black。在这种情况下,直接交换的行为将像 fanout 一样,并将消息广播到所有匹配的队列。routing key 为 black 的消息将同时发送到 Q1 和 Q2。
2.代码实现
生产端代码实现:
package main import ( "fmt" "github.com/streadway/amqp" "time" ) const ( addr = "amqp://lisus2000:lisus2000@192.168.66.130:5672/" ExchangeName = "logs_direct" ) var LogLevels = []string{"error", "info", "error", "warning"} func main() { fmt.Println("生产者启动") var ( conn *amqp.Connection channel *amqp.Channel err error ) //连接MQServer if conn, err = amqp.Dial(addr); err != nil { fmt.Println("Connect RabbitMQ Err =", err, "Addr =", addr) return } //需要关闭 defer conn.Close() //创建一个Channel,所有的连接都是通过Channel管理的 if channel, err = conn.Channel(); err != nil { fmt.Println("Create Channel Err =", err) return } defer channel.Close() //创建交换机 if err = channel.ExchangeDeclare( ExchangeName, //声明交换机 "direct", //交换机类型为直连 true, false, false, false, nil, ); err != nil { fmt.Println("ExchangeDeclare Err =", err) return } //直接向交换机发送数据即可 for i := 0; i < 100; i++ { logLevel := LogLevels[i%4] msg := fmt.Sprintf("Msg Level %s", logLevel) if err = channel.Publish(ExchangeName, logLevel, false, false, amqp.Publishing{ ContentType: "text/plain", Body: []byte(msg), }); err != nil { fmt.Println("Publish Err =", err) return } fmt.Println("Send msg ok, msg =", msg) time.Sleep(5 * time.Second) } }
消费端代码实现
package main import ( "fmt" "github.com/streadway/amqp" "time" ) const ( AddrClient = "amqp://lisus2000:lisus2000@192.168.66.130:5672/" ExchangeNameClient = "logs_direct" ) var ReceiveLogLevels = []string{"error", "info", "error", "warning"} func main() { fmt.Println("消费端启动") var ( conn *amqp.Connection channel *amqp.Channel queue amqp.Queue msgs <-chan amqp.Delivery err error ) //连接MQServer if conn, err = amqp.Dial(AddrClient); err != nil { fmt.Println("Connect RabbitMQ Err =", err, "Addr =", AddrClient) return } //需要关闭 defer conn.Close() //创建一个Channel,所有的连接都是通过Channel管理的 if channel, err = conn.Channel(); err != nil { fmt.Println("Create Channel Err =", err) return } defer channel.Close() //创建交换机 if err = channel.ExchangeDeclare( ExchangeNameClient, // name "direct", // type true, // durable false, // auto-deleted false, // internal false, // no-wait nil, // arguments ); err != nil { fmt.Println("ExchangeDeclare Err =", err) return } //创建队列 if queue, err = channel.QueueDeclare( "", //队列名 false, //持久的 false, // delete when unused false, //独占的 false, nil, ); err != nil { fmt.Println("QueueDeclare Err =", err) return } //交换机绑定队列 // 参数1 队列名称 // 参数2 routing key // 参数3 交换机名称 if err = channel.QueueBind(queue.Name, ReceiveLogLevels[0], ExchangeNameClient, false, nil); err != nil { fmt.Println("QueueBind Err =", err) return } //读取数据 if msgs, err = channel.Consume( queue.Name, // queue "", // consumer true, // 自动消息确认 false, // exclusive false, // no-local false, // no-wait nil, ); err != nil { fmt.Println("Consume Err =", err) return } go func() { for msg := range msgs { fmt.Printf("Received a message: %s\n", msg.Body) } }() time.Sleep(100 * time.Second) }
3.测试
启动消费端
启动消费端
通过图我们看到只消费了error类型的消息