【项目实战】秒杀场景超高含金量项目,3000 QPS 高并发秒杀系统实现

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

【项目实战】秒杀场景超高含金量项目,3000 QPS 高并发秒杀系统实现
视频链接见文章底部


一、项目导入

1. 导入代码

2. 阅读代码

前后端分离的跨域问题?(CORS)

比如80端口 请求9000端口

@Configuration
public class CorsConfig {
   
    @Bean
    public CorsWebFilter corsFilter() {
   
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        CorsConfiguration config = new CorsConfiguration();
        // 允许cookies跨域
        config.setAllowCredentials(true);
        // #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedOrigin("*");
        // #允许访问的头信息,*表示全部
        config.addAllowedHeader("*");
        // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
        config.setMaxAge(18000L);
        // 允许提交请求的方法,*表示全部允许
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}

跨域 --> 客户端需要询问服务端是否允许跨域

在发起api请求前,会构造一个预请求。疫预请求的方法是options类型

且请求参数,请求体都不存在。预请求的作用是请求到服务端,如果服务端允许跨域,就会通过预请求携带对应的响应头信息回来。

如果浏览器检测到响应头中有允许当前页面跨域,就会发起真实请求

cors:是一套机制,用于浏览器校验跨域请求。(同源策略)

基本理念:只要服务器明确表示允许则,校验通过。明确拒绝或没有表示,百不通过

微服务中如何获得用户真实IP?

用户---->网关---->微服务

网关可以拿到用户ip!

public class CommonFilter implements GlobalFilter {
   
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
   
        /**
         * pre拦截逻辑
         * 在请求去到微服务之前,做了两个处理
         * 1.把客户端真实IP通过请求同的方式传递给微服务
         * 2.在请求头中添加FEIGN_REQUEST的请求头,值为0,标记请求不是Feign调用,而是客户端调用
         */
  ServerHttpRequest request =exchange.getRequest().mutate().
 header(CommonConstants.REAL_IP,exchange.getRequest().getRemoteAddress().getHostString()).
               header(CommonConstants.FEIGN_REQUEST_KEY,CommonConstants.FEIGN_REQUEST_FALSE).build();

全局过滤器,因为微服务的请求是网关转发过来的,getRequest().getRemoteAddress()获取到的是网关ip。

登录流程(jwt)

第0步:拿着token查

第一步:拿到用户信息,先到redis中查,查不到到数据库中查,并且存到redis中。

第二步:如果查到的信息为空或者查出的数据密码不符,往mq中发送消息(登录失败),抛出异常,提示密码或者用户名错误

第三步:密码符合,查出用户信息,创建token

private String createToken(UserInfo userInfo) {
   
    //token创建
    String token = UUID.randomUUID().toString().replace("-", "");
    //把user对象存储到redis中
    CommonRedisKey redisKey = CommonRedisKey.USER_TOKEN;
    redisTemplate.opsForValue().set(redisKey.getRealKey(token), JSON.toJSONString(userInfo), 		         redisKey.getExpireTime(), redisKey.getUnit());
    return token;
}

过期时间是30分钟,如何保证用户体验?
在过滤器中

/**
 * post拦截逻辑
 * 在请求执行完微服务之后,需要刷新token在redis的时间
 * 判断token不为空 && Redis还存在这个token对于的key,这时候需要延长Redis中对应key的有效时间.
 */
String token,redisKey;
if(!StringUtils.isEmpty(token =exchange.getRequest().getHeaders().getFirst(CommonConstants.TOKEN_NAME))
        && redisTemplate.hasKey(redisKey = CommonRedisKey.USER_TOKEN.getRealKey(token))){
   
    redisTemplate.expire(redisKey, CommonRedisKey.USER_TOKEN.getExpireTime(), CommonRedisKey.USER_TOKEN.getUnit());
}

二、秒杀功能实现

1. 秒杀列表功能展示

秒杀产品列表查询

数据库设计

商品表t_product

id bigint 主键id
product_name varchar 产品名称
product_title varchar 产品标题
product_img varchar 产品图片链接
product_detail longtext 产品详细介绍
product_price decimal 产品价格
秒杀商品表t_seckill_product
id
product_id(对应t_product_id)
seckill_price(价格)
intergral(积分)
stock_count(库存)
start_date(开始日期)
time(秒杀开始场次)
bigint
bigint
decimal
decimal
int
date
int

秒杀商品SeckillProductVo(t_product + t_seckill_product)

productName
productTitle
productImg
productDetail
productPrice
currentCount;

逻辑:

Result<List<SeckillProductVo>> selectTodayListByTime(@RequestParam("time") Integer time)

得到秒杀商品t_seckill_product_id集合

Result<List<Product>> selectByIdList(@RequestParam("ids") List<Long> idList);

得到id的商品集合

在这里插入图片描述

整体思想图

商品详情页

在这里插入图片描述

@RequestMapping("/find")
public Result<SeckillProductVo> findById(Integer time, Long seckillId) {
   
    return Result.success(seckillProductService.selectByIdAndTime(seckillId, time));
}
@Cacheable(key = "'selectByIdAndTime:' + #time + ':' + #seckillId")
public SeckillProductVo selectByIdAndTime(Long seckillId, Integer time) {
   
    SeckillProduct seckillProduct = seckillProductMapper.selectByIdAndTime(seckillId, time);

    Result<List<Product>> result = productFeignApi.selectByIdList(Collections.singletonList(seckillProduct.getProductId()));
    if (result.hasError() || result.getData() == null || result.getData().size() == 0) {
   
        throw new BusinessException(n
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值