读书笔记《RabbitMQ实战指南》(一)

书中以java语言作为示例代码,我以go语言来实现有必要记录的示例

一、特点

  • 可靠性:RabbitMQ使用一些机制保持可靠性,如持久化,传输确认及发布确认等.
  • 灵活的路由: 在消息进入队列之前,通过交换器来路由消息.
  • 扩展性: 多个RabbitMQ节点可以组成一个集群,也可以根据实际业务情况动态地扩展集群中节点.
  • 高可用性: 队列可以在集群中的机器上设置镜像,使得在部分节点出现问题的情况下仍然可用.
  • 多种协议: 除了原生支持AMQP协议,还支持STOMP、MQTT等多种消息中间件协议.
  • 多语言客户端: RabbitMQ 几乎支持所有常用语言,比如JAVA、Pthon、Ruby、PHP、C#、JavaScript等.
  • 管理界面: 易用的用户界面,使得用户可以监控和管理消息,集群钟的节点等.
  • 插件机制: RabbitMQ提供了许多插件,以实现从多方面进行扩展,当然也可以编写自己的插件.

二、安装

  1. 安装Erlang
    可到官网下载:https://www.erlang.org/
tar zxvf otp_src_19.3.tar.gz
cd otp_src_19.3
./configure --prefix=/opt/erlang
  • 如果遇到错误:No curses library functions found.需要安装ncurses
yum install ncurses-devel
  • 继续安装
make 
make install 
  • 添加环境变量
#修改/etc/profile
  ERLANG_HOME=/opt/erlang
  export PATH=$PATH:$ERLANG_HOME/bin
#执行 
source /etc/profile 让配置文件生效
  • 输入 erl命令检测Erlang是否安装成功

2.安装RabbitMQ
可到官网下载:https://www.rabbitmq.com/releases/rabbitmq-server/

tar zvxf rabbitmq-server-generic-unix-3.6.10.tar.gz -C /opt
cd /opt
mv rabbitmq_server-3.6.10 rabbitmq
  • 修改/etc/profile文件,添加环境变量:
export PATH=$PATH:/opt/rabbitmq/sbin
export RABBITMQ_HOME=/opt/rabbitmq
#保存后执行
source /etc/profile
  • 输入以下命令运行rabbitmq服务
    -detached:参数让rabbitmq服务以守护进程的方式在后台运行
rabbitmq-server -detached
  • 运行 rabbitmqctl status命令查看RabbitMQ是否正常启动。
    也可以使用rabbitmqctl cluster_status命令查看集群信息。
  • rabbitmq服务的用户名密码默认都是guest,默认只能通过本地网络访问。
    所以添加另一个用户,并设置访问权限
# 添加新用户,用户名为root, 密码为root123
rabbitmqctl add_user root root123
# 为root用户设置所有权限:
rabbitmqctl set_permissions -p / root ".*" ".*" ".*"
# 设置root用户为管理员角色:
rabbitmqctl set_user_tags root  administrator

三、路由模式生产者和消费者Go代码示例

package Rabbitmq

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

type Rabbit struct {
    conn         *amqp.Connection
    channel      *amqp.Channel
    ExchangeName string `json:"exchange_name" description:"交换器名称"`
    RoutingKey   string `json:"routing_key" description:"routing_key"`
    QueueName    string `json:"queue_name" description:"队列名称"`
    Url          string `json:"url" description:"监听地址"`
}

func NewProducer() *Rabbit {
    rabbitMq := &Rabbit{
        nil,
        nil,
        "exchange_demo",
        "routingkey_demo",
        "queue_demo",
        "amqp://root:root123@127.0.0.1:5672/demo", //一般从配置文件读取
    }
    var err error
    // 创建链接
    rabbitMq.conn, err = amqp.Dial(rabbitMq.Url)
    rabbitMq.checkError(err, "创建链接失败")
    // 创建信道
    rabbitMq.channel, err = rabbitMq.conn.Channel()
    rabbitMq.checkError(err, "创建channel失败")
    return rabbitMq
}

func (r *Rabbit) checkError(err error, message string) {
    if err != nil {
        log.Fatalf("%s: %s", message, err)
    }
}
// 生产者
func (r *Rabbit) Product(message string) {
    // 1.尝试创建交换机,创建一个类型为direct的持久化的非自动删除的交换器
    err := r.channel.ExchangeDeclare(
        // 交换机名称
        r.ExchangeName,
        //交换机类型
        "direct",
        // 是否持久化
        true,
        // 是否自动删除
        false,
        // true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定
        false,
        false,
        nil,
    )
    r.checkError(err,"创建交换器失败")
    // 2.发送消息到队列中
    err = r.channel.Publish(
        r.ExchangeName,
        // 如果为true, 根据exchange类型和routkey规则,如果无法找到符合条件的队列,那么会把发送的消息返回给发送者
        r.RoutingKey,
        false,
        // 如果为true, 当exchange发送消息到队列后发现队列上没有绑定消费者,则会把消息发还给发送者(RabbitMq3.0去掉了该参数的支持,官方解释为该参数影响镜像队列的新能,增加了代码的复杂度建议采用TTL和DLX方法替代)
        false,
        amqp.Publishing{
            ContentType: "text/plain",
            Body:        []byte(message),
        })
    if err != nil {
        fmt.Println(err)
    }
}

// 消费者
func (r *Rabbit) RecieveRouting() {
    //1.试探性创建交换机
    err := r.channel.ExchangeDeclare(
        r.ExchangeName,
        //交换机类型
        "direct",
        true,
        false,
        false,
        false,
        nil,
    )
    r.checkError(err, "Failed to declare an exch"+
        "ange")

    //2.试探性创建队列,这里注意队列名称不要写
    q, err := r.channel.QueueDeclare(
        "", //随机生产队列名称
        false,
        false,
        true,
        false,
        nil,
    )
    r.checkError(err, "Failed to declare a queue")

    if len(os.Args) < 2 {
        log.Printf("Usage: %s [info] [warning] [error]", os.Args[0])
        os.Exit(0)
    }
    for _, s := range os.Args[1:] {
        log.Printf("Binding queue %s to exchange %s with routing key %s",
            q.Name, r.ExchangeName, s)
        //绑定队列到 exchange 中
        err = r.channel.QueueBind(
            q.Name,
            //需要绑定key
            r.RoutingKey,
            r.ExchangeName,
            false,
            nil)
        r.checkError(err, "Failed to bind a queue")
    }


    //消费消息
    messges, err := r.channel.Consume(
        q.Name,
        "",
        true,
        false,
        false,
        false,
        nil,
    )

    r.checkError(err, "Failed to register a consumer")

    forever := make(chan bool)

    go func() {
        for d := range messges {
            log.Printf(" [x] %s", d.Body)
        }
    }()

    log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
    <-forever
}
posted @ 2020-05-24 20:19  胖墩墩的博客  阅读(36)  评论(0)    收藏  举报  来源