2020年04月12日更新:
承蒙各位的厚爱,得到了130+的star,我倍感惊喜~不过这个项目我是存在很大的遗憾的,一直想重构下代码并且留下完整的笔记,也让一些联系我的朋友有点难以下手。但是我又担心没有此能力,会对大家造成误导。现在呢,我新开辟了一个新项目,仓库地址为:https://github.com/sunweiguo/xiaoxiaoxudeshop
这个项目我跟着大牛老师做一做,并且我会根据自己的理解进行代码的重构(我觉得有很多值得优化的点,还有就是支付,我还是想用原生的接入方式而不是课程中说的接入它的支付中心,所以我打算还是用支付宝扫码支付作为支付手段,以及其他的一些点,比如图片上传啥的),最终我会像模像样地部署到云服务器上。
最最重要的一点是:单体架构从0到1的过程,我会完整记录笔记,一步一个截图。目前已经有了四个笔记,还在努力更新中~
我写在这里的原因,一方面是希望帮助到一些朋友,另一方面也是勉励自己,毕竟不为收入,只为做些对自己、对他人有意义的事情。所以还是希望看到的朋友多多star,多多留下宝贵意见。其他就不多说了。。。
mamabuy笔记的代码仓库。
本代码仓库是关于本实践只有后端代码,另一个项目:https://github.com/sunweiguo/spring-cloud-for-snailmall
借鉴了这里很多的思想,并且拥有完整的前后端代码。体验地址为:www.oursnail.cn (2019/1/25说明 暂时不可用)
另外,我会花很多心思在我的博客上: https://sunweiguo.github.io
只是对电商中的核心点做了一些实现。涉及以下的知识点:
1️⃣ maven聚合工程创建
maven
聚合工程的创建,创建eureka
注册中心服务端(mama-buy-server-registry
),还可能涉及其他的组件,比如fiegn
接口调用、zuul
网关实现(未实现)、stream
消息驱动、sleuth
链路追踪(这里也没去追踪)、hystrix
服务降级和熔断(没有去做)、spring security
实现用户信息校验
2️⃣消息总线
spring-cloud-config
+zookeeper
+kafka
+actuator
做成一个消息总线,自动从git上拉取配置并刷新,mama-buy-config-server
3️⃣ 分库分表
shardingJDBC
分库分表中间件的使用
4️⃣ 全局异常处理
对异常进行统一的处理,解决潜在异常以及自己不停捕获异常导致代码的不优雅。
5️⃣ 分布式session管理
spring session
+redis
实现分布式session
管理,并且探讨了实现原理
6️⃣ ZK分布式锁
由用户注册问题,延申出如何防止用户名注册重复问题,这里由于是分布式+分表的情况,所以select...for update
以及唯一索引都不再适用本场景,这里使用curator
客户端实现的ZK分布式锁。并且探讨了ZK分布式锁的基本原理
7️⃣ 分布式ID生成
对于分布式ID生成问题,核心点为:唯一+有序,前者要求是必须全局唯一的,后者是基于mysql的存储引擎如何提高查询效率而提出的要求。本文采用snowflake
算法来实现。mama-buy-keygen-service
8️⃣ SKU概念和表设计
电商中有一个重要概念叫做SKU
,即最小库存单位,探讨一下最简单的商品详情页是如何设计sku
的。
9️⃣ 全文检索
关于商品信息的全文搜索(搜索框或者点击分类显示这个分类下的所有商品),数据库的模拟查询限制太多:速度慢、匹配问题、全表扫描等问题,这里搭建ELK平台(Elasticsearch、Logstash和Kibana),对商品表的内容进行建立索引,再加上分词器ik_max_word
,对匹配结果进行排序,展示出来。这里详细说明了ELK平台的搭建过程和测试。最后用Jest
,客户端编写java代码,在代码层面实现全文搜索功能。
1️⃣ 0️⃣ 缓存
spring cache
的基本使用,对于简单的缓存场景可以用这个。
1️⃣1️⃣下单扣减库存引起的超卖问题解决
针对用户下单扣减库存问题,详细探讨了普通update存在超卖的问题,后续一系列的优化:
- 乐观锁(多线程竞争性能低);
- 悲观锁(性能低,直接锁行,需要排队);
redis
的watch
实现的乐观锁,不过watch
不能再集群环境下使用,限制较多;- 用
incrby
原子递增或者递减操作,对于一个变量可以这样实现,但是库存一般都要维护两个变量:库存+锁定库存,前者表示还剩多少库存没有被下单,也没有被支付,是真正可以利用的库存;后者表示被下单的库存,但是还没有支付,如果订单超时,是需要将其收回的。所以一个原子操作是不够的,我们需要一个办法让这两个变量的变化放在一个事务里面。 - 引出了本文的实现方案:
redis
+lua
脚本。为什么要用lua呢?可以用lua将一系列操作封装起来执行,输入自己的参数即可。lua
脚本在redis
中执行是串行的、原子性的。
1️⃣2️⃣幂等性问题
幂等性问题:用户下单除了由于多线程竞争引发超卖问题之外,还有一个问题是幂等性问题,因为下订单往往是通过MQ去异步下订单,如果MQ出现问题,于是进行了重发机制,最后可能导致两次或两次以上去下订单成功,那么一个用户明明就买了一个商品,结果扣掉了两件商品,是不能忍受的。如何解决呢?这里是采用redis
分布式锁来锁订单号一段时间来解决的。让他在一段时间内锁住,防止由于MQ重发机制导致重复进来扣减库存。由于锁的是每一个订单号,所以性能不会受到影响,一段时间后让锁自动过期失效。
1️⃣ 3️⃣ 定时任务
定时任务,这里采用分布式定时任务Elastic Job
来实现,这里详细介绍了如何整合,此外,定时任务用处非常多,比如这里用定时任务是扫描哪些订单是超时的,可以去扫描出来去关闭。还有就是,下订单一般是先下到订单流水表中,后面慢慢通过定时任务同步到订单表中,这样可以记录一些中间状态的详细信息,出现问题也可以从这个表中找出一些原因。
1️⃣4️⃣redis事件通知
下完订单后可以利用redis
过期事件回调通知来提高订单超时解决的效率,提早将库存归位,定时扫描再检查。
1️⃣ 5️⃣ 分布式事务解决方案
复杂的业务场景往往会设计跨库操作,比如多个数据库,比如redis
和数据库等等,如何保证数据的一致性呢?介绍了两种可行性方案:基于可靠消息服务的分布式事务(目前只有RocketMQ
消息队列可以实现)和最大努力通知。我们追求的是数据的最终一致性,所以可以容许中间状态的存在。
1️⃣ 6️⃣ 网关设计和安全校验
这一块,zuul
用来实现网关,作为系统对外的唯一入口;spring security
进行token
校验,实现与单车项目的实现类似。这两个都没有在代码中实现。
具体还是看笔记。