/mama-buy

:dog::dog:spring cloud分布式电商实战代码,并且对以下内容进行了详细笔记记录:maven聚合工程创建,消息总线,分库分表,全局异常处理,分布式session管理,ZK分布式锁,分布式ID生成,SKU概念和表设计,全文检索,缓存,下单扣减库存引起的超卖问题解决,幂等性问题,定时任务,redis事件通知,分布式事务解决方案等。

Primary LanguageJava

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存在超卖的问题,后续一系列的优化:

  • 乐观锁(多线程竞争性能低);
  • 悲观锁(性能低,直接锁行,需要排队);
  • rediswatch实现的乐观锁,不过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校验,实现与单车项目的实现类似。这两个都没有在代码中实现。

具体还是看笔记。