1、为什么使用消息队列
消息队列的本质:先进先出的数据结构
用到分布式项目中,进行数据传递,数据用消息的形式封装起来放到队列里提供消费
应用场景:
解耦:实例就是比如在订单系统和支付系统、库存系统、物流系统中间加一层MQ
异步:当用户提交请求时,订单系统处理并返回,并不依赖于支付系统、库存系统、
物流系统返回的数据,说白了就是这些数据就需要存到库里,或者日志数据,
那异步最好,否则就得用同步,看具体业务场景。
削峰:秒杀场景,某一时间点,请求流量大,用MQ削峰,用户请求的数据缓存在MQ
消费者可以配置限流,
比如正常时段QPS是1万,最高峰是10万,应对流量高峰配置高性能的服务器不划算
公司会考虑经济,此时可以使用消息队列削峰。
2、消息队列的比较
产品:ActiveMQ RabbitMQ RocketMQ kafka
关于选型,根据以下指标:
开发语言:
单机吞吐量:
时效性:
可用性:
功能特性:
总结怎么选:
ActiveMQ现在不怎么用了,对高并场景没有大规模的验证,社区也不活跃,并发不高
的场景可以考虑
RabbitMQ有二次开发的需求不是特别推荐,除非erlang语言玩的牛逼,单从追求性能
稳定,社区活跃的角度可以尝试选择
RocketMQ如果想二次开发,JAVA语言写的,阿里内部也验证过高并发场景,稳定性和
性能都还不错,想功能多点,可以尝试选择
kafka大数据领域,实时,日志采集
特性 | ActiveMQ | RabbitMQ | RocketMQ | kafka |
开发语言 | java | erlang | java | scala |
单机吞吐量 | 万级 | 万级 | 10万级 | 10万级 |
时效性 | ms级 | us级 | ms级 | ms级以内 |
可用性 | 高(主从) | 高(主从) | 非常高(分布式) | 非常高(分布式) |
功能特性 | 成熟,并发小可以,对大并发未测试,文档多,协议支持好 | 基于erlang开发,并发能力强,性能好,延时低,管理界面丰富,针对java程序员二次开发不友好 | MQ功能多,阿里进行二次开发,优化之后单机吞吐量会有很大变化 | 只支持主要MQ功能,一些消息查询没有功能提供,为大数据准备,大数据领域应用广 |
3、消息队列的优缺点
其实主要的是突然加了这个MQ,那么对整个架构来说,到底有啥需要注意的,
也就是对缺点的考虑
优点:解耦、异步、削峰
缺点:
- 系统可用性降低
主要是针对如果MQ挂了怎么办,引出高可用的问题
怎么保证高可用
- 系统复杂度提高
因为是对数据封装,那么消息丢失怎么办,重复消费怎么办,
怎么保证消息的顺序性
- 一致性的问题
怎么保证数据一致性
怎么保证高可用: 搭集群
RabbitMQ的集群,镜像集群(生产多镜像集群,可以自己找资料)
RocketMQ的集群,双主双从
原理:Producer通过Name Server来知道我到底要向哪个Broker发送消息
可以理解为Name Server是管理者,那么Name Server怎么知道哪个
broker活着呢,是因为broker要定时向Name Server发心跳,所以
最后得保证Name Server的高可用和broker的高可用,可以搭建Name Server
的三节点集群,broker双主双从,两master两slave,master向slave同步
数据
怎么保证消息不丢失
消息为啥会丢失
三个环节:
1、生产者到MQ这段,MQ接受不到
2、到达MQ还没持久化,MQ宕机了
3、MQ到消费者,MQ发送了消息,消费者接受异常,宕机了,
MQ认为给你了,然后把消息删除了
怎么保证不丢
发送方发送消息,MQ收到,返回确认信息,
MQ进行消息持久化(如果在持久化过程中宕机?这时高可用就派上
用场了,假如说总共10Master,然后在同一时间都挂了,那就点背
吧,喝凉水塞牙了,极端情况排除,这世界谁也做不到完全完美)
MQ收到消费方的ACK确认在删除本地消息
消息重复怎么办(怎么保证幂等性)
产生重复消息是会发生的,网络问题,这句话这么说,没办法保证消息不重复,
但可以保证重复了通过技术手段来不影响业务
场景1:生产者给MQ发消息,MQ确实收到了,也要给生产者回复确认,但此时
不幸网络就出问题了,生产者接受不到,那么会在给MQ发,那么就重复了
场景2:消费者回复确认ACK消息时,MQ没收到,导致MQ的本地消息不会删除
那么消费者可能就会产生重复消费同一条消息
解决办法:
发送消息时携带一个全局唯一的id
获取消息时先根据id在redis/db中查询是否被消费过
如果没消费过,正常消费,消费完,写入redis/db中
如果消费过,那就丢弃
怎么保证消息的顺序性
怎么理解顺序性
比如订单创建、订单付款、订单完成,生产和消费都是带有顺序的
怎么保证顺序性
都保证不了消息顺序到达(一个生产者给两个MQ发)
可以保证消息顺序到达,但保证不了顺序消费(一个生产者给一个MQ发)
生产者:MQ:消费者 ———》 这种可以保证顺序消费,但容易消息堆积,还不能并行,违背初衷了
解决办法:
根据在代码(参考rocketMQ发送方源码)中传入不同的id,然后会发送到不同的队列中(id%mqs.size()取模),保证
并行消费,然后消费方代码(参考rocketMQ消费方源码)再通过分段锁保证顺序消费
注意的是,消费完要ACK确认,然后释放锁,才能进行顺序消费
怎么保证数据一致性
分布式事务来保证
比如库存服务和订单服务,跨数据库,要么同时成功,要么同时失败