国内的互联网大厂,腾讯企鹅、蚂蚁花呗、淘宝天猫、京东小狗、百度的熊,当当猴,小米兔,新浪独眼龙,迅雷蜂鸟,UC松鼠等等,他们都有一个共同点,都有一个对应的动物做吉祥物。这其实是中国的一种图腾文化,相信它们有一种超自然力,会保护自己,并且还可以获得他们的力量和技能。

哈哈哈,跑题了,不过他们还有一个共同点,就是这些大厂不是做电商的,就是做社交的,所以都离不开高并发的问题。做电商的,例如小米,京东,淘宝,当当等,肯定都有限时抢购,限时秒杀的场景了,最出名的就是小米的饥饿营销,京东618,淘宝双11,天猫双12了。

说正文之前,先放一张整体的脑图,文章太长各位看官看不下去?没关系,看下我总结的脑图也是有收获滴~

场景引入

来看一个京东的一个秒杀场景,其实像京东这样的公司,秒杀业务是不间断的,每天都有大量的秒杀场景。

可以看到这款苹果的数据线设置了提醒的人数都有1万多人了,毕竟秒杀的价格只有9块9啊,到18:00开始秒杀的时候,可能人更多,遇到一些购物节,更不用说了。

假设现在要卖100条这款苹果数据线,按以往的经验来看,要预估同时抢购的人数有10万人。

开发A拿到需求,心想,完了呀,这咱们的服务器哪里顶得住,直接挂了呀。

别急,拿到需求,先分析会出现哪些问题,再考虑怎么做设计和实现。

潜在的问题

高并发

这个问题所有人都知道,不单单是秒杀的场景,只不过这个场景是web开发里最具有代表性的。特点就是短时间内出现超高流量,瞬时用户量大。

电商的营销还经常会配合短信通知,订阅推送,平台广告等来吸引更多的用户来参加这个秒杀活动。

咱们的Mysql很难扛得住,就算扛住了,响应也会变的很长,用户体验很差。但是Redis能扛啊,单机的Redis扛个3w的QPS应该问题不大,现在一般是集群部署。

引入缓存层,大量的请求进来,又会引出很多另外的问题了,如缓存雪崩、缓存击穿、缓存穿透等,没处理好,出现这些问题,照样会打到 DB 层,服务还得挂,秒杀活动没了,商城人气也没了。

开发同学何在?出来背锅了—

超卖

只要是秒杀,都逃不开超卖的问题。你只有100的库存,因为你的程序问题,导致卖出去200个,完了,短时间内哪里去搞多出来的这100个货,如果是卖手机呢,100个手机得多少钱,平台规定时间内发不了货,扣你分,用户以更低的价格抢到,肯定不愿意取消,发不了货,投诉你的店铺,Game Over。。

不知道的开发同学的工资够扣吗?搬砖有风险,入行需谨慎–

恶意请求

你卖的价格比市场价低,那么我买到再转手不就赚了?

所以免不了有黄牛,黑客搞些服务器,脚本,模拟大量的请求,你就算单身20年的手速也是比不上机器的请求速度的。

链接暴露

Chrome的开发者模式里是可以看到请求的链接地址的,虽然前段可以在请求前置灰按钮,但是开发人员是知道的,在秒杀前,提前请求。。。

数据库

每秒上万级别的请求落到 数据库 的话,基本都能把库打挂,到时候影响的就不是一个服务了,微服务没做降级,限流,熔断的话,可能是全站。

如何优化

问题还挺多,不着急,听我一点一点分析

前端

页面静态化

秒杀的都是特定的商品,在秒杀前肯定是知道的,而且现在都是前后端分离的,所以可以提前生成页面需要用到的资源,然后放入 CDN服务器 ,请求的时候就能直接加载出来了,减少服务器的压力。

秒杀链接加盐

URL动态化,就连写代码的人都不知道,你就通过MD5之类的摘要算法加密随机的字符串去做url,然后通过前端代码获取url后台校验才能通过。

前端限流

活动开始前按钮置灰

秒杀开始前,按钮置灰,防止用户在开始前就疯狂点击

前端需要定时请求后端服务器,来获取整个系统用的最新的时间,时间一到,按钮再恢复可用状态。有人会说了,那请求是有耗时的,晚一秒怎么办?秒杀活动是卖家定的,晚个几秒有影响吗?没有

活动开始后按钮置灰

用户点击,请求发送成功了,按钮就置灰个几秒,防止被一直点击,这也是个比较常用的方案。

后端

服务单一

现在都是微服务的设计思想了,采用分布式部署。所以就可以把秒杀这样的业务单独设计一个服务,管理自己的数据库,尽量避免影响到其他的服务

Redis集群

单机redis肯定不行,部署Redis集群,主从同步,读写分离,哨兵机制,持久化机制,全套搞起来,非常能顶。

库存预热

秒杀的本质,就是对库存的争夺。

所以提前将相关商品的库存数据加载到Redis里,后续库存的扣减都在Redis里完成,最后再通过MQ异步去修改库存即可。

但是用Redis同样有并发修改数据的问题,怎么办呢?别担心,Redis原生就支持LUA的脚本,LUA脚本是一定可以保证原子性的,写个LUA脚本,封装你的骚操作

限流&降级&熔断

搞了微服务,你跟我说还是跟以前一样,啥都不做?

后端限流:可以使用sentinel等。

降级:当整个微服务的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,我们可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用

熔断:降级了还顶不住?那就熔断吧,至少不能影响其他服务。进入熔断状态后,后续对该服务接口的调用不再经过网络,直接执行本地的默认方法,达到服务降级的效果。

消息队列(削峰填谷)

秒杀就是这种瞬间流量很高,但是平时又没有流量的场景,那消息队列完全契合这样的场景了呀,削峰填谷。

你可以把请求放消息队列,然后一点点消费去改库存就好了嘛,不过单个商品其实一次修改就够了,我这里说的是某个点多个商品一起秒杀的场景。

Nginx

负载均衡

Nginx是个轻量级的高性能web服务器,肯定比你的后端服务器tomcat,uWSGI能顶的多。

通过Nginx反向代理将请求负载均衡到你的后端服务器

限流

限制单个ip的单位时间内的请求次数。

这么做可以在网关层就拦截到很多不像人为的请求,比较脚本

数据库

Mysql的话,要结合实际业务和服务器配置,设置合理的数据库连接池。

单个微服务单个数据库,表的设计尽量简单

风控

前面的所有措施还是拦不住很多羊毛党,因为他们是专业的团队,他们可以注册很多账号来薅你的羊毛,而且不用机器请求,就用群控,操作几乎跟真实用户一模一样,注册了账号,还会养号,然后卖给黄牛。

当然大部分小公司都是没有风控的,但是像阿里云,腾讯这样的大厂,风控还是很厉害的。面对上面的这种用户,通过风控分析出来这个用户是真实用户的概念比较低,就直接认为是机器了,忽略它的请求。

风控可以说是流量进入的最后一道门槛了。

总结

秒杀系统基本涉及到了web开发的大部分技术栈了,我把能想到的点都写了,可能还有遗漏,毕竟水平有限啊

最后来一张总结图,撒花收官~~