1. 秒杀系统设计

秒杀(高并发)系统关注的问题

1、服务单独职责+独立部署

秒杀系统为单独的服务,即使自己扛不住压力挂掉,也不要影响其他服务

2、秒杀链接加密

防止恶意攻击,模拟秒杀请求,1000次/s的攻击;防止链接暴露,自己工作人员,提前秒杀商品;我们使用了带uuid随机码的机制;

3、库存预热+快速扣减

秒杀读多写少,无需每次实时校验库存,我们库存预热,放到redis中,信号量控制进来秒杀的请求;为了保证redis可以保证千万并发,可以给redis做集群,做成分片高可用;我们是用定时任务提取三天写到缓存中;

4、动静分离

Nginx做好动静分离,保证秒杀和商品详情页的动态请求才打到后端的服务集群,使用CDN网络,分担本集群的压力;比如访问静态资源,阿里云CDN会在最快的节点返回静态资源;

5、恶意请求拦截

识别非法攻击请求并进行拦截(网关层),比如伪造的请求没带令牌;保证能放到后端的请求都是正常行为;

6、流量错峰

使用各种手段,将流量分担到更大宽度的时间点。比如验证码(小米商城)、加入购物车(结账,锁库存还有一段时间);

7、限流&熔断&降级(必须)

前端限流+后端限流(限流把不合理的去除掉,如:一秒发送1w次的请求;就算合理的,次数太多也应该限制起来);限制次数,限制总量,快速失败降级运行(一部分流量引导到降级页面),熔断隔离防止雪崩;

8、队列削峰

1万个商品,每个1000件秒杀,双11;所有秒杀成功的请求,进入队列,慢慢创建订单,扣减库存即可;

2.秒杀核心流程

秒杀流程+消息队列监听流程

3. 秒杀系统编写

秒杀请求

@Autowired
private SeckillService seckillService;@GetMapping("/kill")
public String secKill(@RequestParam("killId") String killId, // session_skuID@RequestParam("key") String key,@RequestParam("num") Integer num, Model model){String orderSn = seckillService.kill(killId,key,num);// 1.判断是否登录model.addAttribute("orderSn", orderSn);return "success";
}

登录验证:编写拦截器

@Component
public class LoginUserInterceptor implements HandlerInterceptor {public static ThreadLocal<MemberRespVo> threadLocal = new ThreadLocal<>();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String uri = request.getRequestURI();// 这个请求直接放行boolean match = new AntPathMatcher().match("/kill", uri);// 如果是秒杀请求,才做这一系列的登录验证if(!match){HttpSession session = request.getSession();MemberRespVo MemberRespVo = (MemberRespVo) session.getAttribute(AuthServerConstant.LOGIN_USER);if(MemberRespVo != null){threadLocal.set(MemberRespVo);return true;}else{// 没登陆就去登录session.setAttribute("msg", AuthServerConstant.NOT_LOGIN);response.sendRedirect("http://auth.gulimall.com/login.html");return false;}}return true;}
}

将登录验证拦截器添加到webmvc的配置中

@Configuration
public class SeckillWebConfig implements WebMvcConfigurer {@Autowiredprivate LoginUserInterceptor loginUserInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**");}
}

流量削峰

配置rabbitmq

ipAddr: "192.168.56.10"
spring:rabbitmq:virtual-host: /host: ${ipAddr}

引入rabbitmq配置类,使用JSON序列化器,进行消息转换

@Configuration
public class MyRabbitConfig {@Beanpublic MessageConverter messageConverter(){return new Jackson2JsonMessageConverter();}
}

设置交换机和队列的名字常量

