解决的问题:
不用 MQ 系统耦合场景
A 系统产生了一个比较关键的数据,很多系统需要 A 系统将数据发过来,强耦合(B,C,D,E 系统可能参数不一样、一会需要一会不需要数据,A 系统要不断修改代码维护)
A 系统还要考虑 B、C、D、E 系统是否挂了,是否访问超时?是否重试?
使用 MQ 系统解耦场景
总结:通过一个 MQ 的发布订阅消息模型(Pub/Sub), 系统 A 跟其他系统就彻底解耦了。
不用 MQ 同步高延迟请求场景
一般互联网类的企业,对用户的直接操作,一般要求每个请求都必须在 200ms以内,对用户几乎是无感知的。
使用 MQ 进行异步化之后的接口性能优化
提高高延时接口
没有用 MQ 时高峰期系统被打死的场景
高峰期每秒 5000 个请求,每秒对 MySQL 执行 5000 条 SQL(一般MySQL每秒 2000 个请求差不多了),如果MySQL被打死,然后整个系统就崩溃,用户就没办法使用系统了。但是高峰期过了之后,每秒钟可能就 50 个请求,对整个系统没有任何压力。
使用 MQ 进行削峰的场景
5000 个请求写入到 MQ 里面,系统 A 每秒钟最多只能处理 2000 个请求(MySQL 每秒钟最多处理 2000 个请求),系统 A 从 MQ 里慢慢拉取请求,每秒钟拉取 2000 个请求。MQ,每秒钟 5000 个请求进来,结果只有 2000 个请求出去,结果导致在高峰期(21小时),可能有几十万甚至几百万的请求积压在 MQ 中,这个是正常的,因为过了高峰期之后,每秒钟就 50 个请求,但是系统 A 还是会按照每秒 2000 个该请求的速度去处理。只要高峰期一过,系统 A 就会快速的将积压的消息给解决掉。
算一笔账,每秒积压在 MQ 里消息有 3000 条,一分钟就会积压 18W 条消息,一个小时就会积压 1000 万条消息。等高峰期一过,差不多需要 1 个多小时就可以把 1000W 条积压的消息给处理掉
架构中引入 MQ 后存在的问题
1. 系统可用性降低
MQ 可能挂掉,导致整个系统崩溃
2. 系统复杂性变高
可能发重复消息,导致插入重复数据;消息丢了;消息顺序乱了;系统 B,C,D 挂了,导致 MQ 消息积累,磁盘满了;
3. 一致性问题
本来应该A,B,C,D 都执行成功了再返回,结果A,B,C 执行成功 D 失败
Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点
建议:中小型公司 RabbitMQ 大公司:RocketMQ 大数据实时计算:Kafka
broker进程就是kafka在每台机器上启动的自己的一个进程。每台机器+机器上的broker进程,就可以认为是 kafka集群中的一个节点。
你创建一个 topic,这个topic可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上,每个 partition就存放一部分数据。
这就是天然的分布式消息队列,也就是说一个 topic的数据,是分散放在 多个机器上的,每个机器就放一部分数据。
分布式的真正含义是每个节点只放一部分数据,而不是完整数据(完整数据就是HA、集群机制)
Kafka 0.8版本之前是没有 HA 机制的,任何一个 broker 宕机了,那么就缺失一部分数据。
Kafka 0.8以后,提供了 HA 机制,就是 replica 副本机制。
每个 partition的数据都会同步到其他机器上,形成自己的多个 replica 副本。然后所有 replica 会选举一个 leader。那么生产者、消费者都会和这个 leader 打交道,然后其他 replica 就是 follow。写的时候,leader 负责把数据同步到所有 follower上去,读的时候就直接读 leader 上的数据即可。
如果某个 broker宕机了,刚好也是 partition的leader,那么此时会选举一个新的 leader出来,大家继续读写那个新的 leader即可,这个就 是所谓的高可用性。
写数据的时候,生产者就写 leader,然后 leader将数据落地写本地磁盘,接着其他 follower 自己主动从 leader来pull数据。一旦所有 follower同步好数据了,就会发送 ack给 leader,leader收到所有 follower的 ack之后,就会返回写成功的消息给生产者。
消费的时候,只会从 leader去读,但是只有一个消息已经被所有 follower都同步成功返回 ack的时候,这个消息才会被消费者读到。
MQ 只能保证消息不丢,不能保证重复发送
Kafka 消费端可能出现的重复消费问题
每条消息都有一个 offset 代表 了这个消息的顺序的序号,按照数据进入 kafka的顺序,kafka会给每条数据分配一个 offset,代表了这个是数据的序号,消费者从 kafka去消费的时候,按照这个顺序去消费,消费者会去提交 offset,就是告诉 kafka已经消费到 offset=153这条数据了 ;zk里面就记录了消费者当前消费到了 offset =几的那条消息;假如此时消费者系统被重启,重启之后,消费者会找kafka,让kafka把上次我消费到的那个地方后面的数据继续给我传递过来。
重复消息原因:(主要发生在消费者重启后)
消费者不是说消费完一条数据就立马提交 offset的,而是定时定期提交一次 offset。消费者如果再准备提交 offset,但是还没提交 offset的时候,消费者进程重启了,那么此时已经消费过的消息的 offset并没有提交,kafka也就不知道你已经消费了 offset= 153那条数据,这个时候kafka会给你发offset=152,153,154的数据,此时 offset = 152,153的消息重复消费了
保证 MQ 重复消费幂等性
幂等:一个数据或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的,不能出错。
思路:
MQ 传递非常核心的消息,比如:广告计费系统,用户点击一次广告,扣费一块钱,如果扣费的时候消息丢了,则会不断少钱,积少成多,对公司是一个很大的损失。
Kafka 可能存在的数据丢失问题
1. 消费端弄丢数据
原因:消费者消费到那条消息后,自动提交了 offset,kafka以为你已经消费好了这条消息,结果消费者挂了,这条消息就丢了。
例子:消费者消费到数据后写到一个内存 queue里缓存下,消息自动提交 offset,重启了系统,结果会导致内存 queue 里还没来得及处理的数据丢失。
解决方法:kafka会自动提交 offset,那么只要关闭自动提交 offset,在处理完之后自己手动提交,可以保证数据不会丢。但是此时确实还是会重复消费,比如刚好处理完,还没提交 offset,结果自己挂了,此时肯定会重复消费一次 ,做好幂等即可。
2. Kafka 丢掉消息
原因:kafka 某个 broker 宕机,然后重新选举 partition 的 leader时,此时其他的 follower 刚好还有一些数据没有同步,结果此时 leader挂了,然后选举某个 follower成 leader之后,就丢掉了之前leader里未同步的数据。
例子:kafka的leader机器宕机,将 follower 切换为 leader之后,发现数据丢了
解决方案:(保证 kafka broker端在 leader发生故障,或者leader切换时,数据不会丢)
按 2 的方案设置了 ack =all,一定不会丢。它会要求 leader 接收到消息,所有的 follower 都同步 到了消息之后,才认为本次写成功。如果没满足这个条件,生产者会无限次重试 。
背景:mysql binlog 同步的系统,在mysql里增删改一条数据,对应出来了增删改 3 条binlog,接着这 3 条binlog发送到 MQ 里面,到消费出来依次执行,起码是要保证顺序的吧,不然顺序变成了 删除、修改、增加。日同步数据达到上亿,mysql->mysql,比如大数据 team,需要同步一个mysql库,来对公司的业务系统的数据做各种复杂的操作。
场景:
Kafka 消息顺序错乱
写入一个 partition中的数据一定是有顺序的。
生产者在写的时候,可以指定一个 key,比如订单id作为key,那么订单相关的数据,一定会被分发到一个 partition中区,此时这个 partition中的数据一定是有顺序的。Kafka 中一个 partition 只能被一个消费者消费。消费者从partition中取出数据的时候 ,一定是有顺序的。
Kafka 保证消息顺序性
如果消费者单线程消费+处理,如果处理比较耗时,处理一条消息是几十ms,一秒钟只能处理几十条数据,这个吞吐量太低了。肯定要用多线程去并发处理,压测消费者4 核 8G 单机,32 条线程,最高每秒可以处理上千条消息
消费端出了问题,不消费了或者消费极其慢。接着坑爹了,你的消息队列集群的磁盘都快写满了 ,都没人消费,怎么办?积压了几个小时,rabbitmq设置了消息过期时间后就没了,怎么办?
例如:
场景:几千万条数据再 MQ 里积压了七八个小时
快速处理积压的消息
一个消费者一秒是 1000 条,一秒 3 个消费者是 3000 条,一分钟是 18W 条,1000 多 W 条需要一个小时恢复。
步骤:
原来 3 个消费者需要 1 个小时可以搞定,现在 30 个临时消费者需要 10 分钟就可以搞定。
如果用的 rabbitmq,并且设置了过期时间,如果此消费在 queue里积压超过一定的时间会被 rabbitmq清理掉,数据直接搞丢。
这个时候开始写程序,将丢失的那批 数据查出来,然后重新灌入mq里面,把白天丢的数据补回来。
如果消息积压mq,长时间没被处理掉,导致mq快写完满了,你临时写一个程序,接入数据来消费,写到一个临时的mq里,再让其他消费者慢慢消费 或者消费一个丢弃一个,都不要了,快速消费掉所有的消息,然后晚上补数据。