java 2023秒杀项目 day(1) 面经

这篇具有很好参考价值的文章主要介绍了java 2023秒杀项目 day(1) 面经。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、秒杀项目

1.1 如何设计秒杀系统

  • 高可用
  • 高性能:支持高并发访问
  • 一致性:秒杀要保持数据的一致性

1.2 数据库

  • 字符集:utf8mb4
    可以存储emoji表情

二、业务

2.1 登录

2.2.1 密码加密

  • MD5(MD5(密码+固定salt)+随机salt)
  • 前端使用固定salt进行第一次加密,后端使用随机salt进行第二次加密。

2.2.2 密码参数校验

  • pom
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
  • 自定注解
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
        validatedBy = {
            IsMobileValidator.class
        }
)
public @interface IsMobile {
    //该字段必填

    boolean required() default true;

    String message() default "手机号码格式错误";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

package com.example.seckilldemo.validator;

import com.example.seckilldemo.utils.ValidatorUtil;
import org.thymeleaf.util.StringUtils;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
 * 手机号码校验规则
 *
 * @author: LC
 * @date 2022/3/2 3:08 下午
 * @ClassName: IsMobileValidator
 */
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {

    private boolean required = false;

    @Override
    public void initialize(IsMobile constraintAnnotation) {
//        ConstraintValidator.super.initialize(constraintAnnotation);
        required = constraintAnnotation.required();
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        if (required) {
            return ValidatorUtil.isMobile(s);
        } else {
            if (StringUtils.isEmpty(s)) {
                return true;
            } else {
                return ValidatorUtil.isMobile(s);
            }
        }
    }
}

2.2.3 分布式session

  • 请求通过分布器分发到不同的tomcat服务器,有的tomcat存储了用户相关的session信息,有的没有,当请求分发到没有session信息的tomcat服务器上时,用户需要重新进行登录。
2.2.3.1 解决方案
  • session复制:会增加存储,只需要进行tomcat配置
  • 后端集中存储,会增加复杂度

2.2.4 参数解析器

  • 配置
package com.example.seckilldemo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * MVC配置类
 *
 * @author: LC
 * @date 2022/3/3 2:37 下午
 * @ClassName: WebConfig
 */
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private UserArgumentResolver userArgumentResolver;
    @Autowired
    private AccessLimitInterceptor accessLimitInterceptor;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
//        WebMvcConfigurer.super.addArgumentResolvers(resolvers);
        resolvers.add(userArgumentResolver);
    }

    //静态资源展示
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        //swagger 和 knife4j
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(accessLimitInterceptor);
    }
}

  • 参数解析实现
package com.example.seckilldemo.config;

import com.example.seckilldemo.entity.TUser;
import com.example.seckilldemo.service.ITUserService;
import com.example.seckilldemo.utils.CookieUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定义用户参数
 *
 * @author: LC
 * @date 2022/3/3 4:46 下午
 * @ClassName: UserArgumentResolver
 */
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {

    @Autowired
    private ITUserService itUserService;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        Class<?> parameterType = parameter.getParameterType();
        return parameterType == TUser.class;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        return UserContext.getUser();

        //        HttpServletRequest nativeRequest = webRequest.getNativeRequest(HttpServletRequest.class);
//        HttpServletResponse nativeResponse = webRequest.getNativeResponse(HttpServletResponse.class);
//        String userTicket = CookieUtil.getCookieValue(nativeRequest, "userTicket");
//        if (StringUtils.isEmpty(userTicket)) {
//            return null;
//        }
//        return itUserService.getUserByCookie(userTicket, nativeRequest, nativeResponse);
    }

}

2.3 异常处理

2.3.1 ControllerAdvicer+ExceptionHandler

处理controller抛出的异常。

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public RespBean ExceptionHandler(Exception e) {
        if (e instanceof GlobalException) {
            GlobalException exception = (GlobalException) e;
            return RespBean.error(exception.getRespBeanEnum());
        } else if (e instanceof BindException) {
            BindException bindException = (BindException) e;
            RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);
            respBean.setMessage("参数校验异常:" + bindException.getBindingResult().getAllErrors().get(0).getDefaultMessage());
            return respBean;
        }
        System.out.println("异常信息" + e);
        return RespBean.error(RespBeanEnum.ERROR);
    }
}

