GithubHelp home page GithubHelp logo

sunweiguo / mmall Goto Github PK

View Code? Open in Web Editor NEW
398.0 21.0 158.0 78.2 MB

:loudspeaker:快乐蜗牛商城springcloud版本。本项目是对慕课网电商项目进行后端架构升级,主要是采用springcloud技术栈来进行服务拆分和治理,提高系统的并发能力。含有完整的前端页面,从用户选择商品-加入购物车-生成订单-支付宝扫码支付-后台管理系统。大体实现所有功能。除了springcloud技术栈,还涉及分布式锁、MQ异步下单、redis预减库存等内容

Java 92.32% PLpgSQL 7.68%
springcloud mall happymmall mmall muke geely rosen

mmall's Introduction

2020年04月12日更新:

承蒙各位的厚爱,得到了250+的star,我倍感惊喜~不过这个项目我是存在很大的遗憾的,一直想重构下代码并且留下完整的笔记,也让一些联系我的朋友有点难以下手。但是我又担心没有此能力,会对大家造成误导。现在呢,我新开辟了一个新项目,仓库地址为:https://github.com/sunweiguo/xiaoxiaoxudeshop

这个项目我跟着大牛老师做一做,并且我会根据自己的理解进行代码的重构(我觉得有很多值得优化的点,还有就是支付,我还是想用原生的接入方式而不是课程中说的接入它的支付中心,所以我打算还是用支付宝扫码支付作为支付手段,以及其他的一些点,比如图片上传啥的),最终我会像模像样地部署到云服务器上。

最最重要的一点是:单体架构从0到1的过程,我会完整记录笔记,一步一个截图。目前已经有了四个笔记,还在努力更新中~

我写在这里的原因,一方面是希望帮助到一些朋友,另一方面也是勉励自己,毕竟不为收入,只为做些对自己、对他人有意义的事情。所以还是希望看到的朋友多多star,多多留下宝贵意见。其他就不多说了。。。


2018年的最后一天,对商城项目的架构做个改造

前方图片高能...更有几十兆gif演示动画,图片全部存放于七牛云上。

2019/5/8 演示网站不可用。关于这个电商后端的功能实现(第一期项目),我做过笔记,可以看看:http://note.youdao.com/noteshare?id=35c4469a6d7257d8233eb1441e2edd49

image

一直以来都是学习慕课的实战视频,虽然也跟着做出了一些东西,但是思路都是别人提供好的,脱离了老师,我一直在问自己一个问题:能不能独立地按照自己的思路做出一些东西来?

带着这个想法,我想,做一个开源项目对于我来说还是很遥远,但是,我想从改造现有项目开始,逐步开启个人独立开发的序幕。

在去年,即2017年年底,我在慕课上学习了这两门课程:

https://coding.imooc.com/class/96.html

https://coding.imooc.com/class/96.html

第一期项目实现了比较简单的电商业务,整合SSM,并且部署到云端。

第二期实现了tomcat集群,配合redis实现分布式session,还有一些定时任务、redis分布式锁、maven环境隔离的一些东西,还涉及很多spring和springmvc的有用的机巧。

整体感觉是:一期实现业务,二期对于一期的提高不是太大,跟分布式无太大关系,仅仅实现了单点登陆和分布式session存储而已。

个人感觉下一期的课程应该是springCloud的分布式改造,进行服务拆分和治理。所以,在整合这两个课程的基础上用springCloud进行微服务治理。

项目详细描述

整体效果演示:

image

上面个gif有68兆,翻墙的话(我用的是免费的谷歌插件:SetupVPN,偶尔用用还是不错的),可以很快加载出来,但是不翻墙基本上没这个耐心等了。下面贴个小一点的gif:

image

部分页面截图:

image

image

image

image

image

image

后台管理系统为:http://admin.oursnail.cn 用户名为 admin 密码为 1234567 请不要乱改数据哦!

image

image

image

image

image

经过一遍遍测试,商城还是存在一些无伤大雅的bug,但是主要还是锻炼自己的能力嘛!

下订单的时候,报错:商品不存在或库存不足,是因为我模拟的秒杀,所以商品的库存要提前预置于redis中,后端管理系统的商品管理页面有预置库存的按钮。

新增商品的时候,对于上传图片,需要耐心等待一会,需要等待FTP服务器上传完毕,给一个返回信息(Map,是图片的文件名)才能真正显示(对于富文本中的图片上传,在上传之后需要等待一会,时间与小图上传差不多,否则直接保存不报错,但是前端看不到图,因为还没上传完毕,url还没回传回来)

普通注册的账号,没有管理员权限,所以不能登陆后台管理系统。

后来做了eureka集群,但是配置文件还是只指向其中一个eureka

