后端架构师必知必会系列:消息队列与异步处理

作者:禅与计算机程序设计艺术

1.简介

消息队列(Message Queue)简介

在微服务架构模式中,由于应用数量众多,服务调用关系错综复杂,最终导致了可用性、性能等指标的衰退,为了解决这些问题,需要采用分布式架构模式。其中一个重要组件就是消息队列,它可以作为中间件实现服务之间的数据通信。

消息队列(Message queue)是一个先进的技术,它允许应用程序之间的松耦合沟通,异步地处理消息,并通过可靠的传递保证数据一致性。消息队列分为点对点模型和发布/订阅模型两种,点对点模型类似于电话系统中的一个“客服”直接把信息传递给客户,而发布/订阅模型允许多个消费者同时接收同一条消息。

为什么要用消息队列?

我们来看看为什么要用消息队列,主要有以下几点原因:

  1. 削峰填谷。当流量突然增长时,通过消息队列可以将请求集中起来,然后逐个进行处理,减轻后端服务器的压力,提高系统的响应能力。

  2. 流量削减。一般情况下,当网站访问量达到一定程度之后,数据库查询速度就会成为瓶颈。通过增加消息队列,可以缓冲大量请求,从而降低对数据库的查询频率。

  3. 数据一致性。消息队列可以保证数据的一致性。例如,用户订单的创建成功之后,就可以向对应的消息队列发送一条消息,通知其他系统更新库存。

  4. 可靠性。消息队列可以保证消息的可靠性传输。例如,消息可能因为网络延迟或者路由问题丢失,但是通过消息队列重试机制可以保证消息最终到达。

  5. 扩展性。消息队列具有很强的扩展性。通过增加消息队列服务器,可以横向扩展应用,并能够快速应对变化的工作负载。

  6. 异步处理。消息队列支持异步处理,可以提升用户体验。比如用户上传了一个文件,通过消息队列通知后台进行处理,不需要等待后台处理完成再返回响应,可以更快地响应用户请求。

总之,消息队列是一种分布式系统间的信息交换方式,可以用来帮助实现微服务架构下各个服务之间的通信和协作。因此,掌握好消息队列对于后端架构师来说是十分必要的。

消息队列与异步处理原理及实践

消息队列与异步处理的原理

同步阻塞与异步非阻塞

在程序执行过程中,如果某个任务耗时过长,则会影响其他任务的运行。那么,如何优化程序运行效率呢?一种方法是通过异步非阻塞的方式。所谓异步非阻塞,就是让一些不涉及到IO操作的任务不等待结果立即返回,而是继续执行下一个任务;其它的任务则被推迟到IO操作完成后再处理。这样做的好处是可以提高CPU利用率,缩短处理时间。

那么,如何实现异步非阻塞编程呢?最简单的方法就是利用事件循环(event loop)。通常,我们都可以在一个线程里启动一个事件循环,在循环里不断检测是否有新的任务需要处理。如果有,则获取任务,执行任务,然后将结果放入回调函数(callback function)或future对象里。当IO操作完成后,通过future对象得到结果,继续执行相应的回调函数。这种方法虽然简单,但缺乏灵活性,无法应对复杂的场景。例如,如果一个任务依赖另一个任务的结果,那就不能按顺序执行了。

因此,为了应对复杂的场景,引入新的抽象概念——协程(coroutine),它是一种比线程更小的执行单位。协程由一个或多个栈帧组成,可以暂停执行并且保存当前状态。当协程需要进行IO操作时,它可以切换到其他的协程,不会影响其他协程的执行。因此,协程使得编写异步代码变得容易而且非常有效率。

消息队列原理与功能

前面说到,消息队列是一种分布式系统间的信息交换方式,它提供异步处理的能力。分布式系统中,由于服务调用关系错综复杂,使得后端服务间需要相互通信。因此,消息队列可以帮助分布式系统内各个服务实现异步通信,包括服务间的RPC(Remote Procedure Call)调用,数据流等。

基本上,消息队列就是一个队列,里面装着待处理的消息。生产者(producer)就是向队列中添加消息的程序,消费者(consumer)则是从队列中取出消息并处理的程序。消息队列有两个主要功能:第一,它保证消息的传递。在实际应用中,只要生产者和消费者正确配置,消息队列就能确保消息的传递。第二,它提供异步处理。也就是说,生产者只管生产消息,消费者不等待生产者发送完毕,可以自己处理下一条消息。

RabbitMQ消息队列

RabbitMQ是目前最流行的开源消息代理软件,由Erlang语言开发。它支持多种消息路由策略、HA(High Availability)高可用部署,以及HTTP和AMQP等多种接口协议。RabbitMQ可以很好的与众多语言平台和系统集成,因此在企业级环境下广泛使用。下面我们来看一下RabbitMQ是如何工作的。

