RabbitMQ基础篇(一)

RabbitMQ基础篇(一)_成电至菜的博客-CSDN博客_start_consuming

本系列博客主要从实践理论两个角度带着大家一步一步认识了解熟悉RabbitMQ,总共分为基础篇、进阶篇、高级篇。基础篇主要介绍一些 MQ 的基础概念,RabbitMQ 的历史背景,以及同类型的一些技术,搭建运行环境并且编写一个简单的程序,进阶篇我们将使用 RabbitMQ 做更多的实验,以实践的方式深入学习,高级篇会讲解一些 RabbitMQ 的高级应用,如何搭建 RabbitMQ 的高可用基础架构以及 RabbitMQ 集成的一些插件,并且总结 RabbitMQ 的设计思想等。

为什么使用 MQ

MQ 让我们的请求和处理实现了分离,客户端的请求服务器端可以异步的执行,执行完成后返回结果,这种解耦极大的提升了服务器响应客户端请求的能力,请求的异步化降低了服务器同一时间需要处理的请求数,对于我们构建高性能服务有很大的帮助。

什么是 RabbitMQ

  • RabbitMQ 是一个消息中间件:它接受并转发消息。你可以把它当做一个快递站点,当你要发送一个包裹时,你把你的包裹放到快递站,快递员最终会把你的快递送到收件人那里,按照这种逻辑 RabbitMQ 是一个快递站,一个快递员帮你传递快件。RabbitMQ 与快递站的主要区别在于,它不处理快件而是接收,存储和转发消息数据。

四大核心概念

 

  • 生产者
    产生数据发送消息的程序是生产者
  • 交换机
    交换机是 RabbitMQ 非常重要的一个部件,一方面它接收来自生产者的消息,另一方面它将消息
    推送到队列中。交换机必须确切知道如何处理它接收到的消息,是将这些消息推送到特定队列还是推送到多个队列,亦或者是把消息丢弃,这个得有交换机类型决定
  • 队列
    队列是 RabbitMQ 内部使用的一种数据结构,尽管消息流经 RabbitMQ 和应用程序,但它们只能存
    储在队列中。队列仅受主机的内存和磁盘限制的约束,本质上是一个大的消息缓冲区。许多生产者可
    以将消息发送到一个队列,许多消费者可以尝试从一个队列接收数据。这就是我们使用队列的方式
  • 消费者
    消费与接收具有相似的含义。消费者大多时候是一个等待接收消息的程序。请注意生产者,消费
    者和消息中间件很多时候并不在同一机器上。同一个应用程序既可以是生产者又是可以是消费者。

RabbitMQ 核心部分

各个名词介绍 

  • Broker:接收和分发消息的应用,RabbitMQ Server 就是 Message Broker
  • Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ Server 提供的服务时,可以划分出多个 vhost,每个用户在自己的 vhost 创建 exchange/queue 等
  • Connectionpublisher/consumer 和 broker 之间的 TCP 连接
  • Channel:如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP,Connection 的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个 thread 创建单独的 channel 进行通讯,AMQP method 包含了 channel id 帮助客户端和 message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的Connection 极大减少了操作系统建立 TCP connection 的开销
  • Exchange:message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发
    消息到 queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)
  • Queue:消息最终被送到这里等待 consumer 取走
  • Binding:exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key,Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据

 

关键字解释: 
0、AMQP协议: 
AMQP:Advanced Message Queuing Protocol 高级消息队列协议,是一个异步消息传递所使用的应用层协议规范。

1、ErLang: 
Erlang是一种通用的并行程序设计语言,它由乔·阿姆斯特朗(Joe Armstrong)在瑞典电信设备制造商爱立信所辖的计算机科学研究室开发,目的是创造一种可以应付大规模开发活动的程序设计语言和运行环境。Erlang于1987年发布正式版本,最早是爱立信拥有的私有软件,经过十年的发展,于1998年发表开放源代码版本。 
Erlang是运作于虚拟机的解释型语言,但是现在也包含有乌普萨拉大学高性能Erlang计划(HiPE)[2]开发的原生代码编译器,自R11B-4版本开始,Erlang也支持脚本方式执行。在编程范型上,Erlang属于多重范型编程语言,涵盖函数式、并行及分布式。循序运行的Erlang是一个及早求值, 单次赋值和动态类型的函数式编程语言。

