我们目前做的项目是一个社区团购的电商系统,主要就是实现一个线上线下的结合。在我们的项目中涉及到秒杀、团购、支付、商家的认证等功能。我所负责的模块是秒杀模块,其中的一个核心就是高流量高并发场景下的秒杀商品完成支付,收到接口回调付款成功的大致流程。
1、商家端会在后台发起一些秒杀商品信息,因为秒杀活动开始时是同一时间的高流量高并发造成的大量请求,所以我用到了redis来做秒杀商品的缓存,目的就是提前预热参与秒杀的商品信息,避免在秒杀活动开始时给数据库制造压力
2、在消费者端,用户下单,但是因网络问题或系统延迟而导致用户重复下单,这就可能造成占用库存量的问题,当时呢,我是使用token令牌应用于项目解决用户重复下单。当我们发起请求拿到前台的token令牌进行判断,并删除缓存的token令牌,如果该用户重复下单的情况下传来的第二两次token令牌就不存在,会给用户友好提示以此解决幂等性问题。
3、因为是高流量高并发的业务场景会有高并发的问题,就会出现超卖的现象,当时解决这个问题我采用的是redisson分布式锁,对库存进行上锁,redis分布式锁其实就是往redis设置一个key和value同时设置一个有效时间,并且redis是单线程的,不会并发操作,之后在进行删除redis的缓存进行解锁,从而避免超卖的现象,后期测项目效率不够快,做了优化通过lua脚本,意为着可以将多个操作合并为一个原子操作,做到无锁
4、在用户抢到之后采用异步下单,为什么用到rabbitMq呢?是因为我们的秒杀需求是同一时间内的大量请求,采用异步下单可以解决消峰平谷、避免高耦合,mq还有重试机制
因网络延迟导致下单失败时mq会进行重试,持续重试机制三次后,mq会认为消息出现问题停止重试。如果说用户在5分钟内未完成下单可以用延迟队列取消订单,以免未下单的用户占用库存(用户体验感)
5、因为是高流量高并发的场景下有大量用户同时访问,会导致后端服务压力的激增,所以我采用阿里的sentinel框架做了限流熔断,为什么会采用sentinel框架呢?与hystix相比之下sentinel框架有管理平台可方便我们对数据的可视化,使用一个SentinelResource 定义资源限制每秒请求的数量,有效地保护后端资源因过载出现问题,如果系统某一节点出现异常进行熔断降级处理,保证其他服务不受影响
6、还有就是防止黑客攻击,因为黑客可以通过抓包获取我的接口信息,对于解决接口安全当时我是用加密签名,验签的方式保证黑客不能伪造请求和篡改数据,在与前端联调的接口文档中会约定一个签名方式,当中的签名涉及到有时间戳、随机数作为拼接参数,而时间戳是来保证我的请求在多少时间内是有效的,并把这个参数发送到后端进行比较相同则是正常请求
7、因为我们是线上线下的交易,涉及到金钱是比较重要的,拿到用户下单完成后的订单号到数据库进行校验该订单的信息确保订单存在,同时避免用户重复支付的问题,我会拿这笔订单的信息到订单交易流水表中查询一下存在且是支付中或支付成功的状态会给用户返回友好提示订单不可重复支付,因为订单交易流水可以有多条但是交易流水支付成功数据只有一条,在我对该流水交易校验完成后我会先保存一条流水信息,再调用支付宝三方接口去支付,因为支付是用户发起支付宝接收的,如果没有收到支付宝的回调信息就可能会丢单,此时我在调用三方接口前保存流水就可有效防止丢单问题,在回调的时候同时也要考虑到避免数据被篡改我也对接口做了验签,同时我还做了防止用户重复回调因为订单交易成功后是要给用户加积分的,如果同一笔订单多次调用是会重复加积分的这是不对的,所以对支付接口回调的幂等性问题我是用交易的订单状态判断的,如果是处理中说明没有收到过回调,如果不为处理中说明已经收到回调,避免重复回调,接下来修改交易流水状态,订单状态
8、当时对于支付考虑到接入支付宝、微信、银联等不同接口接入支付,所以我是用策略模式来实现的,为什么这么做呢?因为支付的主流程是大致不变的基本都是避免重复支付、保存交易流水,只是调用的三方接口不同,我是抽象出一个接口,子类实现接口的方法,定义不同支付方式的service接口纳入spring容器,拿到前台传来的支付类型通过applicationContext.getBean获取对象用父类接收并处理,如果在以后还要再接入其他接口,我可以在原有支付的基础上对代码做扩展减少支付代码的冗余,这样一个开闭原则可以有效避免我后期对代码维护提高。
电商秒杀业务流程图
如果想了解其他知识可以去我主页看别的文章!