08 | 答疑解惑(一) : 网关如何接收服务端的秒杀结果?
08 | 答疑解惑(一) : 网关如何接收服务端的秒杀结果?
讲述:李玥
时长12:52大小11.77M
1. 网关如何接收服务端的秒杀结果?
2. 详解 RocketMQ 和 Kafka 的消息模型
3. 如何实现单个队列的并行消费?
4. 如何保证消息的严格顺序?
写在最后
赞 71
提建议
精选留言(97)
- 大龄小学生置顶2019-08-08老师,一图胜千言,来点图吧。
作者回复: 你要的图来了。我在文中补充了一个流程图,便于同学们理解。
共 3 条评论42 - linqw2019-08-08学习完网关答疑篇,写下自己的理解和疑惑,老师有空帮忙看下哦 1、秒杀的理解: APP--发送秒杀请求--》网关(也是RPC服务端,和配置中心保持长连接,比如nacos,将其路由和配置信息定时的发送给配置中心,配置中心对其进行管理,定时的清除宕机的网关路由信息,如超过一定时间没有接收到网关的心跳包)--》将其APP请求做一定的封装,增加网关id和网关实例中唯一的请求id发送给消息队列,为了保证消息不丢失,网关对其发送消息出现的异常进行处理,如超时异常,直接返回秒杀失败,网关发送消息的这个过程中可能涉及到分布式事务,使用消息队列的分布式事务进行处理,然后网关需要等待一段时间,等待秒杀服务端使用RPC调用网关实例的接收秒杀结果,为此创建一个新对象,将其请求id做为key,新对象做为value放入CurrentMap中,调用新对象的超时wait方法进行等待秒杀结果--发送封装的APP请求,包含网关id和请求id--》消息队列接收APP请求消息,为了保证消息不丢失,开启Sync_Flush参数将消息保存到磁盘,并且为了防止一台机器磁盘出问题,集群需要2台机器都有消息才确认请求--从消息队列中拉取消息--》秒杀服务端,为了低延迟执行风控、预占库存,拿到消息中网关id,从本地路由中查询网关id的实例信息,如果获取不到调用网关实例时,需先从配置中心获取到网关的路由信息,秒杀服务端也需和配置中心保持长连接,定时的从配置中心拉取网关的路由信息,保存到本地,使用RPC调用网关实例的接收秒杀结果的方法,为了保证消息不丢失,先执行消费逻辑,再响应消息队列,如果根据网关id获取不到网关实例,或者确认消息队列超时或出现异常,秒杀服务端回滚事务,此过程也涉及到分布式事务,为了防止消费重复消息,接口的幂等性,将请求id和网关id做为唯一键。也为了防止消息积压,消息队列中的主题队列和消费组中的消费者一一对应,保证消息被快速消费。 2、秒杀异步,APP发送请求给网关,网关接收请求后将请求做一定的封装(包括请求id,网关id,账户id),然后发送到消息队列中,响应APP请求,无需等待后需的流程,然后秒杀成功以否直接返回,后续流程处理完使用短信的形式告知用户是否秒杀成功,不知道这样做法是否可行。 3、最近在撸rocketmq的源码,搞了namesrv、logging、logappend模块,想成为commiter,立个flag,等后续JMQ出来,撸其源码,也想成为commiter,道阻且长,持续进化。展开
作者回复: 我认真的看了同学的对于秒杀的理解,技术上都没什么问题。 从业务角度,老师有一些不同的看法。 对于秒杀这种场景,宏观上的设计应该是倾向于利用有限的资源处理短时间内海量的请求,保证服务不宕机。有少量请求处理出错(注意是后端错误,用户不可见)或消息丢失,是可以接受的。 毕竟秒杀拼的就是运气,某个用户秒杀请求在处理的时候丢失,和处理成功但没秒到,对于用户来说都是运气不好而已。 基于这样的设计理念,很多保证数据可靠性的做法都可以牺牲掉,用于换取系统更大的吞吐量比较划算。
共 2 条评论67 - 滴流乱转小胖儿2019-08-08mq界,吴彦祖老师你好,感谢分享
作者回复: 谢谢你,蔡徐坤同学。
共 8 条评论37 - 摩云神雕2019-09-20请教下老师,topic的partition数 是根据什么确定的? 我理解partition的设置 是为了一个消费组中多个消费者并行消费的, 那么partition数根据什么设定呢? 根据消费者数 和 broker机器的性能吗 假设我现在的kafka有 3个broker节点, 创建了一个topic, partition值设为5, 然后我的一个消费组中有5个消费者, 正好一个消费者 消费一个partition, 后续, 这个消费组 我又想加2个消费者, 呢partition数 也调成7 是吧? 但是, 我这个topic 可能被好几个消费组消费的, 你消费组A扩展了消费者数, 想扩展partition数, 但是 我消费组B没这个需求啊, 怎么办? 这个topic是消费组A、B、C、D大家共同订阅的, 就因为 你A组加了几个消费者, 我就去改这个topic的partition数吗? 这里没太想通; 另外,在生产环境中 改了partition数,会造成什么影响?展开
作者回复: 多个消费组的时候,确实有你说的问题,partition数量需要兼顾所有消费者。一般的做法都是照顾消费最慢的那个消费组,按照它的速度和消费者数量来确定partition数量。 大部分MQ都支持动态扩容,增加分区数量,分区数量变更后,会重新分配消费者和分区对应关系,对生产基本没什么影响。
共 5 条评论27 - 虢國技醬2019-09-05"同一个消费组内,每个队列只能被一个消费者实例占用" 我在想:如果队列只有两个Q1和Q2,但是G1中有三个消费者实例C1,C2和C3;那会不会有一个消费者实例一直在偷懒(没有干活的机会,😂)。 对于这种情况会报错?还是说内部会有机制队列的占用是换着分配给消费者实例的(不是某个实例一直占用)?
作者回复: 是的,这时候就会有一个消费者没有干活的机会。 至于,如何分配队列与消费者的关系,不同的消息队列处理也不一样,有的消息队列是绑定队列与消费者,这样有一个消费者一直闲着,其它二个一直干活。 也有的消息队列是分时绑定,也就是你干一会儿,我干一会儿,但任何时刻,都会有一个消费者处于闲着的状态。
共 2 条评论24 - 木木木2019-08-20关于有序性还有疑惑,即使采用了一致性hash,无论扩容还是缩容队列,对分配相邻队列的用户部分还是有影响的,难道要等这些队列消费完了,阻止生产者发消息吗?感觉不具有可操作性
作者回复: 扩容后只需要等一会儿,确保扩容之前的消息都消费完成了(不确定的话可以等久一点儿也没关系)再消费新分区的数据就可以了,生产不需要停。 因为一致性哈希可以保证单调性:如果已经有一些内容通过哈希分派到了相应的分区中,又有新的分区加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的分区中去,而不会被映射到旧的分区集合中的其他分区
19 - 大魔王汪汪2019-08-08看了几期,感觉分布式消息队列的设计方案和分布式存储系统的设计方案很类似,如果再加上事务处理,存储细节方案应该更像了
作者回复: 同学那不是像,消息队列就是个分布式存储系统。
共 2 条评论13 - Liam2019-08-14秒杀这个案例中,超时之后不需要补偿机制吗,对于下游服务来说很可能以及成功了
作者回复: 这个案例中,你说的这种情况是有可能存在的。 是否需要补偿,也无所谓对错,总体效果是一样的。秒杀的目的就是从众多秒杀用户中公平的选择n个用户,补偿或不补偿,影响的只是这n个用户是谁的问题。 所以这是一个架构选择的问题。 我建议是不用补偿,按失败处理,锁定的库存超时未支付后会自动释放,好处是比较简单。
共 2 条评论11 - asdf1002019-08-22消息一直在队列里不删除?
作者回复: 消息什么时候删除取决于消息队列的配置,比如Kafka默认就是超过多长时间后就自动删除了。
共 2 条评论10 - 大白先生2019-08-09老师,在秒杀场景中,后端服务通过调用onResult方法来放入秒杀结果,有没有可能有一种情况,就是秒杀成功了,但是获取mutex是空,这样的活,会不会前段接收了秒杀失败的提示,但是库存扣减成功了
作者回复: 会有这种情况。但无需特殊处理。 一般秒杀成功后,还有后续的支付流程。 有些用户秒杀成功也不会支付。你说的这种情况与“秒杀成功不支付”一并处理就行了。一般都是有一个支付超时,超过这个时限还没支付,就取消这个秒杀资格,自动释放库存。
共 2 条评论10 - 托尼斯威特2019-11-04刚开始学习 RocketMQ. Consumer可以register的MessageListener 有两种: MessageListenerOrderly 和 MessageListenerConcurrently. 这和老师讲的, RocketMQ 每个queue只要一个单线程消费者矛盾吗?
作者回复: 首先强调一下,并不是“每个队列只要一个单线程消费者”,而是“每个队列只能被一个消费者实例占用。” rocketMQ的MessageListenerConcurrently,和我们上面讲的内容也不矛盾,它这个并行消费是完全在客户端实现的。大致的原理就是: 1. 客户端从服务端的某个队列读取一批消息; 2. 分发给客户端的多个线程消费; 3. 都消费成功后,给服务端返回消费成功确认。
共 5 条评论10 - 切糕2019-11-03我是看了rocketMQ源码后在看了一遍,受益匪浅9
- haha2019-08-08七夕不过节,继续学习 —— 谁让我是单身狗呢共 2 条评论9
- 海罗沃德2019-12-09说到一致性哈希算法,王争老师的算法课程里专门有一课讲一致性哈希,跟李玥老师的课互相印证,算法就落地了
作者回复: 其实这些最基础的算法和数据结构的应用是非常广泛的,只是为了方便,被很多中间件或者底层系统给封装后,对一般的业务开发人员就不可见了。
8 - 猿人谷2019-08-08这篇答疑解惑,虽然简短,但绝对的诚意十足。希望多出这种答疑解惑的章节,毕竟评论区里很多留言的问题非常有代表性,对代表性的问题出这种答疑解惑的章节,学到的更多,也更能体现大家的参与度。8
- skyun2019-08-08关于顺序消息还是有点疑问,考虑一下几个场景: 1、消息消费失败进入了重试队列; 2、增加或减少了消费者组里的消费者个数,导致consumer与queue之间的对应关系重新映射; 3、broker节点故障或者超时导致投递失败,业务上重新选择broker投递 这几种场景下,即使在投递时通过selector进行选择了queue,我想依旧会导致消息的乱序,请教下老师,这对在这些场景下,得分别采用怎么样的方式来保证消息的有序性。问题有点多,麻烦老师了展开
作者回复: a1:这个只能关闭重试队列。 a2: consumer rebalance不会影响到消费顺序,因为对于单个队列来说,无论是不是rebalance,都是串行消费的。 a3:这个无需担心,也不用做任何处理,消息队列自身可以保证即使broker宕机发生切换,依然保证队列中消息的顺序不会变。
共 4 条评论6 - vi2019-08-21李sir,一个迟来的学生,刚刚看到这里,对于rabbitmq来说,没有消费组队列,只有exchange转送到相应的对列中,要想提高并发,看到的方法可以设置多线程消息,好像变成了单个对列并行消息的模式,会不会也有文中所说的这个问题,rabbitmq还可以通过设置prefetch来缓存一定的数目,是不是就相当于增加每个消费者的队列数来解决并发的问题了
作者回复: 想要增加消费并发,可以考虑exchange把消息平均的分摊到多个消费队列中。
共 2 条评论5 - Geek_821c962020-05-29老师,你好。 秒杀结果的map是不是可以替换成Redis更好些?
作者回复: 这是个选择的问题,无所谓哪个更好。使用Redis持久化秒杀请求后,引入了复杂度和故障点,系统维护更困难了,但提升了请求的成功率,秒杀服务变为无状态服务代码逻辑也更简单。
4 - 大白先生2019-08-08对于这句话我不是很理解,还请老师帮忙解答。“队列中当前有 10 条消息,对应的编号是 0-9,当前的消费位置是 5。同时来了三个消费者来拉消息,把编号为 5、6、7 的消息分别给三个消费者,每人一条。”一个消费队列在一个消费组内不是只有一个消费者可以消费消息么,怎么能够同时来拉消息。如果您说的三个消费者是三个消费组的话,每个消费组维护自己的消费位置都是5,那么消费的都会是编号5的消息呀。
作者回复: 我们不是在考虑如何来实现并行消费么?就不能再遵循现有的串行消费的限制了呀。
共 3 条评论4 - 张三丰2019-10-21如果有一台机器宕机了,而宕机前某个队列记录的是这个账户的流水账,宕机后这个账户的流水账可能会转移到其他的队列,这个时候怎么保证这个账户的流水账是有序的呢?
作者回复: 关于如何保证严格顺序的问题,我们在这节课中也有讲到哦。
3