package com.atguigu.common.constant;
public class RabbitInfo {public static class Order{// 其实厂里应该大写,但是我们为了区分,这里也不改了public static final String exchange = "order-event-exchange";public static final String delayQueue = "order.delay.queue";public static final String delayRoutingKey = "order.locked";public static final String releaseQueue = "order.release.queue";public static final String releaseRoutingKey="order.release";// 其他路由key也是跳到releaseQueuepublic static final String baseRoutingKey="order.#";public static final int ttl = 900000;}public static class Stock{public static final String exchange="stock-event-exchange";public static final String delayQueue="stock.delay.queue";public static final String delayRoutingKey="stock.locked";public static final String releaseQueue="stock.release.queue";public static final String releaseRoutingKey="stock.release.queue";public static final String baseRoutingKey="stock.#";public static final int ttl = 900000;}public static class SecKill{public static final String exchange="seckill-event-exchange";public static final String delayQueue="seckill.delay.queue";public static final String delayRoutingKey="seckill.locked";public static final String releaseQueue="seckill.release.queue";public static final String releaseRoutingKey="seckill.release.queue";public static final int ttl = 900000;}
}

给交换机发请求

// 3. userId+skuId在redis中标识买过商品
String redisKey = memberRespVo.getId() + "-" + skuId;
// 让数据自动过期
long ttl = redisTo.getEndTime() - redisTo.getStartTime();
// SETNX,也就是不存在的时候才占位,如果能占位成功,说明这个人没买过
Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(redisKey,num.toString(),ttl<0?0:ttl,TimeUnit.MILLISECONDS);
if(aBoolean){// 占位成功 说明从来没买过RSemaphore semaphore = redissonClient.getSemaphore(SKUSTOCK_SEMAPHONE + randomCode);//使用tryAcquire()方法,因为acquire()是阻塞的boolean acquire = semaphore.tryAcquire(num);if(acquire){// 秒杀成功// 快速下单 发送MQ/**生成订单号,这样数据库通过消息队列保存后,订单支付页面也知道保存的id是多少 */String orderSn = IdWorker.getTimeId() + UUID.randomUUID().toString().replace("-","").substring(7,8);SecKillOrderTo orderTo = new SecKillOrderTo();orderTo.setOrderSn(orderSn);orderTo.setMemberId(memberRespVo.getId());orderTo.setNum(num);orderTo.setSkuId(redisTo.getSkuId());orderTo.setSeckillPrice(redisTo.getSeckillPrice());orderTo.setPromotionSessionId(redisTo.getPromotionSessionId());rabbitTemplate.convertAndSend(RabbitInfo.Order.exchange,RabbitInfo.SecKill.delayRoutingKey, orderTo);// 返回订单号return orderSn;}

监听秒杀单的队列

package com.atguigu.gulimall.order.listener;@RabbitListener(queues = RabbitInfo.SecKill.delayQueue)
@Component
public class OrderSecKillListener {@Autowiredprivate OrderService orderService;@RabbitHandlerpublic void listener(SecKillOrderTo secKillOrderTo, Channel channel, Message message) throws IOException {try {// 秒杀的时候没有订单,这时候才创建订单orderService.createSecKillOrder(secKillOrderTo);// 手动ack确认消费channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);} catch (Exception e) {channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);}}
}

本文完!!!

谷粒商城:秒杀系统设计与编写相关推荐

  1. 【谷粒商城 -秒杀服务】

    谷粒商城–秒杀服务–高级篇笔记十二 1.后台添加秒杀商品 未配置秒杀服务相关网关 1.1 配置网关 - id: coupon_routeuri: lb://gulimall-couponpredica ...

  2. 谷粒商城--秒杀服务--高级篇笔记十二

    谷粒商城–秒杀服务–高级篇笔记十二 1.后台添加秒杀商品 未配置秒杀服务相关网关 1.1 配置网关 - id: coupon_routeuri: lb://gulimall-couponpredica ...

  3. java商城源码视频教程_Java商城秒杀系统设计视频教程学习下载(含项目源码)...

    本Java商城秒杀系统视频教程目录如下:    java高并发秒杀系统1-1节java高并发商城秒杀优化学习指引.mp4 java高并发秒杀系统1-2节项目环境搭建(Eclipse)-节.mp4 ja ...

  4. 谷粒商城-分布式高级篇[商城业务-秒杀服务]

    谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...

  5. 谷粒商城-分布式高级篇【业务编写】

    谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...

  6. Day437438439.秒杀服务 -谷粒商城

    秒杀服务 一.定时任务-Quartz Cron表达式 执行定时任务需要给一个时间计划,这个时间计划可以用 Cron 表达式来编写 官方文档 Cron 表达式是一个字符串,是用空格分割的六到七个属性. ...

  7. 尚硅谷谷粒商城第十六天 支付、秒杀

    1. 支付 订单搞定之后就是支付了,首先搭建支付工程. 1.1. 搭建环境 pom.xml <?xml version="1.0" encoding="UTF-8& ...

  8. 尚硅谷2020微服务分布式电商项目《谷粒商城》-支付、秒杀

    学习更多的知识,整理不易,拒绝白嫖,记得三连哦 关注公众号:java星星 获取全套课件资料 1. 支付 订单搞定之后就是支付了,首先搭建支付工程. 1.1. 搭建环境 pom.xml <?xml ...

  9. 谷粒商城二十三秒杀服务

    秒杀是每一个电商系统中非常重要的模块,商家会不定期的发布一些低价商品,发布到秒杀系统中,秒杀系统的商品一般会放到首页展示,这样就可以引导用户购买商品. 秒杀的购买流程和普通的购买流程最大的特点就是瞬时 ...

  10. 分布式项目-谷粒商城。

    分布式项目 一,分布图 二,环境搭建 1.安装linux 2.安装docker 1 卸载系统之前的docker sudo yum remove docker \docker-client \docke ...

最新文章

  1. Struts2异常| 页面写入s:debug/标签报错, 去掉此标签后正常显示
  2. SQL SERVER自定义函数
  3. mysql clr_SQLCLR Tips: 配置数据库使其支持SQLCLR
  4. html marquee css,HTML之marquee(文字滚动)详解_html/css_WEB-ITnose
  5. Linux内核dev_set_drvdata()和dev_get_drvdata()存储自定义结构体用法
  6. 中关村 - DIY之如何唯美地阅读电子书
  7. Linux下scp命令的用法,Linux中cp和scp命令的使用方法
  8. 历史经验之QT在WIN32下编译环境配置步骤
  9. 调用Kinect 2.0摄像头
  10. 计算机多媒体专业就业现状,计算机多媒体技术就业前景怎么样
  11. linux五笔教程,RHEL6 64位操作系统安装极点五笔输法
  12. 杂谈随想第002篇:博客访问量破万的想法
  13. 企业微信——定时群机器人布置
  14. idea中GIT版本回退、
  15. Elasticsearch6.8开发指南-第三章-设置Elasticsearch
  16. ngnix配置cgi和fastcgi
  17. gym创建自己的强化学习环境env
  18. 京东实战·案例·[项目]
  19. H5微信端在IOS上不能播放音乐解决方案
  20. X265编码核心函数分析

热门文章

  1. 2016河南省第九届ACM程序设计竞赛[正式赛四]
  2. 当贝投影F5发布:3000ANSI流明亮度,6S疾速开机,16ms超低延迟
  3. 机器学习:决策树算法案例(西瓜数据集3.0)
  4. yum安装freeswitch
  5. css 加粗或斜体hover鼠标划过,导致异常闪动,整体宽度增加的解决办法
  6. 【5G学习笔记-8】38.306 36.306 User Equipment (UE) radio access capabilities 以及终端CDRX能力上报 featureGroupIndic
  7. Poker2 的爱与愁-入手两周,小小体会
  8. NLP学习笔记[1] -- 构建词向量模型 -- Word2Vec与词嵌入
  9. cad小插件文字刷_CAD实用小插件,这是一串让你效率提升相见恨晚的代码!
  10. 零基础学习 自动化编程- 第一天 计算机语言