P310、商城业务-秒杀服务-后台添加秒杀商品
秒杀具有瞬间高并发的特点,针对这一特点,必须要做限流 + 异步 + 缓存(页面静态化)
- 独立部署。
限流方式:
-
前端限流,一些高并发的网站直接在前端页面开始限流,例如:小米的验证码设计
-
nginx 限流,直接负载部分请求到错误的静态页面:令牌算法 漏斗算法
-
网关限流,限流的过滤器
-
代码中使用分布式锁-信号量
-
rabbitmq 限流(能者多劳:chanel.basicQos(1)),保证发挥所有服务器的性能。
-
上架秒杀商品(价格和平常不一样)
-
后台管理系统的优惠营销-每日秒杀,可以录入每天的秒杀场次
-
通过点击后台管理页面点击查看路径,
-
添加网关路由 api/coupon/** 路由到coupon服务
-
增删改查场次信息,自动生成,可以直接用
-
关联商品:给场次新增一些秒杀哪些商品的信息,价格,件数
-
点关联,查询的是所有场次的商品信息,需改为所查场次的关联商品信息。通过传入的promotionSessionId,修改查询条件
http://localhost:88/api/coupon/seckillskurelation/list?t=1634368262922&page=1&limit=10&key=&promotionSessionId=1 -
确认关联商品的增加、修改可以使用
秒杀场次表sms_seckill_session
场次商品关联表sms_seckill_sku_relation
P311、商城业务-秒杀服务-定时任务&Cron表达式
- 创建秒杀微服务(独立模块、独立部署)
Springboot模块+SpringBootDevTools+Lombok+SpringWeb+SpringDataRedis+OpenFeign - 添加依赖common并排除seata,common依赖mybatis,需排除数据源自动配置
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) - 配置application.properties,服务名,端口号,nacos.discover地址端口,redis地址,添加服务发现注解@EnableDiscoveryClient
定时任务(quartz流行框架,java原生timer类,每天对账,每月财务汇总,)
Cron表达式 秒分时日月周 年(spring不支持)
枚举:1,2,3
范围:3-8
任意:*
步长:7/6,第七秒启动,每隔6秒
防止日和周几冲突:一个精确(或*),另一个?
最后一个:L,***?*3L (1表示周日,2表示周一,3表示周二,3L表示最后一个周二)
工作日:W, (LW最后一个工作日)
第几个:# ***?*5#2:第2个周4
网上有cron表达式生成器
秒杀商品定时上架->定时任务查询需要上架的秒杀商品->封装最新的秒杀商品信息保存到redis中->设置秒杀商品分布式信号量作为库存扣减信息->结束
上架放到缓存,商品数据从缓存拿,库存也上架到缓存,扣库存从缓存扣
P312、商城业务-秒杀服务-SpringBoot整合定时任务与异步任务
解决:使用异步+定时任务完成定时任务不阻塞功能
类上面@EnableScheduling 开启定时任务
方法上面加@Scheduled(cron="") 开启一个定时任务
自动配置类TaskSchedulingAutoConfiguration
3、让定时任务异步执行
异步任务
类上面@EnableAsync 开启异步任务功能
方法 @Async 给希望异步执行的方法上标注,默认线程池,核心大小8
自动配置类TaskExecutionAutoConfiguration 属性绑定在TaskExecutionProperties
注意:2)经常不生效。有些版本不好使,有些版本好使。
P313、商城业务-秒杀服务-时间日期处理
定时上架类,定时配置类
上架最新3天(提前预告)
当天00:00:00-23:59:59
明天00:00:00-23:59:59
后天00:00:00-23:59:59
重复上架无需处理
远程调用优惠服务获取活动(秒杀服务没有数据库,在优惠服务的数据库里)
扫描三天的活动 sql条件between 2020-02-19 00:00:00 and 2020-02-21 23:59:59含头不含尾
LocalDate
LocalTime
LocalDateTime
时间最好格式化 start.format(DateTimeFormatter.ofPatter(”yyyy-MM-dd HH:mm:ss”))
P314、商城业务-秒杀服务-秒杀商品上架-1
优惠服务里,查询每一个活动关联的所有商品(判断活动 list!=null&list.size()>0)遍历
秒杀服务远程调用该接口
秒杀服务封装返回的数据信息(秒杀活动的信息,sku信息vo)
上架商品(缓存到redis,使用StringRedisTemplate(string,string) )
缓存活动信息
Key(活动开始结束时间)
Value(活动的所有商品信息)
缓存活动的关联商品信息
redis缓存2个信息
1、缓存活动信息key:seckill:sessions:start_endtime
value:[sessionId_skuIds]
2、活动里的商品信息:seckill:skus,Hash结构
缓存商品:
1)sku的基本信息
2)sku的秒杀信息
3)设置当前商品的秒杀时间信息
4)随机码
使用JSON字符串
新建Redis保存TO(秒杀商品详细信息,sku的详细信息,SkuInfoVo)
新建SkuInfoVo ,商品详细信息
P315、商城业务-秒杀服务-秒杀商品上架-2
- Sku的基本信息,调用远程商品服务
- 设置当前商品的秒杀时间信息
- 随机码
seckill?skuId=1,秒杀接口暴露,商品id暴露,容易招致攻击,无限重试发这个请求,时间一到肯定抢到。
seckill?skuId=1&key=随机码 秒杀开启那一刻暴露随机码
秒杀库存 - > redisson分布式锁:分布式信号量。根据商品随机码扣库存,而不是商品ID
使用库存作为分布式锁-信号量:限流
信号量id:seckill:stock+随机码
使用随机码减信号量。
P316、商城业务-秒杀服务-秒杀商品上架-3
P317、商城业务-秒杀服务-幂等性保证
上架只需要一台服务器上架就可以:分布式锁处理
上架时判断存不存在(判断条件使用 场次-商品id,注意UUID)
P318、商城业务-秒杀服务-查询秒杀商品
秒杀页面展示商品:返回当前页面时间需要展现的秒杀商品信息
1)确定当前时间属于哪个秒杀场次
2)获取这个秒杀场次需要的所有商品信息
秒杀开始页面需要随机码
秒杀预告页面不能随机码
页面展示商品信息
配置hosts,Linux(nginx),配置网关,秒杀请求路由至秒杀服务
修改前端页面
P319、商城业务-秒杀服务-秒杀页面渲染
商品详情页展示秒杀预告
1)页面展示完成后给秒杀服务发送Ajax请求查询是否参与秒杀
2)商品服务远程调用查询秒杀服务(采取这个)
找到所有需要参与秒杀的商品的key(绑定hash操作)
使用正则匹配
商城模块新建vo接收数据
前端页面展示(预告和正在秒杀显示不一样)
注意时间格式化
P320、商城业务-秒杀服务-秒杀系统设计
抢购功能。
不管是不是“立即抢购”按钮,都是加入购物车,价格是秒杀价格
关注重点不在业务,而在于高并发。
2的解决方法:1、连接加密,生成MD5或UUID
2、加随机码,只有活动开始才生成
3:还可以redis集群,分片的高可用
4:nginx存静态,可以复制多份
上线后,更好的条件,CDN网络:比如阿里云
恶意请求拦截,网关
前端限流:点一下后不能再点,点一下等1秒 等等。但知道点按钮发什么请求,限不住恶意脚本。
队列削峰:杀手锏
P321、商城业务-秒杀服务-登录检查
判断活动时间,修改按钮
抢购链接需要带随机码,场次和商品id
前端页面(url)编写
前端设置登录拦截,没登陆跳转登录页面
后端创建拦截器判断是否登录,拦截器创建后一定要配置到web中(创建WebConfig 实现WebMvcConfig,注入拦截器…)
使用SpringSession依赖(session使用redis保存),获取前端cookie
拦截器必须添加到这里才能生效。
P322、商城业务-秒杀服务-秒杀流程
第一套流程:加入购物车,跳到订单确认页,然后结算、支付(和以前业务整合)
优点:错峰,业务统一,数据模型、、、和普通商品只差价格、库存
缺点:增加购物车、订单等服务的压力
第二套流程:适用超高并发(独立业务)
点击立即抢购,请求发给秒杀系统,开始抢购,抢购先做一系列判断,比如登录,如果登录成功,再来校验秒杀请求合法性,校验包括当前秒杀商品有没有到秒杀时间,随机码和skuid的对应关系对不对,包括这个人是不是已经秒杀过商品等幂等性,通过以后,想要秒杀直接获取信号量,获取到就成功,获取不到就失败。(数据库库存已经锁定信号量的量了)成功以后,快速生成一个订单号,只生产一个订单号,把用户信息、订单号、商品直接发给MQ,只发了一个消息,没有动数据库,订单服务慢慢监听消息,创建订单,此时,立即给用户返回秒杀成功,正在准备订单,然后跳转到收获地址确认页,支付确认页。
优点:到创建订单为止没有访问数据库、没有远程调用,很快
缺点:订单服务炸了,却还是返回秒杀成功,没有服务消费订单,导致用户一致支付不成功。
该方法主要结合消息队列(通常是保证最终一致性,这里的作用更多是流量削峰)
判断是否买过(redis数据缓存至活动结束(过期时间),判断是否储存成功,成功没买过,失败买过)。
分布式信号量减库存(用tryAcquire方法,比如传100毫秒,没拿到返回null,不阻塞,也可以不设时间,就尝试一下)
P323、商城业务-秒杀服务-秒杀效果完成
RabbitMQ依赖,队列的编写
秒杀成功一次12ms,1个线程1s100并发
1个tomcat500线程,就是5W并发
百万并发20个单机集群就可以了。
P324、商城业务-秒杀服务-秒杀页面完成
数据库有一个锁定库存字段。