2.4 秒杀

2.4 逻辑

2.4.1 秒杀前判断

  • 如果在秒杀范围内,前端是会出现秒杀按钮的
  • 判断用户是否登录
  • 判断秒杀商品的库存是否足够
  • 判断用户是否重复下单,查看秒杀订单表
  • 进行秒杀,进行减库存,然后生成秒杀订单表,跳转到订单详情页面

2.4.2 进行秒杀

  • 修改库从需要判断库从大于0才及进行修改:使用update语句
boolean seckillGoodsResult = itSeckillGoodsService.update(new UpdateWrapper<TSeckillGoods>()
                .setSql("stock_count = " + "stock_count-1")
                .eq("goods_id", goodsVo.getId())
                .gt("stock_count", 0)
        );
  • 生成秒杀详情订单

2.5 业务流程

  • 用户进行登录输入用户名和密码,在密码在前端用固定salt加密,传到后端,后端对密码,用户名进行验证。验证通过,为用户生成cookie信息,cookie返回,并且将用户信息存储到redis中。

  • 登陆成功,到秒杀商品列表页面,选择秒杀的商品,进入到商品详情页面,点击秒杀商品进行秒杀。
    java 2023秒杀项目 day(1) 面经

  • 正在秒杀显示正在进行中

  • 秒杀成功进入到商品订单详情页
    java 2023秒杀项目 day(1) 面经

  • 商品详情页java 2023秒杀项目 day(1) 面经

2.6 redis信息

  • 秒杀时的验证码的key
    超时时间300s
captcha+userID+goodsID

2.7 秒杀过程

  • 判断用户登录信息
  • 判断内存标记中的秒杀商品是否足够:Controller实现InitializingBean的afterPropertiesSet方法,将秒杀商品信息读到内存标记中,并且将秒杀商品信息存储到redis中。
 HashMap goodsStockMap = new HashMap<Long, Boolean>();
  • 判断是否重复秒杀:redis中查询订单信息,没有则没有重复秒杀。
  • redis预减库存,预减少成功后发送rabbitmq消息。
  • mq接收者消费mq消息,判断是否重复抢购,判断商品库存,然后秒杀,生成秒杀订单,生成秒杀商品详情订单。将秒杀订单信息存储到redis中。

2.8 接口限流

  • 针对用户请求的uri和用户的id进行限流,输入最大限流次数,通过redis实现限流。
  • 第一次获取key,key为null,设置key并且value为1
  • 后面每访问一次,次数加1,超过次数,将返回信息写入response
 private void render(HttpServletResponse response, RespBeanEnum respBeanEnum) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        PrintWriter printWriter = response.getWriter();
        RespBean bean = RespBean.error(respBeanEnum);
        printWriter.write(new ObjectMapper().writeValueAsString(bean));
        printWriter.flush();
        printWriter.close();
    }

三、问题

3.1 前端不能传秒杀库存

前端传递的数据可能会被修改

3.2 问什么新建秒杀表不在原表加字段

  • 如果秒杀活动较多每次都要在原表上进行修改,比较麻烦,而且物品的原价和秒杀价可能同时使用,在原表修改,容易出错。

3.3 打包

  • 这里需要maven插件
  • 去掉test
    mvn clean
    mvn package
  • java -jar jar包运行
 <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

四、压测

4.1 测试商品详情接口

4.1.1 测试条件

生成5000用户,将用户的cookie存入redis。

4.1.2 查询文件列表

java 2023秒杀项目 day(1) 面经

4.2 单服务秒杀测试

4.2.1 原始秒杀测试没有任何优化策略

  • jmeter结果
  • 出现超卖的情况
  • 此时没有添加事务,没有在扣库存前判断库存是否大于0
    java 2023秒杀项目 day(1) 面经
    java 2023秒杀项目 day(1) 面经