语言特色: 
跨进程通信: 
在并行程序设计语言中,可以使用spawn函数,将特定的函数设置为独立的进程,之后可以做跨进程通信。 
函数式程序设计 
由于Erlang早期以Prolog开发制成,受语言特性影响,即成为函数式语言。 
快速失败 
在运行时期发生的错误,会由错误位置提交消息,发生错误的进程立刻停止执行。借由进程通讯机制,可以自动传递错误、捕捉错误,使其他进程能够帮助处理错误。

2、生产者: 
消息的生成者和发送者,是整个消息生命周期的起点,创建消息设置标签,然后与RabbitMQ代理服务器(Erlang节点)建立连接,并且发送消息到对应的服务器上。

3、消费者: 
消息的接受者和使用者,接收来自RabbitMQ代理服务器的消息,解析请求并且根据内容给出响应,注意消费者只接收到整条消息的一部分:payload,消息路由的过程中,消息的标签没有随着有效载荷一起传递;消费者接收的每一条消息都需要确认,只有确认了以后 RabbitMQ 才能安全地将服务器上的数据删除,并且发送新的消息给消费者。

4、消息: 
消息由两部分组成:有效载荷(payload)和标签(label),有效载荷是生产者需要传输的具体数据内容,标签是对数据的描述,相当于元数据,消息发送的过程中只有payload 进行传播,label 不会发送给消费者。

5、队列: 
队列相当于邮箱服务器的一个具名邮箱,RabbitMQ服务器相当于是邮局,一个邮局有多个邮箱,一个邮箱对应多个接收人,接收人会随时查看邮箱的来信,接收人对应这里的消费者,消费者订阅队列,并且不断的从队列中接收消息进行处理,当一个队列上有多个订阅的消费者的时候,消息会以循环(round-robin)的方式分发给不同的消费者。

  • 为消息提供了处所,消息在此等待消费。
  • 方便实现负载均衡,一个队列可以对应多个消费者,并且以循环的方式分发请求到不同的消费者。
  • 队列是 Rabbit 中消息的终点。

6、交换器: 
类似于路由器,完成消息的分发,一个交换器可能绑定多个队列,多个交换器也可能绑定同一个队列,交换器根据路由键(routing key)将消息路由到对应的队列上。交换器的类型总共有四种:

  • direct: 如果路由键匹配的话,消息就被投递到对应的队列。服务器上面都有一个默认的交换器‘’,当创建一个队列 queue_name 的时候就会先绑定到默认的交换器上, 队列的名称会作为路由键。默认交换器 + 临时队列 ==> RPC 通信
  • fanout:将收到的消息广播到绑定的队列上,生产者的代码和消费者的代码两者完全解耦合。
  • topic:允许来自不同源头的消息能够到达同一个队列(日志系统)。
  • headers:(不常用) 
    direct 交换器是你一种一对一的模式, fanout 交换器是一种广播模式,一对多的模式, topic 交换器是一种多对一的模式,每个队列都有一个特定的主题,不同应用程序的消息会通过路由键路由到同一个消息队列。

7、绑定: 
交换器(exchange) 与队列(queue) 之间存在一个绑定关系,这种绑定关系主要通过两个命令完成:basic.publish(body, exchange, routing_key), queue.bind(queue, exchange, routing_key)。 
例如:basic.publish(“log”, “logs_exchange”, “error.log”), queue_bind(“msg_log_queue”, “logs_exchange”, “error.“),通过这样的绑定,logs_exchange 交换器上面所有 error. 的日志都会发送到 msg_log_queue 队列。

8、虚拟主机: 
一台RabbimtMQ服务器上面可以创建多个虚拟主机 vhost,每个虚拟主机之间相互隔离,每个虚拟主机都拥有自己的交换器、队列、绑定。安装 RabbitMQ 后会生成默认的 vhost : “/”, 缺省的用户名密码是 guest/guest,RabbitMQ 的权限控制是以 vhost 为单位的,vhost 上给不同的用户设置了不同的权限。使用 vhost 可以保证通信架构的安全性和可移植性,在集群上创建 vhost 的时候,整个集群都会创建该 vhost. 

 每一个RabbitMQ服务器都能创建虚拟消息服务器,我们称为虚拟主机Vhost.每一个vhost 本质上是一个mini版 的RabbitMq.vhost之于Rabbit就像虚拟机之于物理机一样,他们通过在各个实例间提供逻辑上分离,允许为不同的应用程序安全保密的运行数据,这很有,它既能将同一个Rabbit的众多客户区分开来,又可以避免队列和交换器的命名冲突。 拥有自己的队列 交换器 和绑定. 更重要的是, 它拥有自己的权限机制, 在Rabbit里创建一个用户时, 用户通常会被指派至少一个vhost , 并且只能访问被指派vhost内的队列,交换器和绑定. vhost 之间是绝对隔离的.  vhost之间是相互独立的,这避免了各种命名的冲突,就像App中的沙盒的概念一样,每个沙盒是相互独立的,且只能访问自己的沙盒,以保证非法访问别的沙盒带来的安全隐患.在RabbitMq 集群上创建vhost 时, 整个集群上都会创建该vhost. vhost 不仅消除了为基础架构中的每一层运行一个RabbitMq服务器的需要, 童谣避免为每一层创建不同的集群.

