消息队列的常见问题

it2022-05-06  0

一.什么是消息队列?为什么使用消息队列?消息队列有什么优点和缺点? 答:消息队列就是一个容器,当我们需要使用消息的时候,我们从容器中取出消息队列,来供自己使用。 消息队列是一种先进先出的数据结构,所以消费消息时也是按照顺序来消费的,比如生产者生产消息的 为什么使用消息队列:解耦,异步,削峰(三个优点) 解耦:如上图所示,如果A系统发送数据给BCDE系统,现在F系统也需要A系统的数据,那A系统就需要修改代码发送给F系统。因为A系统产生关键的数据,那是否要考虑BCD系统是否挂掉,是不是要重发,等耦合再一起。 使用MQ 如下图所示:A系统产生一条关键的数据,放到MQ队列中,如果那个系统需要用到这条数据,就去消息队列里消费,如果那个系统不需要这条数据,取消对该消息的订阅即可,因此A系统不需要去维护BCD系统成功还是失败 因此通过一个MQ就可以使A系统和其他系统进行了解耦 削峰填谷:如右图所示每天A系统在调用量都是在50左右只有在上午10点的时候会出现每秒高并发的请求大约5k/s,如果系统是基于数据库的,这样高的并发有可能把数据库挂掉,mysql的一般能抗住2k/s左右,因为只是在10点的时候才有高的并发量,如果通过分库来实现又会造成资源浪费,因此可以通过MQ消息来实现这个逻辑,如左图所示:如果使用了A系统,如果有5k的请求,写到消息队列中,因为A系统每秒处理2k个因此每次可以取小于2k个,这样即使高峰的时候也不会挂掉。这样生产者每秒是5k个进来,消费者是2k个出去,因此会在队列中有积压,每小时大约 积压几十万,因为过了这个高峰期每次请求 就是50个,在这个时候慢慢消费也是ok的 异步:现在有一个场景A系统写库消耗的时间是50ms,B系统写库需要耗时140ms。C系统写库需要耗时100ms,D系统写库需要耗时210ms,这样一个请求就需要耗时50+140+100+210=400ms这还没要计算其他时间的消耗,如果依赖的系统更多则耗时更久,因此这样的设计几乎是不能接受的。如果使用MQ队列则 优点:解耦,每个成员不受其它成员的阻塞,只需要一个简单的容器就可以 提高性能:系统B不再依赖系统A,只需要取监听容器中的消息就可以 削峰填谷: 异步: 缺点:系统的可用性降低,原因:系统引入的外部越多,越容易挂掉,举例:完成A的调用需要BCD步骤,如果BCD挂掉,导致 调用A的调用失败。 系统的复杂性提高了,可能提高了10倍 一致性问题:当A系统处理完了直接返回都以为A的请求成功了。但是问题,要是BCD三个系统,其中的一个失败,这样就就是调用A的系统是失败的,但对A的处理默认是成功的 二.如何保证消息的可靠性阐传输?(如何处理消息丢失的问题) 生产者丢失消息 在生产者发送数据之前开启事务,如果生产者的消息没有被消息组件接收成功 ,就会重复该消息,直到消息被接受成功 kafka如何保证不丢消息 1.给topic设置replication.factor参数:这个值必须大于1,要求partition最少有一个副本 2.在kafka服务端设置min.isync.replicas参数:这个值必须大于1,标识要求至少有一个follower在跟自己保持联系且正常同步数据,这样在能在leader挂掉之后,还有follower。 3.在生产者设置ack=all;表示要求每条消息在写入副本上之后,才算是成功的 4.在生产端设置retries=max(很大的一个值,表示无限重试)一旦写入事变,无限重试 rabbitmq:设置消息持久化,只有消息持久化到了磁盘,才会通知生产者ack,这样就算持久化之前机器挂掉了,生产者也可以进行重发,(其实也是写到了内存是在内存缓存页上)

