为什么要使用消息中间件
同步通信:耗时长,受网络波动影响,不能保证高成功率,耦合性高。
1.同步方式(耗时长):
同步方式的问题:当一个用户提交订单到成功需要300ms+300ms+300ms+20ms = 920ms,这是不能容忍的。也就是说库存、支付、物流、最后保存数据库全部成功,订单的提交才算完成。
解决方案:异步处理
将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理,提高了应用程序的响应时间。
消息队列:Redis 发布订阅(pub/sub)
异步方式:用户点击完下单按钮后,只需等待25ms就能得到下单响应 (20 + 5 = 25ms)。也就是说,订单消息提交到MQ,MQ回馈一个消息成功,然后再把订单提交到数据库20ms,就完成了。至于MQ通知库存、支付、物流系统所花费的时间和订单系统成功没有关系了。 这样这个订单系统提升用户体验和系统吞吐量(单位时间内处理请求的数目)
2.系统的耦合性越高,容错性就越低,可维护性就越低
服务与之间耦合度,比如订单服务与用户积分服务(需求:下单成功,增加积分)
如果不用消息队列,订单服务和积分服务就要通信,下单后调用积分服务的接口通知积分服务进行处理(或者定时扫描之类的),那么调用接口失败,或者延时等等...一系列的问题要考虑处理,非常繁琐
用户了消息队列,用户A下单成功后下单服务通过redis发布(mq的生产者)一消息,就不用管了.用户积分服务redis订阅了(mq的消费者),就会受到这用户A下单的消息,进行处理.这就降低了多个服务之间的耦合,即使积分服务发生异常,也不会影响用户正常下单.处理起来就非常的丝滑,各干各的互不影响.
解决方案:应用程序解耦合
MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。
使用消息队列的方式:使用 MQ 使得应用间解耦,提升容错性和可维护性。库存和支付和物流直接去MQ取到订单的信息即可,即使库存系统报错,没关系,等到库存修复后再次从MQ中去取就可以了
3.高并发
订单系统,在下单的时候就会往数据库写数据。但是数据库只能支撑每秒1000左右的并发写入,并发量再高就容易宕机。低峰期的时候并发也就100多个,但是在高峰期时候,并发量会突然激增到5000以上,这个时候数据库肯定卡死了。但不一定宕机,只会很慢,一旦宕机就会有消息丢失。
解决方案:削峰填谷
消息被MQ保存起来了,5000条数据对于MQ,简直是小意思,然后系统就可以按照自己的消费能力来消费,比如每秒1000个数据,这样慢慢写入数据库,这样就不会卡死数据库了
4.Redis 发布订阅(pub/sub)和MQ
对比
Redis队列:Redis队列是一个Key-Value的NoSQL数据库,开发维护很活跃,虽然是一个Key-Value数据库存储系统,但它本身支持MQ功能,所以完全可以当做一个轻量级的队列服务来使用。
MQ队列 :在分布式系统中存储转发消息,在易用性、扩展性、高可用等方面表现不俗,主要是为了实现系统之间的双向解耦。
区别
Redis没有相应的机制保证消息的消费,当消费者消费失败的时候,消费体丢失,需要手动处理。MQ:具有消息消费确认,即使消费者消费失败,也会自动使消息体返回原队列,同时可全程持久化,保证消息体被正确消费;
Redis采用主从模式,读写分离,但是故障转移还没有非常完善的官方解决方案;MQ集群采用磁盘、内存节点,任意单点故障都不会影响整个队列的操作;
将整个Redis实例持久化到磁盘,MQ的队列、消息,都可以选择是否持久化;
Redis的特点是轻量级,高并发,延迟敏感,用于即使数据分析、秒杀计数器、缓存等;MQ的特点是重量级,高并发,用于异步、批量数据异步处理、并发任务串行化,高负载任务的负载均衡等。
1.可靠性
redis:没有机制保证消息的可靠性,发布一条消息没有对应的订阅者的话,这条消息将丢失,不会存在内存中。
mq:具有消息确认机制,发布一条消息,没有消费者消费该队列,这条消息一直存放在队列中,直到有消费者消费了该条消息,保证消息的可靠消费。
2.实时性
redis实时性高,redis是高效的缓存服务器,所有数据到存在内存中,所以具有更高的实时性。
3.消费者负载均衡
mq队列可以被多个消费者同时监控消费,但每一条消息只能消费一次,由于mq的消费确认机制,因此能够根据消费者的消费能力调整负载。
redis发布订阅模式,一个队列可被多个消费者同时订阅,消息到达时,会将消息一次发送给每个订阅者,是一种消息的广播形式,redis本身不做消费者的负载均衡,因此消费效率存在瓶颈。
4.持久性
redis:redis的持久化是针对整个redis缓存,可将整个redis实例持久化到磁盘来做备份,以防止异常情况下导致数据丢失。
mq:每条消息都可以选择持久化,持久化粒度更小,更灵活。
5.队列监控
mq实现了后台监控平台,可在平台上看到所有创建的队列的详细情况。redis没有监控平台。
6.性能
发布消息时,数据较小时,redis性能高于mq,数据大小超过10K时redis比较慢。读取消息时,无论数据大小,redis性能高于mq。
总结:
redis:轻量级,低延迟,高并发,低可靠性。
mq:重量级,高可靠,异步,不保证实时。
什么是消息中间件
MQ全称为Message Queue,消息队列是应用程序和应用程序之间的通信方法。是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信,在项目中,可将一些无需即时返回且耗时的操作提取出来,进行异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量
MQ的劣势
1、系统可用性降低:系统引入的外部依赖越多,系统稳定性越差。一旦 MQ 宕机,就会对业务造成影响。如何保证MQ的高可用?
2、系统复杂度提高:MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用。如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?
3、一致性问题:A 系统处理完业务,通过 MQ 给B、C、D三个系统发消息数据,如果 B 系统、C 系统处理成功,D 系统处理失败。如何保证消息数据处理的一致性?
既然 MQ 有优势也有劣势,那么使用 MQ 需要满足什么条件呢?
生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为空,这才让明明下层的动作还没做,上层却当成动作做完了继续往后走,即所谓异步成为了可能。容许短暂的不一致性。确实是用了有效果。即解耦、提速、削峰这些方面的收益,超过加入MQ,管理MQ这些成本。
常见的 MQ 产品
目前业界有很多的 MQ 产品,例如 RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMq等,也有直接使用 Redis 充当消息队列的案例,而这些消息队列产品,各有侧重,在实际选型时,需要结合自身需求及 MQ 产品特征,综合考虑。
** ** | RabbitMQ | ActiveMQ | RocketMQ | Kafka |
公司/社区 | Rabbit | Apache | 阿里 | Apache |
开发语言 | Erlang | Java | Java | Scala&Java |
协议支持 | AMQP,XMPP,SMTP,STOMP | OpenWire,STOMP,REST,XMPP,AMQP | 自定义 | 自定义协议,社区封装了http协议支持 |
客户端支持语言 | 官方支持Erlang,Java,Ruby等,社区产出多种API,几乎支持所有语言 | Java,C,C++,Python,PHP,Perl,.net等 | Java,C++(不成熟) | 官方支持Java,社区产出多种API,如PHP,Python等 |
单机吞吐量 | 万级(其次) | 万级(最差) | 十万级(最好) | 十万级(次之) |
消息延迟 | 微秒级 | 毫秒级 | 毫秒级 | 毫秒以内 |
功能特性 | 并发能力强,性能极其好,延时低,社区活跃,管理界面丰富 | 老牌产品,成熟度高,文档较多 | MQ功能比较完备,扩展性佳 | 只支持主要的MQ功能,毕竟是为大数据领域准备的。 |
AMQP 和 JMS
MQ是消息通信的模型;实现MQ的大致有两种主流方式:AMQP、JMS。
AMQP
AMQP是一种协议,Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP 规范发布。
JMS
JMS即Java消息服务(JavaMessage Service)应用程序接口,好比java提供一套jdbc的接口API,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
AMQP 与 JMS 区别
JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式;
JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的;
JMS规定了两种消息模式;而AMQP的消息模式更加丰富。