4.2.2 未使用mq的测试

  • 不使用mq,不使用内存标记,不使用redis预减库存
    java 2023秒杀项目 day(1) 面经

  • 不使用mq,不使用内存标记,使用redis预减库存
    java 2023秒杀项目 day(1) 面经

  • 不使用mq,使用内存标记,使用redis预减库存
    java 2023秒杀项目 day(1) 面经

4.2.3 使用了mq后的测试

  • 使用mq,使用内存标记,使用redis预减库存
    java 2023秒杀项目 day(1) 面经
  • 50000线程测试结果
    java 2023秒杀项目 day(1) 面经

4.3 多服务秒杀测试

五、秒杀优化

5.1 页面优化

5.1.1 页面缓存(返回的是整个html,前后端未分离)

将页面渲染好后缓存到redis中,设置过期时间T,这样用户得到的就是T内的页面。这样在T时间内不用每次都渲染页面,减少了时间。T不能太长,也不能太短。

5.1.2 url缓存

和页面缓存差不多,只不过url会有动态参数,所以缓存的多一点。根据不同动态参数进行缓存。

5.1.3 页面静态化(前后端分离)

后端只需传递数据到前端,此时后端不需要返回整个html页面了。

5.2 单服务秒杀后端优化

5.2.1 减库存优化及修正

  • 加事务
  • 判断库存>0才进行减库存操作
  • 添加唯一索引防止用户重复下单,这个重复下单是防止用户下用户id和商品id相同的单,上面的防止重复下单是防止用户下多个不同的单。

5.2.2 用户重复秒杀问题

  • 重复秒杀有2种,一种对同一商品进行秒杀,通过建立用户ID和商品ID的唯一索引防止重复秒杀。

5.2.3 使用redis优化

  • 秒杀前将商品信息都加入到redis中,进行预减库存,同时生成秒杀消息,发送到mq对列,给前端返回信息,前端根据返回的结构展示正在秒杀中页面。
  • 使用内存标记减少对redis的访问,原来是库存不足时还是需要通过访问redis,现在时设置额外的变量先访问变量,当变量种的库存不足直接返回,较少对redis的访问。

5.2.4 mq秒杀信息的获取

  • 数据库查询到秒杀的订单则秒杀成功,返回1
  • 数据库查询不到秒杀的订单且redis中商品的库存小于等于0,则秒杀失败
  • 秒杀正在进行中

六、技术作用

6.1 redis

  • 存储商品库存信息
  • 进行预减库存操作
  • 存储用户秒杀的订单信息,进行重复秒杀判断
  • 辅助验证码验证

6.2 ThreadLocal

  • 每个ThreadLocal都有一个自己的ThreadLocalMap,将数据以key,value的形式存到ThreadLocalMap中。
  • 拦截器通过threadlocal对象将用户的信息解析存入,在参数解析器时将用户信息取出,进行参数解析。此时通过threadlocal对象来存储每个请求处理线程的用户信息的副本,来实现数据访问的隔离性。
    threadlocal作用

6.3 Rabbitmq

  • 异步下单
  • 削峰填谷

6.4

七、安全优化

7.1 隐藏接口地址

7.2 验证码

7.3 接口限流

7.3.1 限流标准

最大qps的70%~80%

7.3.2 通用限流

  • 实现根据用户id对用户进行限流
  • 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {

    int second();

    int maxCount();

    boolean needLogin() default true;
}

八、问题

8. 1 商品少卖

在redis中初始化生成2份相同的商品信息会导致商品少卖。但是库存依然到0???文章来源地址https://www.toymoban.com/news/detail-496969.html

九、sql语句

9.1 清空表语句

DELETE  FROM t_order WHERE goods_id = 1;
DELETE FROM `t_seckill_order` WHERE goods_id = 1;