上图描绘了RabbitMQ的工作流程。首先,生产者发送消息到Exchange,它决定把消息投递到哪个Queue。Exchange根据自身的类型(direct、topic、fanout)、routing key(针对direct、topic类型的exchange)或headers(针对headers类型的exchange)将消息投递到相应的Queue。每个Queue都有一个消费者在监听,它会按照FIFO(First In First Out,先进先出)的方式读取消息并处理。当消费者处理完一个消息后,RabbitMQ自动将该消息从队列移除,生产者才能再次发送新消息。

消息队列的使用场景

消息队列的应用场景很多,这里我们列举几个常用的场景:

  1. 任务队列。消息队列可以用于处理慢速或幂等的任务。例如,向图像处理任务队列提交任务,而不是同步等待任务完成。

  2. 异步执行。许多web框架都提供了异步执行的机制。例如,许多Web框架都提供了异步请求接口,用户的请求可以通过消息队列异步执行,不占用服务器资源。

  3. 松耦合架构。在微服务架构中,服务间的通信和协作是很重要的一环。消息队列可以提供松耦合架构。例如,在电商网站中,用户行为数据可以存储在消息队列中,订单系统可以异步消费这些数据。

  4. 分布式事务。在微服务架构下,为了保证数据的一致性,可以使用消息队列提供的事务支持。例如,用户A购买商品X,产生一笔订单,但由于网络问题造成消息积压,此时用户B也来购买商品X,产生一笔订单,两笔订单之间存在数据不一致的问题。使用消息队列事务可以保证所有参与方的数据操作都是成功或失败。

消息队列与异步处理实践

RabbitMQ安装与配置

RabbitMQ的安装依赖Erlang语言,所以首先需要安装Erlang。下载地址http://www.erlang.org/downloads。下载完成后,按照安装文档进行安装即可。安装结束后,打开命令行,输入`erl`命令,出现欢迎信息表示安装成功。

然后,我们就可以安装RabbitMQ了。RabbitMQ官网https://www.rabbitmq.com/download.html,下载Windows版压缩包,解压到指定目录。

配置RabbitMQ之前,需要创建一个用户,密码和虚拟主机。打开RabbitMQ的bin目录,双击打开rabbitmqctl.bat。输入以下命令,创建用户admin:

rabbitmqctl add_user admin password # 创建用户名和密码
rabbitmqctl set_user_tags admin administrator # 设置权限

然后,我们可以创建一个虚拟主机vhost:

rabbitmqctl add_vhost my_vhost # 创建虚拟主机
rabbitmqctl set_permissions -p my_vhost admin ".*" ".*" ".*" # 设置权限

接下来,我们要修改配置文件C:\Program Files\RabbitMQ Server\rabbitmq.conf,指定我们的用户名、密码和虚拟主机:

[
  {rabbit, [
    {tcp_listen_port,     5672},
    {disk_free_limit,      50000000}, %% 设置磁盘限制,超过这个值,RabbitMQ会自动删除部分消息
    {delegate_count,       10},    %% 设置最大连接数,默认500
    {default_user,         "guest"},
    {default_pass,         "guest"},
    {default_vhost,        "/"     },
    {loopback_users,       []}
  ]},

  {rabbitmq_management, [
    {listener, [{port,          15672},
                {ip,            "127.0.0.1"}]}]}
].

[{sasl, [
      {sasl_error_logger, {file,"<path to log directory>"}},
      {errlog_type, error},
      {error_logger_mf_dir, "<path to log directory>"}, % Logs dir
      {error_logger_mf_maxbytes, 10485760}, % Max file size of logs
      {error_logger_mf_maxfiles, 5} % Max number of files in logs
    ]}].

%% 用户名、密码、虚拟主机配置
{rabbit,[
        {default_user, << "admin" >>},
        {default_pass, << "password" >>},
        {default_vhost, <<"my_vhost">>}
       ]}.

最后,我们重启RabbitMQ:

net stop RabbitMQ && net start RabbitMQ
使用Python操作RabbitMQ

首先,我们需要安装pika模块。

pip install pika

我们先创建两个Python脚本,一个生产者,一个消费者。生产者是模拟向队列中发送消息,消费者则是从队列中读取消息并打印出来。

生产者的代码如下:

import time
import random
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

queue_name = 'hello'

for i in range(10):
    message = str(i)+str(random.randint(0,10))+'hello world!'
    channel.basic_publish(exchange='', routing_key=queue_name, body=message)
    print(" [x] Sent {}".format(message))
    time.sleep(1)

connection.close()

消费者的代码如下:

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

queue_name = 'hello'

def callback(ch, method, properties, body):
    print(" [x] Received {}".format(body))

channel.basic_consume(queue=queue_name, on_message_callback=callback, auto_ack=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

connection.close()

运行这两个脚本,可以看到生产者将10条消息发送到队列,消费者则读取它们并打印出来。

猜你喜欢

转载自blog.csdn.net/universsky2015/article/details/133446674
今日推荐