那么如何创建呢:通过客户端工具 rabbitmqctl 完成创建。 
rabbitmqctl add_vhost[vhost_name]: 创建 vhost 
rabbitmqctl delete_vhost[vhost_name]:删除 vhost 
rabbitmqctl list_vhosts:列出所有 vhost

9、持久化: 
实现方式:

  • 消息的投递模式(dilivery mode)选项设置为2(持久):消息标记为持久化的
  • 发送到持久化的交换器上
  • 达到持久化的队列 
    三个条件缺一不可!

原理: 
磁盘上创建一个持久化日志文件,消息如果被发送到非持久化队列,那么这条消息会被删除,持久化消息如果被消费了,持久化日志中的消息被标记为等待垃圾收集,服务器重启,持久化日志文件中的消息会重播,所以持久化日志文件中记录的消息都是持久化的消息,这部分消息在服务器重启后都可以进行恢复,所以一般会将一些重要的消息设置为持久化的,但是因为需要进行不断的磁盘读写操作,性能不是很好,会降低 RabbitMQ 的消息吞吐量,有可能性能下降 10 倍之多,而且在内建集群环境下工作的不好(因为内建集群数据不做冗余)

10、策略: 
(1)持久化事务:AMQP 事务不同于数据库事务,多条命令形成一个事务,只有第一条命令执行成功后才执行其他的命令。同步的,性能很差。 
(2)发送方确认模式(confirm 模式):所有信道上发布的消息都会被指派一个唯一的 ID 号(从 1 开始),一旦消息被投递给所有匹配的队列后,信道会发送一个发送方确认模式生产者应用程序(包含消息的唯一 ID),这样生产者就知道消息已经发送成功了,如果消息和队列都是可持久化的,那么该确认消息会在队列将消息写入磁盘后才发出,发送方确认模式最大的优点就是异步的,生产者可以在等确认消息的同时发送下一条消息,如果收到的是 nack(未确认)消息,那么该消息重发。

11、黑洞: 路由消息找不到对应的队列,那么消息将进入“黑洞”。

12、死信: 被消费者拒绝并且没有重新入队的“垃圾(异常)消息”,存放到死信队列。

常用命令:

  • basic.get:向队列请求单条消息进行消费。

  • basic.ack:消费者显式地确认消息已收到。

  • basic.reject:消费过程中发生错误,当前服务器无法处理该消息,显式的拒绝继续发送,requeue 参数设置为 true, 则RabbitMQ 会将该消息分发给其它订阅的消费者。requeue 参数设置为 false,则 RabbitMQ 会将该消息从队列中移除,这部分消息进入“死信”(存放被拒绝且不能重新入队的消息)。 
    queue.declare:创建队列,一般需要指定名称,不指定会随机地分配一个,用于创建“临时匿名”队列。

    • exclusive:设置为true 表明私有队列。
    • auto-delete:最后一个消费者取消订阅的时候自动删除。
    • passive:测试队列是否存在,存在成功返回,不存在返回错误。
  • basic.publish(body, exchange, routing_key):消息发送,从交换器发送到对应的队列,msg 消息通过默认交换器发送到对应的队列上。发送消息可以指定消息体、目标交换器以及对应交换器上的队列。

$channel -> basic.publish($msg, ‘’, ‘queue_name’)
  • 1
  • exchange.declare: 创建交换器

  • queue.bind(queue, exchange, routing_key):交换器队列绑定

  • basic.consume(msg_consumer, queue, consumer_tag): 消费者订阅队列,一旦订阅,将持续不断的监控队列,消费(拒绝)完一条后继续接收下一条。

    • msg_consumer(channel, method, header, body): 消息处理器,basic.consume 收到消息后用该 handler 对消息进行处理,channel 是消息来源信道,method 里面包含了消息发送者的一些详细信息,比如交换器名称(exchanges)、路由键(routing_key)、投放模式(delivery_tag)等,header 是消息头,body 是消息体。
    • queue:消费者订阅的队列的名称。
    • consumer_tag:消费者的唯一标识。
  • basic.cancel:取消订阅,消费者不再订阅该队列

  • start_consuming:消费者开始监听队列

  • stop_consuming:消费者停止监听队列