到了这里,关于java 2023秒杀项目 day(1) 面经的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 秒杀系统如何设计

    思路:对于秒杀系统,两个架构优化思路: 1)尽量将请求拦截在系统上游 2)读多写少的常用多使用缓存 方法: 1、限制用户在x秒之内只能提交一次请求(前端按钮置灰) 2、同一个uid,或同一类查询(例如车次)。限制访问频度,做页面缓存,x秒内到达站点层的请求,均

    2023年04月21日
    浏览(48)
  • 微服务---Redis实用篇-黑马头条项目-优惠卷秒杀功能(使用java阻塞队列对秒杀进行异步优化)

    1.1 秒杀优化-异步秒杀思路 我们来回顾一下下单流程 当用户发起请求,此时会请求nginx,nginx会访问到tomcat,而tomcat中的程序,会进行串行操作,分成如下几个步骤 1、查询优惠卷 2、判断秒杀库存是否足够 3、查询订单 4、校验是否是一人一单 5、扣减库存 6、创建订单 在这六

    2024年02月05日
    浏览(52)
  • 如何设计一个合格的高并发秒杀系统

    在前面的文章中,详细阐述了建设秒杀系统的目标与存在的挑战,并且简单罗列了如何应对这些挑战的方式。本章,就详细阐述对秒杀系统存在挑战的应对之道,最终构建出兼具高并发、高性能和高可用的秒杀系统。心中不仅了解建设秒杀系统存在的挑战,更清楚的知道这些

    2024年02月05日
    浏览(43)
  • 2023年信息系统项目管理师如何报名?老司机告诉你

    信息系统项目管理师是全国计算机技术与软件专业技术资格(水平)考试(简称软考)项目之一,是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试,既属于国家职业资格考试,又是职称资格考试。信息系统项目管理师,属于软考三个级别中的“高级”

    2024年02月15日
    浏览(31)
  • 华为面经整理(2023)

    最近越来越多公司校招进入面试流程了,为了帮助大家更好的应对面试,大彬整理了 往年华为校招面试的题目 ,供大家参考~ 技术一面 自我介绍 说下项目中的难点 volatile和synchronized的区别, 问的比较细 大顶堆小顶堆怎么删除根节点 CSRF攻击是什么,怎么预防 线程通信方式

    2024年02月06日
    浏览(44)
  • 面经-2023-中兴-数字IC设计

    专栏推荐: 2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 专栏首页: 2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 专栏内容: 笔试复盘篇 2023秋招过程中整理的笔试题,来源包括我自己求职笔试以及整理其他同学的笔试。包含华为、中兴、联发科、

    2024年02月12日
    浏览(41)
  • 2023前端面经(面试准备+面试题)

    1.1 博客粉丝少,要不要写简历上 博客粉丝数量少,文章内容都是基础的东西,要不要写到简历上?咨询了一些群友,以下是一些回复: 1.“我作为面试官,拿到的简历如果有自己建立的站点或者博客的链接我会点进去看看的,另外面试过程中,也会去问一两点。所以我建议

    2024年02月09日
    浏览(45)
  • 面经-2023-联发科MTK-数字芯片设计

    专栏推荐: 2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 专栏首页: 2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 专栏内容: 笔试复盘篇 2023秋招过程中整理的笔试题,来源包括我自己求职笔试以及整理其他同学的笔试。包含华为、中兴、联发科、

    2024年02月12日
    浏览(37)
  • 2023网络安全面试题(附答案)+面经

    随着国家政策的扶持,网络安全行业也越来越为大众所熟知,相应的想要进入到网络安全行业的人也越来越多,为了拿到心仪的Offer之外,除了学好网络安全知识以外,还要应对好企业的面试。 所以在这里我归纳总结了一些网络安全方面的常见面试题,希望能对大家有所帮助

    2023年04月09日
    浏览(53)
  • 2023米哈游图像算法暑期实习面经

    来源:投稿 作者:LSC 编辑:学姐 本文不可转载 违者必究 1.自我介绍 2.能实习多久?公司在心目中的地位排序等 3.是否了解公司,用他们的产品吗?(比如原神) ,喜欢游戏吗? 我只知道公司技术很厉害,游戏做的很好,但是我不喜欢玩游戏,从来没玩过,王者都没玩过,我

    2024年02月16日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包