三.如何保证消息的顺序性 RabbitMQ:有一个生产者,一个queue,多个消费者,当生产者一次生产了三个数据data1/data2/data3,放入队列中,则三个消费者,消费消息,如果依下面的顺序生产完毕data2/data1/data3,则顺序就错乱了。 RabbitMQ:拆分多个queue,每一个queue对应一个consume,而每一个消费者可以对应多个queue,一个queue对应多个worker数。 比如我们有一个topic,有三个partion,在生产者写的时候可以指定一个key,以订单为key,那么关于订单的信息就会在同一个partion中,而且partion中的数据也是有顺序的,在同一个partion中一般是多线程处理消息,这样就导致消息的顺序有可能是乱序的。如果使用单线程就有可能导致吞吐量不高,原因是:如果一个消息出库需要几毫秒,那么1秒处理的消息比较少。 解决方法:1.一个topic一个partion一个consumer,内部单线程消费,但是很少这样做,因为吞吐量很低。 2.在一个消费者内存中写N个queue,相同的key放在同一个队列中,然后N个线程消费N个队列,在内存队列中消息是顺序的,那么在消费者线程消费消息的过程中也是有顺序的,因此在数据库中也是有顺序的。 四.消息重复 造成消息重复的根本原因是:网络延迟,只要通过网络交换数据就不能避免这个问题,如果想解决这个问题,就要跳过这个问题。那么问题就变成,消费者如何解决消息重复的问题,或者消息组件怎么解决消息重复的问题 1.消费者处理消息保证消息的幂等性 2.保证每一条消息都有唯一的编号,且保证消息成功后在去重表上都一条成功的日志同时出现 第一条就是不管来多少条重复的消息,都保持消息的幂等性,不管处理多少次,最终的结果都是一样的。2.原理就是利用一张日志表记录已经处理成功的消息ID,在处理之前,先检查去重日志,如果消息ID已经在表中,说明已经成功处理了该日志,则不再重新处理。日志也可以是数据库,消息的ID作为唯一的索引 第一条解决方案很明显是在消费端去处理的,第二条可以在消费端也可以在消息系统中处理,正常情况下重复的消息很少,如果在消息系统中处理这件事会影响系统的吞吐量和高可用有影响,因此在消费端自己处理的重复的消息 五.有几百万的消息积压,怎么处理 场景:几千万条数据在MQ里积压了七八个小时,从下午4点多,积压到了晚上很晚,10点多,11点多。线上故障了,这个时候要不然就是修复consumer的问题,让他恢复消费速度,然后傻傻的等待几个小时消费完毕。这个肯定不行。一个消费者一秒是1000条,一秒3个消费者是3000条,一分钟是18万条,1000多万条。所以如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概1小时的时间才能恢复过来。 1.先修复consumer,确保其能正常消费消息速度,然后将现有的consumer都停掉 2.然后新建10倍的queue和consumer,(新建topic和partion) 3.然后新建一个consumer程序,这个程序用来消费consumer积压的消息,消费之后不做任何处理直接放入新建的partion和queue中 4.然后再用10台机器,来部署consumer,每一批consumer消费一个临时的queue消息 5.这种方法是临时扩大了queue和consumer的资源,用10倍的consumer来消费积压的消息 6.等快消费完积压的消息,则回复原来的消息部署的架构重新用原来的consumer来消费消息 六.消息设置了过期时间,过期就丢了怎么办 如果你使用的是rabbitmq,rabbitmq可以设置过期时间,也就是TTL,如果你在queue队列里,积压的消息超过了设置的过期时间,那么在queue里的消息就会被清理掉,和上述不一样的是,这个消息是被丢掉。 解决的方法:因为消息是被丢失清理掉,因此只能采取批量重导的方法,把丢失的消息重新找回来,放到消息队列中。 七.积压消息长时间没有处理,mq放不下了怎么办 如果消息积压在mq里,处理的太慢,或者是很长时间没有处理消息,导致mq被写满了,这个时候怎们办。 可以采用批量+重新导入的方法来进行处理 首先写个临时程序连接mq消息,消费数据,来一个丢弃一个,快速消费积压的消息,降低mq消息的压力,在晚上的时候,采取批量重新导入的方法,把丢失的数据重新找回来 八.主流消息的对比


最新回复(0)