消息队列也是构建大型网站架构过程中非常重要的一个中间件。

消息队列中间件是分布式系统中重要的组件,主要解决应用耦合、异步消息、流量削锋等问题。实现高性能、高可用、可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。

消息队列

使用过消息队列的同学都知道,消息队列最常用的两个场景是:

1,解耦,一般用在大的网站进行业务拆分的时候,用于各应用之间进行消息通讯。

2,异步,针对那些不需要同步执行,可以晚点执行的操作都可以使用异步,比如发送邮件和短信,实际上使用消息队列来处理异步还能提升性能,因为消息队列服务器的处理速度远高于数据库服务器。

最常用的消息队列组件有两个:RabbitMQ和Kafka,下面就来总结下它们之间有什么区别。

目前在生产环境,使用较多的消息队列有ActiveMQ、RabbitMQ、ZeroMQ、Kafka、MetaMQ、RocketMQ等。

RabbitMQ vs Kafka

基本信息

先看下面这个表格:

菲律宾太阳 1

可以得出以下结论:Kafka更受欢迎,rabbitMQ版本更新更快。

架构模型

RabbitMQ:

菲律宾太阳 2

Kafka:

菲律宾太阳 3

从上图可以看到:RabbitMQ和Kafka都有消息生产者和消费者,但是两者在broker的处理是不一样的,RabbitMQ有exchange交换机的概念,而Kafka没有。

性能对比

没有可比性,因为两者应用场景不一样。测试条件:普通pc,双核cpuKafka:100k
msg/sRabbitMQ:40k msg/s

应用场景

既然说到应用场景,来看下两者在应用场景方面有什么不同:

RabbitMQ:内置支持高并发(erLang语言特性),支持事务和消息确认可靠性更高,一般用在应用间通讯方面。

Kafka:流式数据,大数据的处理,不支持事务和消息确认,一般用在日志的收集方面,因为允许偶尔的消息丢失。

下面详细介绍一下消息队列在实际应用中常用的使用场景。场景分为异步处理、应用解耦、流量削锋和消息通讯四个场景。

场景说明:用户注册后,需要发送注册邮件和发送注册信息,传统的做法有两种:串行方式、并行方式

串行方式

将注册信息写入数据库成功后,发送注册邮件,然后发送注册短信,而所有任务执行完成后,返回信息给客户端

菲律宾太阳 4串行方式

并行方式

将注册信息写入数据库成功后,同时进行发送注册邮件和发送注册短信的操作。而所有任务执行完成后,返回信息给客户端。同串行方式相比,并行方式可以提高执行效率,减少执行时间。

菲律宾太阳 5并行方式

上面的比较可以发现,假设三个操作均需要50ms的执行时间,排除网络因素,则最终执行完成,串行方式需要150ms,而并行方式需要100ms。

因为cpu在单位时间内处理的请求数量是一致的,假设:CPU每1秒吞吐量是100此,则串行方式1秒内可执行的请求量为1000/150,不到7次;并行方式1秒内可执行的请求量为1000/100,为10次。

由上可以看出,传统串行和并行的方式会受到系统性能的局限,那么如何解决这个问题?我们需要引入消息队列,将不是必须的业务逻辑,异步进行处理,由此改造出来的流程为

菲律宾太阳 6引入消息队列,异步处理消息

根据上述的流程,用户的响应时间基本相当于将用户数据写入数据库的时间,发送注册邮件、发送注册短信的消息在写入消息队列后,即可返回执行结果,写入消息队列的时间很快,几乎可以忽略,也有此可以将系统吞吐量提升至20QPS,比串行方式提升近3倍,比并行方式提升2倍。

场景说明:用户下单后,订单系统需要通知库存系统。

传统的做法为:订单系统调用库存系统的接口。如下图所示:

菲律宾太阳 7传统方式:调用库存接口

传统方式具有如下缺点:-1.
假设库存系统访问失败,则订单减少库存失败,导致订单创建失败-2.
订单系统同库存系统过度耦合

如何解决上述的缺点呢?需要引入消息队列,引入消息队列后的架构如下图所示:

菲律宾太阳 8引入消息队列,实现应用解耦

  • 订单系统:用户下单后,订单系统进行数据持久化处理,然后将消息写入消息队列,返回订单创建成功
  • 库存系统:使用拉/推的方式,获取下单信息,库存系统根据订单信息,进行库存操作。

假如在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其后续操作了。由此实现了订单系统与库存系统的应用解耦。

流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。

应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。

  1. 可以控制参与活动的人数;
  2. 可以缓解短时间内高流量对应用的巨大压力;

流量削锋处理方式系统图如下:

菲律宾太阳 9流量削锋方式系统图

  1. 服务器在接收到用户请求后,首先写入消息队列。这时如果消息队列中消息数量超过最大数量,则直接拒绝用户请求或返回跳转到错误页面;
  2. 秒杀业务根据秒杀规则读取消息队列中的请求信息,进行后续处理。

