提升系统性能首先考虑的是数据库的优化,可是数据库由于历史缘由,横向扩展是一件很是复杂的工程,全部咱们通常会尽可能把流量都挡在数据库以前。html
无论是无限的横向扩展服务器,仍是纵向阻隔到达数据库的流量,都是这个思路。阻隔直达数据库的流量,缓存组件和消息组件是两大杀器。这里就重点说说MQ的应用场景。前端
MQ:Message queue,消息队列,就是指保存消息的一个容器。具体的定义这里就不相似于数据库、缓存等,用来保存数据的。固然,与数据库、缓存等产品比较,也有本身一些特色,具体的特色后文会作详细的介绍。java
如今经常使用的MQ组件有activeMQ、rabbitMQ、rocketMQ、zeroMQ,固然近年来火热的kafka,从某些场景来讲,也是MQ,固然kafka的功能更增强大,虽然不一样的MQ都有本身的特色和优点,可是,无论是哪一种MQ,都有MQ自己自带的一些特色,下面,我们就先聊聊MQ的特色。git
l 先进先出
不能先进先出,都不能说是队列了。消息队列的顺序在入队的时候就基本已经肯定了,通常是不需人工干预的。并且,最重要的是,数据是只有一条数据在使用中。 这也是MQ在诸多场景被使用的缘由。web
l 发布订阅
发布订阅是一种很高效的处理方式,若是不发生阻塞,基本能够当作是同步操做。这种处理方式能很是有效的提高服务器利用率,这样的应用场景很是普遍。spring
l 持久化
持久化确保MQ的使用不仅是一个部分场景的辅助工具,而是让MQ能像数据库同样存储核心的数据。数据库
l 分布式
在如今大流量、大数据的使用场景下,只支持单体应用的服务器软件基本是没法使用的,支持分布式的部署,才能被普遍使用。并且,MQ的定位就是一个高性能的中间件。缓存
消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ服务器
Activemq 监控网络
Rabbitmq 监控
Kafka 监控
场景说明:用户注册后,须要发注册邮件和注册短信。传统的作法有两种 1.串行的方式;2.并行方式
a、串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务所有完成后,返回给客户端。
b、并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差异是,并行的方式能够提升处理的时间
假设三个业务节点每一个使用50毫秒钟,不考虑网络等其余开销,则串行方式的时间是150毫秒,并行的时间多是100毫秒。
由于CPU在单位时间内处理的请求数是必定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)
小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?
引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构以下:
按照以上约定,用户的响应时间至关因而注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,所以写入消息队列的速度很快,基本能够忽略,所以用户的响应时间多是50毫秒。所以架构改变后,系统的吞吐量提升到每秒20 QPS。比串行提升了3倍,比并行提升了两倍。
场景说明:用户下单后,订单系统须要通知库存系统。传统的作法是,订单系统调用库存系统的接口。以下图:
传统模式的缺点:假如库存系统没法访问,则订单减库存将失败,从而致使订单失败,订单系统与库存系统耦合
如何解决以上问题呢?引入应用消息队列后的方案,以下图:
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操做
假如:在下单时库存系统不能正常使用。也不影响正常下单,由于下单后,订单系统写入消息队列就再也不关心其余的后续操做了。实现订单系统与库存系统的应用解耦
流量削锋也是消息队列中的经常使用场景,通常在秒杀或团抢活动中使用普遍。
应用场景:秒杀活动,通常会由于流量过大,致使流量暴增,应用挂掉。为解决这个问题,通常须要在应用前端加入消息队列。
a、能够控制活动的人数
b、能够缓解短期内高流量压垮应用
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面。
秒杀业务根据消息队列中的请求信息,再作后续处理。
消息通信是指,消息队列通常都内置了高效的通讯机制,所以也能够用在纯的消息通信。好比实现点对点消息队列,或者聊天室等。
点对点通信:
客户端A和客户端B使用同一队列,进行消息通信。
聊天室通信:
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现相似聊天室效果。
以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。
具体例子能够参考官网:https://www.rabbitmq.com/web-stomp.html
日志处理是指将消息队列用在日志处理中,好比Kafka的应用,解决大量日志传输的问题。架构简化以下
日志采集客户端,负责日志数据采集,定时写受写入Kafka队列
Kafka消息队列,负责日志数据的接收,存储和转发
日志处理应用:订阅并消费kafka队列中的日志数据
eg:日志收集系统
分为Zookeeper注册中心,日志收集客户端,Kafka集群和Storm集群(OtherApp)四部分组成。
Zookeeper注册中心,提出负载均衡和地址查找服务
日志收集客户端,用于采集应用系统的日志,并将数据推送到kafka队列
Kafka集群:接收,路由,存储,转发等消息处理
Storm集群:与OtherApp处于同一级别,采用拉的方式消费队列中的数据
n 网易使用案例:
网易NDC-DTS系统在使用,应该是最典型的应用场景,主要就是binlog的同步,数据表的主从复制。简单一点就是:MySQL进程写binlog文件 -> 同步应用去实时监控binlog文件读取发送到Kafka -> 目标端处理binlog 。原理上与阿里开源的canal, 点评的puma大同小异。
参考延时队列
分布式事务有强一致,弱一致,和最终一致性这三种:
n 强一致:
当更新操做完成以后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据 CAP 理论,这种实现须要牺牲可用性。
n 弱一致:
系统并不保证续进程或者线程的访问都会返回最新的更新过的值。系统在数据写入成功以后,不承诺当即能够读到最新写入的值,也不会具体的承诺多久以后能够读到。
n 最终一致:
弱一致性的特定形式。系统保证在没有后续更新的前提下,系统最终返回上一次更新操做的值。在没有故障发生的前提下,不一致窗口的时间主要受通讯延迟,系统负载和复制副本的个数影响。DNS 是一个典型的最终一致性系统。
在分布式系统中,同时知足“CAP定律”中的“一致性”、“可用性”和“分区容错性”三者是几乎不可能的。在互联网领域的绝大多数的场景,都须要牺牲强一致性来换取系统的高可用性,系统每每只须要保证“最终一致性”,只要这个最终时间是在用户能够接受的范围内便可,这时候咱们只须要用短暂的数据不一致就能够达到咱们想要效果。
好比有订单,库存两个数据,一个下单过程简化为,加一个订单,减一个库存。 而订单和库存是独立的服务,那怎么保证数据一致性。
这时候咱们须要思考一下,怎么保证两个远程调用“同时成功”,数据一致?
请你们先注意一点远程调用最郁闷的地方就是,结果有3种,成功、失败和超时。 超时的话,成功失败都有可能。
通常的解决方案,大多数的作法是借助mq来作最终一致。
咱们是怎么利用Mq来达到最终一致的呢?
首先,拿咱们上面提到的订单业务举例:
在咱们进行加订单的过程当中同时插入logA(这个过程是能够作本地事务的)
而后能够异步读取logA,发mqA
B端接收mqA,同时减小库存,B这里须要作幂等(避免由于重复消息形成的业务错乱)
那么咱们经过上面的分析可能联想到这样的问题?
本地先执行事务,执行成功了就发个消息过去,消费端拿到消息执行本身的事务
好比a,b,c,a异步调用b,c若是b失败了,或者b成功,或者b超时,那么怎么用mq让他们最终一致呢?b失败就失败了,b成功以后给c发一个消息,b和c对a来说都是异步的,且他们都是同时进行的话,并且须要a,b,c同时成功的状况,那么这种状况用mq怎么作?
其实作法仍是参照于本地事务的概念的。
l 第一种状况:假设a,b,c三者都正常执行,那整个业务正常结束
l 第二种状况:假设b超时,那么须要a给b重发消息(记得b服务要作幂等),若是出现重发失败的话,须要看状况,是终端服务,仍是继续重发,甚至人为干预(全部的规则制定都须要根据业务规则来定)
l 第三种状况:假设a,b,c三者之中的一个失败了,失败的服务利用MQ给其余的服务发送消息,其余的服务接收消息,查询本地事务记录日志,若是本地也失败,删除收到的消息(表示消息消费成功),若是本地成功的话,则须要调用补偿接口进行补偿(须要每一个服务都提供业务补偿接口)。
注意事项:
mq这里有个坑,一般只适用于只容许第一个操做失败的场景,也就是第一个成功以后必须保证后面的操做在业务上没障碍,否则后面失败了前面很差回滚,只容许系统异常的失败,不容许业务上的失败,一般业务上失败一次后面基本上也不太可能成功了,要是由于网络或宕机引发的失败能够经过重试解决,若是业务异常,那就只能发消息给a和c让他们作补偿了吧?一般是经过第三方进行补偿,ABC提供补偿接口,设计范式里一般不容许消费下游业务失败。
怎么理解呢,举个例子:
好比A给B转帐,A先本身扣钱,而后发了个消息,B这边若是在这以前销户了,那重试多少次也没用,只能人工干预。
网易部分业务是用MQ实现了最终一致性,目前教育产品,例如:网易云课堂。
也有一部分业务用了TCC事务,可是TCC事务用的比较少,由于会侵染业务,开发成本比较高,若是体量不大的话直接用JPA或MQ支持事务就好。
网易的产品中使用分布式事务基于技术
TCC,FMT(Framework-managed transactions),事务消息都有。
开源产品myth:https://gitee.com/shuaiqiyu/myth
特性 MQ |
ActiveMQ |
RabbitMQ |
RocketMQ |
Kafka |
生产者消费者模式 |
支持 |
支持 |
支持 |
支持 |
发布订阅模式 |
支持 |
支持 |
支持 |
支持 |
请求回应模式 |
支持 |
支持 |
不支持 |
不支持 |
Api完备性 |
高 |
高 |
高 |
高 |
多语言支持 |
支持 |
支持 |
java |
支持 |
单机吞吐量 |
万级 |
万级 |
万级 |
十万级 |
消息延迟 |
无 |
微秒级 |
毫秒级 |
毫秒级 |
可用性 |
高(主从) |
高(主从) |
很是高(分布式) |
很是高(分布式) |
消息丢失 |
低 |
低 |
理论上不会丢失 |
理论上不会丢失 |
文档的完备性 |
高 |
高 |
教高 |
高 |
提供快速入门 |
有 |
有 |
有 |
有 |
社区活跃度 |
高 |
高 |
中 |
高 |
商业支持 |
无 |
无 |
商业云 |
商业云 |
整体来讲:
l ActiveMQ 历史悠久的开源项目,已经在不少产品中获得应用,实现了JMS1.1规范,能够和spring-jms轻松融合,实现了多种协议,不够轻巧(源代码比RocketMQ多),支持持久化到数据库,对队列数较多的状况支持很差。
l RabbitMQ 它比Kafka成熟,支持AMQP事务处理,在可靠性上,RabbitMQ超过Kafka,在性能方面超过ActiveMQ。
l RocketMQ RocketMQ是阿里开源的消息中间件,目前在Apache孵化,使用纯Java开发,具备高吞吐量、高可用性、适合大规模分布式系统应用的特色。RocketMQ思路起源于Kafka,但并非简单的复制,它对消息的可靠传输及事务性作了优化,目前在阿里集团被普遍应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景,支撑了阿里屡次双十一活动。 由于是阿里内部从实践到产品的产物,所以里面不少接口、API并非很广泛适用。其可靠性毋庸置疑,并且与Kafka一脉相承(甚至更优),性能强劲,支持海量堆积。