都 2020 年,应该没有项目不用消息队列的吧?消息队列解决了哪些问题,条件反射应该都能回答“异步消息”、“应用解耦”和“流量削峰”和。
消息队列使用场景
1、异步处理
比如 @Python大星 之前做的基金项目,当用户购买成功后,有如下几个操作:
① 保存订单信息;② 短信通知用户;③ 当购买金额大的时候邮件通知销售人员;④ 用户积分增加,可兑换奖品或者优惠券。
同步处理这些请求,用户体验差。当我们对接口的 Rt(ResponseTime 响应时间)有要求时,需要异步处理比如短信、邮件和积分问题。
2、应用解耦
比如线下活动购物 POS 机消费,如果消费满足一定条件,就进行微信推送引导用户参与抽奖。当多部门协助时,其他部门负责 POS 机消费信息推送,本部门负责接收信息处理相应的业务逻辑。
3、流量削锋
一般在秒杀或团抢活动中使用广泛。用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面。秒杀业务根据消息队列中的请求信息,再做后续处理。
MQ 选型
面试中,考官经常会问你技术选型的问题,比如消息中间件,市场上种类繁多,如何选择一个合适的呢?
咨询了很多开发小伙伴,有的用 kafka、有的用 rocketMQ、还有的用 rabbitMQ。没有好坏之分,只有适不适合。
图 1
根据图 1 中,我们可以综合几个方面来选型:
① 社区活跃性。
万一哪天这个 mq 社区没人维护,出现 bug, 怎么办,重新再选一个 MQ 吗?从图中可以看出 ActiveMQ 活跃度很低。
② 单机吞吐量。
目前 ActiveMQ、RabbitMQ 吞吐量在万级,而 RocketMQ 和 kafka 在 10 万级;
③ 时效性
ActiveMQ、RocketMQ 和 kafka 延迟在 ms 级别,RabbitMQ 延迟在 ms 级别;
④ 可用性
ActiveMQ、RabbitMQ 基于主从复制,高可用; RocketMQ 和 kafka 非常高,分布式架构;
⑤ 消息可靠性
RocketMQ 有较低概率丢失数据,RabbitMQ 基本不丢,RocketMQ 和 kafka 可以做到 0 丢失;
⑥ 功能扩展
kafka 功能较简单,日志采集 nb,其他是三个不分上下。
kafka
把消息放到【队列】里,我们叫【消息生产者】 producer,把 消息从队列里取出来,我们叫【消息消费者 】consumer。
1、consumer 如何区分 producer 里的消息???
通常,我们在使用消息队列时,n to n,多个 producer 和 consumer,如何进行区分,这里使用了 topic 的概念,给 队列取个响亮的名字。
2、kafka 如何保证吞吐量???
从上面图 1 中我们看出,kafka 在 topic 从几十到几百个时,吞吐量会大幅度下降,尽量保证 topic 数量不要过多。
比如说:n 个 producer 往 topic 里塞消息, n 个 consumer 从 topic 里取消息。
为了提高吞吐量,kafka 使用 partion 分区的概念(和 Mysql 分区类似)
3、kafka 如何保证高可用
当然是集群了,用它,用它,用它。
在 kafka 集群中,我们把每个 kafka 服务器叫“broker”。
比如说我们这 3 台 broker,每台里有 3 个 partion,当 producer 生产消息时,分别进入 3 台 broker 的一个分区中(作为主分区),每台 broker 里剩余的 2 个 partion 只做备份 其他 2 个 broker 里的消息(备份分区)。 你品,你品,你细品。当某个 broker 挂了,选举其他 broker 里的 partion 来做主分区,实现高可用。
4、消费者如何保证消费消息
假设多个消费者 consumer 组团去消费分区里的消息,一个 consumer 对应 一个 分区,假设有一个 consumer 挂了,必然另一个 consumer 要去接盘,但是我得知道挂掉的 consumer 消费进度。
kafka 在这里使用了 offset 的概念,在以前版本的 Kafka,这个 offset 是由 Zookeeper 来管理的,后来 Kafka 开发者认为 Zookeeper 不合适大量的删改操作,于是把 offset 在 broker 以内部 topic (__consumer_offsets) 的方式来保存起来。
消息队列引发的问题
1、如何保证消息的幂等性???
① 数据库层面使用唯一性索引;
② 每次先查再保存到数据库;
2、如何保证消息的可靠性传输???
集群
3、如何保证消息的顺序性???
生产者在写的时候,其实可以指定一个 key,比如说我们指定了某个订单 id 作为 key,那么这个订单相关的数据,一定会被分发到同一个 partition 中去,而且这个 partition 中的数据一定是有顺序的。
写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。
@Python大星 | 文