diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 0ae3860d..b463cea6 100755 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -2187,6 +2187,7 @@ function getBarBigMarket() { children: [ "extra/big-market-v1.md", "extra/big-market-v2.md", + "extra/big-market-v3.md", ] } ] diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-01.gif b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-01.gif new file mode 100644 index 00000000..ef1cf805 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-02.png new file mode 100644 index 00000000..f785dd89 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-03.png new file mode 100644 index 00000000..e5d05ca9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-04.png new file mode 100644 index 00000000..81bcef19 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-05.png new file mode 100644 index 00000000..75e4426e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-05.png differ diff --git a/docs/md/project/big-market/extra/big-market-v2.md b/docs/md/project/big-market/extra/big-market-v2.md index fa3fe4ea..aa7b0f97 100644 --- a/docs/md/project/big-market/extra/big-market-v2.md +++ b/docs/md/project/big-market/extra/big-market-v2.md @@ -10,7 +10,7 @@ lock: no >沉淀、分享、成长,让自己和他人都能有所收获!😄 -大家伙,我是技术UP主,小傅哥。 +大家好,我是技术UP主,小傅哥。 清明假期即将来临,卷王的✋🏻手已经👨🏻‍💻 准备好啦!星球「码农会锁」第8个实战项目,《大营销平台系统》第1阶段用最基本技术栈引导小白入门,第2阶段将引入全体系的分布式技术栈,进行设计实现。—— 你们面试不总缺少分布式技术栈嘛,这回它来啦!😄 diff --git a/docs/md/project/big-market/extra/big-market-v3.md b/docs/md/project/big-market/extra/big-market-v3.md new file mode 100644 index 00000000..b4f228c2 --- /dev/null +++ b/docs/md/project/big-market/extra/big-market-v3.md @@ -0,0 +1,164 @@ +--- +title: 第二阶段启动,分布式架构进行70% +lock: no +--- + +# 《大营销平台系统》第二阶段启动,分布式架构进行70% + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主,小傅哥。 + +我发现,其实不少伙伴并不具有分布式架构开发的经验,而学生👩🏻‍🎓不懂也是在所难免的,但那些`死鬼面试官`管你是不是学生,只要有卷王在,那就拔尖面。还有一部分伙伴一直在公司做 ERP、做B端的,就是业务流程,长、长、长、长、长,但核心技术基本没咋使用。 + +
+ +
+ +**那微服务&分布式架构,都有哪些知识点呢?要怎么学?🤔** + +微服务是把一个庞大的单体应用,拆分为独立的个体应用,每个个体应用通过分布在各个云服务器上进行分布式部署,再通过统一配置中心进行管理分布式应用节点。`这里有人提到 DDD 架构,DDD 是对每个应用软件开发的设计手段。与微服务和分布式,不相关。你可以用其他任何软件设计方法来实现一个独立的应用。` + +那么因为从单体拆分到微服务,在部署成分布式架构,也就需要了如;RPC、MQ、Redis、任务调度、分库分表、配置中心、数据同步等各项技术栈的配合使用。因而也引出了;`接口幂等`、`一致性事务`、`掉单补偿`、`延迟队列`、`延迟双删`、`任务扫描`、`熔断`、`降级`、`限流`、`全链路监控`、`TTL打标`等一些列的知识点。**如图;是小傅哥带着伙伴们基于分布式架构实践的系统。** + +
+ +
+ +那么,接下来小傅哥就基于这样一套系统,给大家介绍分布式架构中一些场景的解决方案。 + +>文末有整套项目的学习路径,涵盖;视频(每节30分钟~60分钟)、小册、代码,学起来嘎嘎有收获! + +## 一、业务介绍 + +### 1. 场景(字节掘金平台) + +为了方便大家理解这块的业务,我们可以看下字节旗下掘金平台的一款实际使用的抽奖效果。因为目前小傅哥做的这块大营销是非常全面的,完全可以支撑掘金的这套使用的。【皮肤UI随便换,电商、出行、外卖各个场景都能用】 + +
+ +
+ +- 地址:[https://juejin.cn/user/center/lottery](https://juejin.cn/user/center/lottery) + +- 流程:`每次点击签到赠送钻石`、`每天免费抽奖1次`、`200个钻石兑换1次抽奖`、`2000个钻石抽奖10次`、`1次抽奖增加10个幸运值`、`6000幸运值可以抽奖到非钻石的实物奖品`、`部分奖品需要抽奖N次后解锁`。—— 这些都是要在系统设计中考虑的点,看着可能不多,但其实满是流程。这也能看出字节研发的功底👍🏻。 + +### 2. 流程 + +基于业务场景诉求,我们来画一下整体的系统的流程。可能看着不大,但流程非常密集。没有点功底,那代码都得写的稀乱。 + +
+ +
+ +- 首先,以用户发起动作为开始,第1步为运营配置活动装配上线,第2~6步为用户参与抽奖活动动作。 +- 之后,第2~4步,为用户对积分的消耗转换为活动账户。类似于你去抓娃娃那,用钱💰换成了币子,几个币子可以抓一次娃娃。 +- 继续,用户从抽奖开始,就类似于商品下单,要扣减他的活动额度账户,比如今天可以参与1次抓娃娃,那么创建一笔流水记录。这样用户如果中途失败了,还可以用流水记录重新发起抽奖。 +- 往下,就可以进入抽奖策略环节了,也就是抽奖大转盘上体现的抽奖N次解锁、多少积分可以抽奖到哪些范围奖品【其实还有黑名单用户抽奖兜底】,这里还有库存的处理。如果2个人同时抽奖到最后1个库存的奖品,就看谁快了,占用了库存的得到奖品,另外一个只能走兜底奖品。 +- 最后,记录用户的中奖信息,和写一个 task 任务表。推送 MQ 消息的方式处理调用接口发奖和回更发奖记录。 + +>那么整个这样的业务和对应的研发流程设计,中间会有哪些细节的场景问题呢?🤔 接下来分析下。 + +## 二、场景方案 + +### 1. 为什么使用分库分表,做了分库分表数据聚合查询如何处理? + +首先我们要知道,是前期就做好分库分表方案,还是后期在做系统重构分库分表和数据迁移哪个成本高。显然是后面的成本更高。 + +而互联网中大厂中,分库分表的架构设计都是非常熟练的,因为有成熟方案,所以前期就分库分表了。但,为了解释服务器空间。所以把分库分表的库,用服务器虚拟出来机器安装。这样即不过多的占用服务器资源,也方便后续数据量真的上来了,好拆分。 + +那么这里的分库分表后的数据怎么提供汇总、聚合的查询呢? + +这里需要用到阿里的 canal 组件,基于 mysql 的 binlog 日志,把自己伪装为一个从数据库,通过 dump 交换完成数据的接收和处理。最终把数据同步到 Elasticsearch 等文件服务中在提供聚合查询。对于需要实时的查询以及数据的处理,还可以用到 Flink 方式进行流式计算。 + +### 2. 抽奖奖品库存如何处理,怎么保证最终一致性? + +在抽奖秒杀这样的场景下,都需要把库存缓存到 Redis 中进行使用。而不能数据库表加行级锁,否则大量的秒杀进行通过加锁和等待释放,就会夯住数据库链接直至拖垮整个服务。 + +那么使用缓存通过大营销项目中的颗粒度更低的分段锁后,怎么来保证一致性呢。这里需要3个步骤,首先是每次扣减完库存,都会写入到 Redis 延迟队列 / MQ 延迟消息,缓慢更新数据库库存。之后是 Redis 内的预热库存消耗完毕后,发送最终 MQ 消息,更新数据库的剩余库存为 0,最终活动结束后,还有任务补偿,扫描抽奖所产生的的参与记录单,更新最终的库存消耗。这里就可以用订单 MQ 通过 Flink 计算,更新最终库存也是可以的。 + +### 3. 写入中奖记录,发送MQ消息失败如何处理? + +本身发送MQ是可能存在万分之一或者十万分之的失败的,而数据库操作和MQ操作,本身不能做数据库事务。但又要保证失败后的补偿处理。所以要结合中奖记录在写一条发送MQ的任务记录,任务记录上有一个状态,标记是否发送完成,这样就可以通过任务扫描的方式完成 MQ 的补偿发送。 + +但这里要知道,做完本身的中奖记录和任务记录后写库事务后,要顺序的可以是多线程的方式,完成一次MQ发送,并且更新数据库 Task 记录。这里是为了业务流程最快的推进,如果是更新失败也没关系,还有兜底的任务补偿。【任务补偿的数量并不多,但非常需要这个手段】 + +### 4. 生产者可能多次发送同一个MQ,怎么保证奖品不会超发? + +这是一个幂等的设计处理,MQ 的消息是必须含带具有唯一标识的业务ID的。比如订单ID、奖品ID、支付单ID、交易单ID、贷款单ID等等。接收MQ的系统,通过唯一ID业务,更新或者写库的时候可以保证幂等性。这样也就不会产生超发的可能。 + +### 5. 抽奖算法如何提供O(1)时间复杂度,提高抽奖效率? + +在大营销系统中,运营人员配置好抽奖活动后,开始上线对外后,会进行数据的预热数据。这个预热的过程会把活动信息、策略信息、库存信息都存储到 Redis 里进行使用。 + +而抽奖的策略就是记录了一个策略下N个奖品的概率,将概率转换为对应的整数数量,写入到缓存中。那么在抽奖的时候就按照整数数量生成随机数来抽奖。这样用空间换时间的效率是非常高的。 + +### 6. 如何对所有分布式节点的应用,活动信息本地内存更新? + +通常我们会有诉求在不重启系统的时候,就要动态变更所有分布式应用节点中某个属性的值,如开关、缓存、调试日志开启/关闭、熔断、限流、或者抽奖黑名单以及概率等。这些东西通常不是 Redis 存储,而是应用中具体字段的属性值,这样效率更高。 + +而这个操作需要使用到类似于 Zookeeper 组件的临时节点监听,动态变更字段值。详细:[https://bugstack.cn/md/road-map/zookeeper.html](https://bugstack.cn/md/road-map/zookeeper.html) + +### 7. 应用刚启动完成,外部调用过程中发现操作数据库连接池不足,超时断开,过一会又好了?是什么问题? + +因为它是刚开始有问题,过一会又好了,所以很有可能是池化的连接数配置的最小值与最大值不是一个,这样应用就会先初始一个最小范围的连接数,随着调用没了在初始化到最大连接数。所以一般我们会把最小连接数和最大连接数配置为一个,避免使用的时候还需要初始化。因为初始化连接也是需要花费时间的。【再有注意配置链接的超时时间,不要太小,也不要太大】 + +### 8. 消费MQ的过程中,如果使用多线程会遇到什么问题? + +这是一个非常容易产生事故的问题,本身 MQ 消费就是多个应用分别消费,如果有消费失败的,可以抛异常重试。但如果是一个消费 MQ 的应用,里面写了多线程,就可能会出现大量的 MQ 挤压,消费不过来,导致系统瘫痪。而如果你重启,那么这些拉下来的 MQ 消息也就随时丢失了。 + +### 9. 分库分表怎么让任务扫描到指定的库表? + +分库分表以后,需要扫描每个库表中的任务表,则需要手动设定具体要扫描的库和表。如果分库分表的数量比较多,可以用不同的任务配置扫描不同的库表方式来部署,这样可以提高扫描效率。 + +如果说扫描出来的数据需要更低的延迟性,可以考虑做[低延迟任务调度设计](https://bugstack.cn/md/road-map/zookeeper.html)。 + +> 综上都是微服务&分布式架构设计中会出现的场景问题,这些东西是需要实际编码学习才能更好的理解的。更多的问题汇总:[https://bugstack.cn/md/project/big-market/notes.html](https://bugstack.cn/md/project/big-market/notes.html) + +## 三、架构演示 + +小傅哥对于这样一套微服务分布式架构做了非常多的系统方案细节设计,举例如下方便大家参考。—— 这些方案都有平均40-50分钟的视频,由浅入深,循序渐进的讲解。 + +### 1. 活动参与流程 + +
+ +
+ +### 2. 分库分表原理 + +
+ +
+ +### 3. 领取活动流程 + +
+ +
+ +>这套系统是从0到1,手把手的带着设计和编码。地址:[https://bugstack.cn/md/project/big-market/big-market.html](https://bugstack.cn/md/project/big-market/big-market.html) + +## 三、加入学习 + +《大营销平台系统》是一套微服务、分布式技术栈架构,DDD 设计的系统。全程视频手把手的教大家学习,就连 IntelliJ IDEA 快捷键也教你使用。每节视频平均在`40分钟`,课程已经录制完成26节【这一部分学习完,就够写简历面试了】 + +> 小傅哥的星球「码农会锁」有8个实战项目【大营销、OpenAI 应用、API网关、中间件等】,每个都是从0到1开发并提供简历模板和面试题,并且还在继续开发,后续还将有更多!价格嘎嘎实惠,早点加入,早点提升自己。项目地址:https://gaga.plus + +
+ +
+**课程采用全程视频手把手,遇到学习问题手把手给调试,学习起来嘎嘎舒服!** + +
+ +
+ +
+ +
\ No newline at end of file diff --git a/docs/md/project/big-market/notes.md b/docs/md/project/big-market/notes.md index e02f35a9..c4a6638e 100644 --- a/docs/md/project/big-market/notes.md +++ b/docs/md/project/big-market/notes.md @@ -123,6 +123,60 @@ DDD 中的依赖倒置是一个非常好的设计,尤其是与 MVC 结构对 如;可以对大营销抽奖模型流程的设计和库表设计,最为耗时,因为你不断的在思考如何拆解出一个好扩展的松耦合结构,同时拆解后,还要保证搜耦合下的高内聚。所以这部分是比较耗时的。同时也可以说在设定某个方法的,名称、入参、出参时,做了大量的思考。因为名字的定义非常影响以后的理解。好的代码就是文档,所以对于这样的东西花费不少时间。 +### 17. 为什么使用分库分表,做了分库分表数据聚合查询如何处理? + +首先我们要知道,是前期就做好分库分表方案,还是后期在做系统重构分库分表和数据迁移哪个成本高。显然是后面的成本更高。 + +而互联网中大厂中,分库分表的架构设计都是非常熟练的,因为有成熟方案,所以前期就分库分表了。但,为了解释服务器空间。所以把分库分表的库,用服务器虚拟出来机器安装。这样即不过多的占用服务器资源,也方便后续数据量真的上来了,好拆分。 + +那么这里的分库分表后的数据怎么提供汇总、聚合的查询呢? + +这里需要用到阿里的 canal 组件,基于 mysql 的 binlog 日志,把自己伪装为一个从数据库,通过 dump 交换完成数据的接收和处理。最终把数据同步到 Elasticsearch 等文件服务中在提供聚合查询。对于需要实时的查询以及数据的处理,还可以用到 Flink 方式进行流式计算。 + +### 18. 抽奖奖品库存如何处理,怎么保证最终一致性? + +在抽奖秒杀这样的场景下,都需要把库存缓存到 Redis 中进行使用。而不能数据库表加行级锁,否则大量的秒杀进行通过加锁和等待释放,就会夯住数据库链接直至拖垮整个服务。 + +那么使用缓存通过大营销项目中的颗粒度更低的分段锁后,怎么来保证一致性呢。这里需要3个步骤,首先是每次扣减完库存,都会写入到 Redis 延迟队列 / MQ 延迟消息,缓慢更新数据库库存。之后是 Redis 内的预热库存消耗完毕后,发送最终 MQ 消息,更新数据库的剩余库存为 0,最终活动结束后,还有任务补偿,扫描抽奖所产生的的参与记录单,更新最终的库存消耗。这里就可以用订单 MQ 通过 Flink 计算,更新最终库存也是可以的。 + +### 18. 写入中奖记录,发送MQ消息失败如何处理? + +本身发送MQ是可能存在万分之一或者十万分之的失败的,而数据库操作和MQ操作,本身不能做数据库事务。但又要保证失败后的补偿处理。所以要结合中奖记录在写一条发送MQ的任务记录,任务记录上有一个状态,标记是否发送完成,这样就可以通过任务扫描的方式完成 MQ 的补偿发送。 + +但这里要知道,做完本身的中奖记录和任务记录后写库事务后,要顺序的可以是多线程的方式,完成一次MQ发送,并且更新数据库 Task 记录。这里是为了业务流程最快的推进,如果是更新失败也没关系,还有兜底的任务补偿。【任务补偿的数量并不多,但非常需要这个手段】 + +### 19. 生产者可能多次发送同一个MQ,怎么保证奖品不会超发? + +这是一个幂等的设计处理,MQ 的消息是必须含带具有唯一标识的业务ID的。比如订单ID、奖品ID、支付单ID、交易单ID、贷款单ID等等。接收MQ的系统,通过唯一ID业务,更新或者写库的时候可以保证幂等性。这样也就不会产生超发的可能。 + +### 20. 抽奖算法如何提供O(1)时间复杂度,提高抽奖效率? + +在大营销系统中,运营人员配置好抽奖活动后,开始上线对外后,会进行数据的预热数据。这个预热的过程会把活动信息、策略信息、库存信息都存储到 Redis 里进行使用。 + +而抽奖的策略就是记录了一个策略下N个奖品的概率,将概率转换为对应的整数数量,写入到缓存中。那么在抽奖的时候就按照整数数量生成随机数来抽奖。这样用空间换时间的效率是非常高的。 + +### 21. 如何对所有分布式节点的应用,活动信息本地内存更新? + +通常我们会有诉求在不重启系统的时候,就要动态变更所有分布式应用节点中某个属性的值,如开关、缓存、调试日志开启/关闭、熔断、限流、或者抽奖黑名单以及概率等。这些东西通常不是 Redis 存储,而是应用中具体字段的属性值,这样效率更高。 + +而这个操作需要使用到类似于 Zookeeper 组件的临时节点监听,动态变更字段值。详细:[https://bugstack.cn/md/road-map/zookeeper.html](https://bugstack.cn/md/road-map/zookeeper.html) + +### 22. 应用刚启动完成,外部调用过程中发现操作数据库连接池不足,超时断开,过一会又好了?是什么问题? + +因为它是刚开始有问题,过一会又好了,所以很有可能是池化的连接数配置的最小值与最大值不是一个,这样应用就会先初始一个最小范围的连接数,随着调用没了在初始化到最大连接数。所以一般我们会把最小连接数和最大连接数配置为一个,避免使用的时候还需要初始化。因为初始化连接也是需要花费时间的。【再有注意配置链接的超时时间,不要太小,也不要太大】 + +### 23. 消费MQ的过程中,如果使用多线程会遇到什么问题? + +这是一个非常容易产生事故的问题,本身 MQ 消费就是多个应用分别消费,如果有消费失败的,可以抛异常重试。但如果是一个消费 MQ 的应用,里面写了多线程,就可能会出现大量的 MQ 挤压,消费不过来,导致系统瘫痪。而如果你重启,那么这些拉下来的 MQ 消息也就随时丢失了。 + +### 24. 分库分表怎么让任务扫描到指定的库表? + +分库分表以后,需要扫描每个库表中的任务表,则需要手动设定具体要扫描的库和表。如果分库分表的数量比较多,可以用不同的任务配置扫描不同的库表方式来部署,这样可以提高扫描效率。 + +如果说扫描出来的数据需要更低的延迟性,可以考虑做[低延迟任务调度设计](https://bugstack.cn/md/road-map/zookeeper.html)。 + +> 综上都是微服务&分布式架构设计中会出现的场景问题,这些东西是需要实际编码学习才能更好的理解的。更多的问题汇总:[https://bugstack.cn/md/project/big-market/notes.html](https://bugstack.cn/md/project/big-market/notes.html) + ## 四、公司面试 ### 【美团】1. 领域模型是怎么设计的,抽奖过程是什么样,DDD四层架构和职责,以及为什么要这么设计?少卖和超卖。