在学习的视频中,一期只是实现业务功能,单体架构,一个tomcat。二期对其做了集群,并且解决了集群模式下session存储问题,实现了比较简单的单点登陆功能。架构如下:

image

对于上面的架构来说,只是做了一些集群进行优化,随着业务的发展,用户越来越多,用户服务等其他服务必然要拆分出来独立成为一个服务,这样做的好处是,一方面一个团队负责一个服务可以提高开发效率,另外,对于扩展性也是非常有利的,但是也是有缺点的,会带来很多的复杂性,尤其是引入了分布式事务,所以不能为了分布式而分布式,而是针对不同的业务场景而采用合适的架构。

微服务的实现,主要有两种,国内是阿里系的以dubbo+zookeeper为核心的一套服务治理和发现生态。另一个则是大名鼎鼎的spring cloud栈。

spring cloud并不是像spring是一个框架,他是解决微服务的一种方案,是由各种优秀开源组件共同配合而实现的微服务治理架构。下面的图是我构思的项目结构图:

image

最前面是Nginx,这里就作为一个静态资源映射和负载均衡,nginx中有几个配置文件,分别为www.oursnail.cn.conf,这个主要是对zuul网关地址做一个负载均衡,指向网关所在的服务器,并且找到前台页面所在位置对页面进行渲染。admin.oursnail.cn.conf,这个主要是配置后端以及后端的页面文件;img.oursnail.cn.conf是对图片服务器地址进行映射。

然后是zuul网关,这里主要是用来限流、鉴权以及路由转发。

再后面就是我们的应用服务器啦。对服务器进行了服务追踪(sleuth),实现了动态刷新配置(spring cloud config+bus)等功能。以http restful的方式进行通信(openFeign),构建起以eureka为注册中心的分布式架构。

每个服务都是基于springboot打造,结合mybatis持久层操作的框架,完成基本的业务需求。springboot基于spring,特点是快速启动、内置tomcat以及无xml配置。将很多东西封装起来,引入pom就可以直接使用,比如springMVC就基本上引入starter-web即可。

由于资源的原因,只有三台最低配的服务器,所以本来想做的基于ES的全文检索服务没有做,也没有分库分表。

至于定时任务以及Hystrix服务熔断和降级,比较简单,就不做了。

下面详细介绍每个模块实现的大体思路(仅供参考,毕竟应届生,真实项目没做过):

  • 用户模块

关于用户模块,核心的功能是登陆。再核心是如何验证以及如何存储用户信息。这里采取的方案为:

image

对于用户注册,我这里就是用户名(昵称),那么如何保证不重复呢(高并发)?这里还是用了分布式锁来保证的。

对于未登陆章台下用户修改密码,逻辑为:

image

  • 购物车模块

image

  • 订单模块

image

针对这些问题,我想说一下我的思路。

对于幂等性,这里产生幂等性的主要原因在于MQ的重传机制,可能第一个消息久久没有发出去,然后重新发送一条,结果第一条消息突然又好了,那么就会重复发两跳,对于用户来说,只下一次单,但是服务器下了多次订单。网上解决这个问题的思路是创建一张表,如果是重复的订单号,就不可能创建多次了。还有一种可能方案是用分布式锁对该订单号锁住一段时间,由于只是锁住订单号,所以不影响性能,在这一段时间内是不可以再放同一个订单号的请求进来。

对于MQ消息不丢失,只能是订阅模式了。消息发出去之后,消费端给MQ回复一个接收到的信息,MQ本次消费成功,给订阅者一个回复。

对于全局唯一ID生成,这里用的是雪花算法,具体介绍可以看我的笔记

对于分布式事务,比较复杂,这里其实并没有真正处理,对于数据库扣减库存和数据库插入订单,他们在不同的数据库,廖师兄比较倾向的方式是:

image

image

这一切的基础还是需要有一个可靠的消息服务,确保消息要能送达。

针对redis预减库存存在的并发问题,这里的思路是用lua+redis,在预减之前判断库存是否够,这两个操作要在一个原子操作里面才行,lua恰好可以实现原子性、顺序性地操作。

  • 支付模块

这里对接的是支付宝-扫码支付,用到是支付宝沙箱环境。支付的扫码支付详细流程在这里聊一聊哈。

商户前台将商品参数发送至商户后台,商户后台生成内部订单号并用于请求支付平台创建预下单,支付平台创建完预订单后将订单二维码信息返还给商户,此时用户即可扫取二维码进行付款操作。

内部订单号:这是相对于支付宝平台而言的,这个订单号是我们商城自己生成的,对于我们商城来说是内部订单号,但是对于支付宝来说是外部订单号。

将一系列的数据按照支付宝的要求发送给支付宝平台,包括商品信息,生成的验签sign,公钥;支付宝去将sign解密,进行商品的各种信息校验。校验通过,同步返回二维码串。