一般的消费者流程: 
basic.consume(订阅)–> start_consuming(开始监听) –> basic.cancel (取消订阅)–> stop_consuming(停止监听)

环境搭建

(1)下载安装 RabbitMQ:rabbitmq-server-3.6.9.exe 
(2)配置启动服务器 
配置环境变量: 
D:\Otp\erl8.3\bin 添加到环境变量 path 里面

启动服务器: 
cd 到 rabbitmq 的 sbin 目录 
rabbitmq-server.bat start:启动 rabbitmq 服务器:5672 rabbitmq, 15672 客户端插件, 25672 集群, 4369 epmd

查看启动状态: 
rabbitmqctl.bat status : 查看 erlang 节点状态 
rabbitmq-plugins.bat enable rabbitmq_management: 允许插件访问 
访问路径:http://localhost:15672/

创建用户并授权 
rabbitmqctl.bat add_user lijl85 test: 创建用户 
rabbitmqctl.bat set_permissions “.” “.” “.*”: 授权,三个参数分别对应配置、读、写权限。

(3)客户端接入: 
python 接入:使用 pip 安装皮卡丘(pika: RabbitMQ 的 python 实现)

 /*
 * 引入 pika
 */
 import json, pika

Hello World!

# coding = utf-8
# broker_util.py:get_channel 可以获取代理服务器的信道

import pika

# 获取代理服务器信道
def get_channel(vhost, server, user, password):
    AMQP_VHOST = vhost
    AMQP_SERVER = server
    AMQP_USER = user
    AMQP_PASS = password
    credentials_broker = pika.PlainCredentials(AMQP_USER, AMQP_PASS)
    conn_params = pika.ConnectionParameters(credentials=credentials_broker, virtual_host=AMQP_VHOST, host=AMQP_SERVER)
    connection = pika.BlockingConnection(conn_params)
    return connection.channel()
# producer.py:生产者,发出 Hello World 消息

import json, pika
import common.broker_util as broker;

channel = broker.get_channel(vhost="/", server="localhost", user="lijl85", password="test")
channel.exchange_declare(exchange="hello_exchange", type="direct", auto_delete=False)
channel.queue_declare(queue="hello_queue", auto_delete=False)
channel.queue_bind(queue="hello_queue", exchange="hello_exchange", routing_key="hello")

msg = json.dumps({"data": "Hello RabbitMQ World!"})
msg_props = pika.BasicProperties()
msg_props.content_type = "application/json"
msg_props.durable = False  # 非持久化消息

channel.basic_publish(properties=msg_props, body=msg, exchange="hello_exchange", routing_key="hello")
# consumer.py:消费者

import json
import common.broker_util as broker;

channel = broker.get_channel(vhost="/", server="localhost", user="lijl85", password="test")
channel.exchange_declare(exchange="hello_exchange", type="direct", auto_delete=False)
channel.queue_declare(queue="hello_queue", auto_delete=False)
channel.queue_bind(queue="hello_queue", exchange="hello_exchange", routing_key="hello")

def msg_handler(hello_channel, method, hello_header, body):
    msg = json.loads(body)
    print(msg.get("data"))
    channel.basic_ack(delivery_tag=method.delivery_tag)

channel.basic_consume(consumer_callback=msg_handler, queue="hello_queue", no_ack=False, consumer_tag="hello_consumer")
channel.start_consuming()

broker_util 是单独封装的模块,用于获取与代理服务器的信道,需要传入当前使用的 vhost, server, user, password,用户名密码可以在 RabbitMQ Management Web UI 中设置,使用的时候先启动 consumer.py,然后执行 producer.py 就可以看到一条消息发送。

执行结果: 
这里写图片描述

用户配置: 
这里写图片描述

交换器配置: 
这里写图片描述

队列配置: 
这里写图片描述

消息发送面板: 
这里写图片描述

通过登录 http://localhost:15672 我们可以看到关于该 erlang 节点的各种信息以及消息发送的情况,RabbitMQ Web UI 比 rabbitmqctl 工具更强大的一点是它可以看到消息的分类:准备发送的消息 + 确认的消息 + 未确认的消息 + 总消息条数。直观而且友好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值