日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下:

菲律宾太阳 10消息队列应用于日志处理的架构

  • 日志采集客户端:负责日志数据采集,定时写受写入Kafka队列;
  • Kafka消息队列:负责日志数据的接收,存储和转发;
  • 日志处理应用:订阅并消费kafka队列中的日志数据;

这种架构在实际开发中的应用,可以参照案例:新浪技术分享:我们如何扛下32亿条实时日志的分析处理

菲律宾太阳 11服务的技术架构设计

  1. Kafka:接收用户日志的消息队列。
  2. Logstash:做日志解析,统一成JSON输出给Elasticsearch。
  3. Elasticsearch:实时日志分析服务的核心技术,一个schemaless,实时的数据存储服务,通过index组织数据,兼具强大的搜索和统计功能。
  4. Kibana:基于Elasticsearch的数据可视化组件,超强的数据可视化能力是众多公司选择ELK
    stack的重要原因。

消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列、聊天室等。

点对点通讯

菲律宾太阳 12点对点通讯架构设计

在点对点通讯架构设计中,客户端A和客户端B共用一个消息队列,即可实现消息通讯功能。

聊天室通讯

菲律宾太阳 13聊天室通讯架构设计

客户端A、客户端B、直至客户端N订阅同一消息队列,进行消息的发布与接收,即可实现聊天通讯方案架构设计。

菲律宾太阳 14电商系统架构示意图

消息队列采用高可用、可持久化的消息中间件。比如Active MQ,Rabbit
MQ,Rocket MQ。

  • 应用将主干逻辑处理完成后,写入消息队列。消息发送是否成功可以开启消息的确认模式。(消息队列返回消息接收成功状态后,应用再返回,这样保障消息的完整性)
  • 扩展流程订阅队列消息。采用推或拉的方式获取消息并处理。
  • 消息将应用解耦的同时,带来了数据一致性问题,可以采用最终一致性方式解决。比如主数据写入数据库,扩展应用根据消息队列,并结合数据库方式实现基于消息队列的后续处理。

菲律宾太阳 15日志收集系统架构示意图

分为Zookeeper注册中心,日志收集客户端,Kafka集群和Storm集群四部分组成。

  • Zookeeper注册中心,提出负载均衡和地址查找服务;
  • 日志收集客户端,用于采集应用系统的日志,并将数据推送到kafka队列;
  • Kafka集群:接收,路由,存储,转发等消息处理;
  • Storm集群:与OtherApp处于同一级别,采用拉的方式消费队列中的数据;

讲消息队列就不得不提JMS 。JMS(Java Message
Service,Java消息服务)API是一个消息服务的标准/规范,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。在EJB架构中,有消息bean可以无缝的与JM消息服务集成。在J2EE架构模式中,有消息服务者模式,用于实现消息与应用直接的解耦。

在JMS标准中,有两种消息模型P2P(Point to Point),Publish/Subscribe。

4.1.1 P2P模式

菲律宾太阳 16P2P模式

P2P模式包含三个角色:消息队列,发送者,接收者。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。

P2P的特点

  • 每个消息只有一个消费者(即一旦被消费,消息就不再在消息队列中)
  • 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
  • 接收者在成功接收消息之后需向队列应答成功

如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式。4.1.2
Pub/Sub模式

菲律宾太阳 17Pub/Sub模式

包含三个角色:主题,发布者(Publisher),订阅者(Subscriber)
。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

Pub/Sub的特点

  • 每个消息可以有多个消费者
  • 发布者和订阅者之间有时间上的依赖性。针对某个主题的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。
  • 为了消费消息,订阅者必须保持运行的状态。

为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活,它也能接收到发布者的消息。如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。

在JMS中,消息的产生和消费都是异步的。对于消费来说,JMS的消息者可以通过两种方式来消费消息。

  1. 同步订阅者或接收者通过receive方法来接收消息,receive方法在接收到消息之前将一直阻塞;
  2. 异步订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。

JNDI:Java命名和目录接口,是一种标准的Java命名系统接口。可以在网络上查找和访问服务。通过指定一个资源名称,该名称对应于数据库或命名服务中的一个记录,同时返回资源连接建立所必须的信息。JNDI在JMS中起到查找和访问发送目标或消息来源的作用。

1. ConnectionFactory

创建Connection对象的工厂,针对两种不同的JMS消息模型,分别有QueueConnectionFactory和TopicConnectionFactory两种。可以通过JNDI来查找ConnectionFactory对象。

2. Destination

Destination的意思是消息生产者的消息发送目标或者说消息消费者的消息来源。对于消息生产者来说,它的Destination是某个队列或某个主题;对于消息消费者来说,它的Destination也是某个队列或主题。所以,Destination实际上就是两种类型的对象:Queue、Topic可以通过JNDI来查找Destination。

发表评论

电子邮件地址不会被公开。 必填项已用*标注