一、为什么要使用消息队列?
主要三点原因:解耦,异步,削峰;
1、解耦;
如果服务A和服务B耦合,服务B出了问题,会导致服务A访问服务B时失败;
如果使用消息队列,可以返回给用户成功,先把消息持久化,等服务B恢复后,就可以正常消费了;
2、异步:
将消息写入消息队列,非必要的业务逻辑以异步的方式运行不影响士流程业务
3、削峰;
消费者按照数据库能处理的并发量,从消息队列中拉取消息;
比如秒杀活动,一般会因为流量过大,从而导致流量暴增,应用挂掉;
这个时候加上消息队列,服务器接收到用户的请求后,首先写入消息队列,如果消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;
二、消息队列带来的代价(缺点)
1、系统可用性降低,
如果消息队列挂了,可能影响业务系统的可用性;
2、系统复杂性增加,
引入消息队列,要多考虑很多方面的问题;比如:一致性问题,如何保证消息不被重复消费,如何保证消息不丢失等;
三、常见的消息队列对比
ActiveMQ,主从架构,社区成熟,但性能比较差,版本迭代慢,不推荐使用;
RabbitMQ,主从架构,性能优秀,延时低,达到微秒级,其他产品都是ms级,但基于erlang开发,很难做源码级别的研究和定制,不推荐使用;
RocketMQ,分布式架构,阿里出品,基于Java开发,适合做二次开发和改造;
Kafka,分布式架构,优势在于专为超高吞吐量的实时日志采集、实时数据同步、实时数据计算等场景来设计;
在大数据领域的实时计算以及日志采集被大规模使用;
综上所述,中小型公司,技术实力较为一般,用RabbitMQ是不错的选择;
大型公司,基础架构研发能力较强,用RocketMQ是不错的选择;
如果是大数据领域的实时计算、日志采集等场景,用Kafka是业内标准的;
四、如何保证消息队列的高可用
Kafka: partition和replica机制
Kafka基本架构是多个broker组成,每个broker是一个节点;创建一个topic可以划分为多个 partition,每个partition可以存在于不同的broker上,每个 partition就放一部分数据,这就是天然的分布式消息队列;
意思是,一个topic的数据,是分散放在多个机器上的,每个机器就放一部分数据;
Kafka0.8以后,提供了HA机制,就是replica副本机制,每个 partition的数据都会同步到其他机器上,形成自己的多个replica副本;
然后所有replica会选举一个 leader出来,生产和消费都跟这个leader打交道,其他replica就是follower;
写的时候,leader会负责把数据同步到所有follower上去,读的时候直接读leader;
五、如何保证消息顺序?
1、kafka的topic是无序的,但是一个topic包含多个partition,每个partition内部是有序的;
只要保证生产者写消息时,按照一定的规则写到同一个
partition,不同的消费者读不同的partition消息,就能保证生产和消费者消息的顺序;
2、消费者使用异步重试机制;
在处理消息时,先判断该数据唯一性id(比如订单号)在重试表有没有数据,如果有,则直接把当前消息保存到重试表。如果没有,则进行业务处理,如果出现异常,将该消息保存到重试表;比如订单系统发了“下单”、“支付”、“完成”三条消息;
下单消息由于网络原因处理失败了,后面两条消息是无法入库的,因为只有下单消息的数据才是完整的数据,其他类型的消息只会更新状态;
首先,考虑异步重试机制,如果同步重试,会严重影响消息消费者的消费速度,降低它的吞吐量;然后,考虑消费顺序,必须下单处理完成后,支付才处理,所以处理支付消息时,根据唯一id(订单号)去重试表查,如果有,则将自身也放入重试表,等待重试表进行顺序处理;
六、如何解决消息积压
1、消息体过大
虽说kafka号称支持百万级的 TPS,但从producer发送消息到 broker需要一次网络10,broker写数据到磁盘需要一次磁盘10(写操作),consumer从
broker获取消息先经过一次磁盘 IO (读操作),再经过一次网络10;
一次简单的消息从生产到消费过程,需要经过2次网络10和2次磁盘10,如果消息体过大,势必会增加10的耗时,进而影响 kafka生产和消费的速度;
消费者速度太慢,就会出现消息积压;
而且消息体过大还会浪费服务器的磁盘空间;
解决方案:
消息队列里的消息体,尽量只包含必要信息,非必要信息让消费者自己去查;
2、路由规则不合理
为了保证顺序,按照唯一id去路由 partition,可能会导致某些 partition消息太多,消费者消费不过来,而某些partition消息太少,消费者空闲的情况,
解决方案:
从业务层面出发,改变路由规则;使消息发送更均匀;
3、如果已堆积了十几万消息,只能临时紧急扩容
新建一个topic,partition是原来的10倍,临时建立好原先10倍的queue数量;
然后写一个临时的分发数据的 consumer程序,部署上去,消费积压的数据,直接均匀轮询写入临时建立好的10倍数量的 queue;
接着临时使用10倍的机器来部署 consumer,每一批consumer消费一个临时queue的数据;
这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据;
等快速消费完积压数据之后,恢复原先部署的架构;
七、mq中的消息过期失效了怎么办
可以采取批量重导,就是大量积压的时候,直接将数据写到数据库,等过了高峰期以后将这批数据一点点导出来,重新写入mq中;