支付业务流程图:

image

在获取支付的二维码串之后,用工具包将其转换未二维码展示给用户扫码。

用户扫码后,会收到第一次支付宝的回调,展示要支付的金额,商品信息等。

用户输入密码成功后,正常情况会收到支付宝的第二次回调,即支付成功信息。

但是也可能会由于网络等原因,迟迟收不到支付宝的回调,这个时候就需要主动发起轮询去查看支付状态。

在支付成功之后,接收回调的接口要记得返回success告诉支付宝我已经收到你的回调了,不要再重复发给我了。接收回调的接口也要做好去除重复消息的逻辑。

image

这个流程是多么地简单而理所当然!

对应于代码层面,其实就是两个接口,一个是用户点击去支付按钮,此时发起预下单,展示付款二维码,另一个是接收支付宝回调:

预下单:

image image image

接收支付宝支付状态回调:

image

项目进展

  • 2018/12/31 完成了聚合工程的创建、Eureka服务注册中心、spring cloud config+gitHub+spring cloud bus(rabbitMQ)实现配置自动刷新--v1.0
  • 2018/12/31 将Eureka注册中心(单机)和配置中心部署到服务器上,这比较固定,所以先部署上去,以后本地就直接用这两个即可,对配置进行了一点点修改
  • 2018/12/31 关于配置的自动刷新,用postman发送post请求是可以的,但是用github webhook不行,不知道是不是这个版本的问题
  • 2018/12/31 用户模块的逻辑实现,首先增加了一些pom文件的支持,整合mybatis,测试数据库都通过,下面就可以真正去实现业务代码了
  • 2019/1/1 完成用户注册、登陆、校验用户名邮箱有效性、查看登陆用户信息、根据用户名去拿到对应的问题、校验答案是否正确、重置密码这个几个接口,在注册这个接口,增加一个ZK分布式锁来防止在高并发场景下出现用户名或邮箱重复问题
  • 2019/1/2 上午完成门户用户模块所有接口--v2.0
  • 2019/1/2 下午完成品类管理模块,关于繁琐的获取用户并且鉴权工作,这里先放每个接口里面处理,后面放到网关中去实现--v3.0
  • 2019/1/3 上午引入网关服务,将后台重复的权限校验统一放到网关中去做,并且加了限流,解决了一下跨域问题。--v4.0
  • 2019/1/3 下午和晚上完成门户和后台的商品管理模块所有的接口功能,除了上传文件的两个接口没有测试以外,其他接口都进行了简单的测试,其中还用Feign去调用了品类服务接口--v5.0
  • 2019/1/3 初步把购物车模块和模块引入,通过基础测试,后面在此基础上直接开发代码即可,明天下午看《大黄蜂》,晚上师门聚餐吃火锅,明天早上赶一赶吧,今天任务结束!
  • 2019/1/4 整理了接口文档,并且画了一下购物车模块的流程图以及订单服务的流程图,针对订单服务中,记录了需要一些注意的问题,尽可能地完善,提高可用性和性能。并且完成购物车模块的controller层。
  • 2019/1/5 完成购物车模块,并且进行了简单的测试,这里进行了两处改造,一个是判断了一下是否需要判断库存;另一个是商品信息从redis中取,取不出来则调用商品服务初始化值
  • 2019/1/5 收货地址管理模块,这个模块就是个增删改查,没啥东西写,这里就不加缓存了。
  • 2019/1/6 完成了后台订单管理模块并且进行了测试,调用收货地址服务时,发现收货地址服务无法读取到cookie,通过这个方法(https://blog.csdn.net/WYA1993/article/details/84304243) 暂时解决了问题
  • 2019/1/6 预置所有商品库存到redis中;预置所有商品到redis中;大概确定好订单服务思路:预减库存(redis判断库存)---对userID增加分布式锁防止用户重复提交订单--MQ异步下订单
  • 2019/1/6 新增全局唯一ID生成服务,雪花算法实现
  • 2019/1/7 完善订单服务-这一块涉及跨库操作,并且不停地调用其他服务,脑子都快晕了,这里采取的策略是:用到购物车的时候,去调用购物车服务获取;产品详情从redis中获取。首先将商品以及商品库存全部缓存到redis中,然后用户下单,先从redis中判断库存,够则减,判断 和扣减放在lua脚本中原子执行,然后MQ异步出去生成订单(生成订单主表和订单详情表放在一个本地事务中),这两步操作成功之后,再用MQ去异步删除购物车。MQ消费不成功则重试。 对于扣减库存这一步,想法是用定时任务,定时与redis中进行同步。这里是模拟了秒杀场景,预减库存+MQ异步,提交订单-->redis判断并且减库存-->调用cart-service获取购物车-->MQ异步(userId,shippingId)生成订单主表和详情表-->上面都成功,则MQ异步(userId) 去清除购物车,库存用定时任务去同步(未做),理想的做法是:MQ异步扣减库存,订单服务订阅扣减库存消息,一旦库存扣减成功,则进行订单生成。
  • 2019/1/8 继续完善订单接口,完成支付服务,就直接放在订单服务里面了,因为与订单逻辑紧密,就放在一起了。
  • 2019/1/8 使用了一下swagger,发现代码侵入比较强,每一个接口上面都要手动打上响应的注解
  • 2019/1/8 关于hystrix熔断与降级,可以引入hystrix的依赖,用@HystrixCommand注解来控制超时时间、服务降级以及服务熔断。也可以直接再@FeignClient接口中指定服务降级的类,这里不演示了,因为设置比如超时时间,我还要重新测试,写起来很简单,测起来有点儿麻烦
  • 2019/1/9 服务跟踪,服务端是直接用的线程的,只需要下载:wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec',然后nohup java -jar zipkin.jar > zipkin.server.out & ,开放9411端口,打开浏览器http://ip:9411看到页面即可。 客户端只需要添加相应依赖和配置文件即可。用客户端测试,发现死活不出现我的请求,经过搜索,发现需要增加spring.zipkin.sender.type= web这个配置项才行.
  • 2019/1/10 初步把项目部署到服务器上,进行测试,bug多多,修改中...
  • 2019/1/10 改了一天的bug,其中网关的超时时间以及feign的超时时间都要改大一点,否则会超时报错。最终成功,花了三台服务器,部署了11个服务。后面把部署过程写一下。
  • 2019/1/11 将注册中心做成集群,因为早上一起来,注册中心挂了???
  • 2019/1/11 docker部署(商城第四期的改造目标:容器化+容器编排),本期改造结束。
  • 2019/1/11 完善readme文档
  • 2019/1/12 两次发现redis数据被莫名其妙清空,我确定不是缓存到期,为了安全起见,设置了redis的密码,明天看缓存数据还在不在。
  • 2019/1/14 redis数据没有再丢失,修复用户更新信息的bug

项目启动

安装redis、zookeeper、mysql、jdk、nginx以及rabbitMQ。

对代码进行maven-package操作。打包成jar包。将其放到服务器上:

image

执行nohup java -jar snailmall-user-service.8081 > user-service.out &后台启动即可。

补充:针对配置刷新,修改了github信息,用postman请求http://xxxxx:8079/actuator/bus-refresh 触发更新。

本改造是基于快乐慕商城一期快乐慕商城二期的基础上进行改造。所以需要在其业务基础上改造会比较顺手。关于微服务,尤其是电商中的一些处理手段,很多思路都是学习于码吗在线中分布式电商项目。再加上慕课网廖师兄的spring cloud微服务实践

前台项目

只要阅读readme文档即可。代码仓库为:https://github.com/sunweiguo/snailmall-front

学习不仅要有输入,更要有自己的输出,实践是提升的捷径!

mmall's People

Contributors

sunweiguo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mmall's Issues

启动不了

Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'servletEndpointRegistrar' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.endpoint.web.ServletEndpointRegistrar]: Factory method 'servletEndpointRegistrar' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'healthEndpoint' defined in class path resource [org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthEndpoint]: Factory method 'healthEndpoint' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorAutoConfiguration': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorAutoConfiguration$$EnhancerBySpringCGLIB$$a204d749]: Constructor threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource': Post-processing of FactoryBean's singleton object failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class

service 层的操作

你好,在对数据库层面做增删改操作的时候你不加事务的吗?(表示疑惑)

[security vulnerability] Arbitrary Order Receiving Address Access Vulnerability

Recently, our team found an arbitrary order receiving address access vulnerability in the latest version of the project.

The vulnerability logic is present in the file: https://github.com/sunweiguo/MMall/blob/master/snailmall-shipping-service/src/main/java/com/njupt/swg/service/ShippingServiceImpl.java#L85

image

The developer failed to check the ownership of the shippingId with the access user when querying the order details via [shippingMapper.selectByPrimaryKey](https://github.com/sunweiguo/MMall/issues/shippingMapper.selectByPrimaryKey)(), leading to any order can be traversed by [getShipping.do](https://github.com/sunweiguo/MMall/issues/getShipping.do)?shippingId={x} to get details (i.e., https://github.com/sunweiguo/MMall/blob/master/snailmall-shipping-service/src/main/java/com/njupt/swg/controller/ShippingController.java#L90), leaking user's receiving address

image

We recommend that developers add the access control policy before querying via shippingId to ensure that the accessor is the owner of the order.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.