栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

应用springboot和redis写的秒杀系统

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

应用springboot和redis写的秒杀系统

应用springboot和redis写的秒杀系统

提示:项目源码会后续放出

文章目录
  • 前言
  • 一、搭建项目
  • 二、分布式session
    • 1.用户登录功能
      • MD5的加密:
      • 整理思路:
      • 优化部分Cookie
      • 分布式session问题
          • 解决方案
  • 总结


前言

提示:对系统技术的介绍
前端技术:Thymeleaf、Bootstrap、Jquery
后端技术:SpringBoot、MyBaitsPlus、Lombok
中间插件:RabbitMQ、Redis

  1. JDK:1.8版本及以上
  2. maven:配置到idea,3.6.1版本
  3. 数据库:redis数据库
  4. idea平台
  5. 项目搭建、分布式session(用户登录、共享session)、秒杀功能(商品列表、商品详情、秒杀、订单详情)、压力测试(JMeter入门、自定义变量、正式压测)、页面优化(缓存、静态化分离)、服务优化(RebbitMQ消息队列、接口优化、分布式锁)、接口安全(隐藏秒杀地址、验证码、接口限流)
  6. 秒杀其实主要解决的是两个问题,一个是并发的读、一个是并发的写,这个项目特别适合小白理解并发编程。

提示:以下是本篇文章正文内容

一、搭建项目
  1. 项目名称seckill,表示秒杀
  2. 结构:com.shmily.seckill
    java web、mybatis-plus、mysql driver、lomback的jar
  3. 资源文件:resources文件夹下的(static、templates)
  4. 配置数据库
spring:
 thymeleaf:
   #关闭缓存
   cache: false
datasource:
 driver-class-name: com.mysql.cj.jdbd.Driver
 url: jdbc:mysql://localhost:3306/seckill?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
 username: root
 password: 123456
 hikari:
   #连接名
   pool-name: DateHikariCP
   #最小空闲连接
   minimum-dile: 5
   #空闲了解存活最大对的时间,默认是600000(10分钟)
   idle-timeout: 180000
   #最大的连接数,默认是10
   maximum-pool-size: 10
   #从连接池返回的连接自动提交
   auto-commit: true
   #连接最大存活时间,0表示永久存活,默认是30分钟
   max-lifetime: 1800000
   #连接超时时间,默认是30秒
   connection-timeout: 30000
   #测试连接是否可用的查询语句 
   connection-test-query: SELECt 1
   
   
   
   
#mybatis-plus的配置
mybatis-plus:
 #配置Mapper.xml文件
 mapper-locations: classpath*:/mapper
@Component
public class MD5Util {
    public static String md5(String src){
        return DigestUtils.md2Hex(src);
    }
    private static final String salt="1a2b3c4d";
    public static String inputPassToFromPass(String inputPass){
        String str=salt.charAt(0)+salt.charAt(2)+inputPass+salt.charAt(4);
        return md5(str);
    }
    public static String formPassToDBPass(String fromPass,String salt){
        String str=salt.charAt(0)+salt.charAt(2)+fromPass+salt.charAt(4);
        return md5(str);
    }
    public static String inputPassToDBPass(String inputPass,String salt){
        String fromPass=inputPassToFromPass(inputPass);
        String dbPass=formPassToDBPass(fromPass,salt);
        return dbPass;
    }
}
整理思路:
  1. 先是跳转到login页面的实现,然后使用的是mybatis-plus的逆向工程去实现userMapper、userService、user的生成等。
  2. 然后再controller中去接受前台发送的请求,其中接受请求的参数,可以定义一个实体类LoginVo去接受,返回的是调用的service层的业务逻辑方法。对于LginVo类中定义了主要是两个参数,一个是mobile和password这两个变量。除了定义接受参数的对象之外,还要定义一个返回数据的对象RespBean和公共返回对象的枚举类RespEnum

以下的RespBean的书写

@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {

    private long code;
    private String message;
    private  Object obj;

    
    public static RespBean success(){
        return new RespBean(RespBeanEnum.SUCCESS.getCode(),RespBeanEnum.SUCCESS.getMessage(),null);
    }

    public static RespBean success(Object obj){
        return new RespBean(RespBeanEnum.SUCCESS.getCode(),RespBeanEnum.SUCCESS.getMessage(),null);
    }

    
    public static RespBean error(){
        return new RespBean(RespBeanEnum.ERROR.getCode(),RespBeanEnum.ERROR.getMessage(),null);
    }

    public static RespBean error(Object obj){
        return new RespBean(RespBeanEnum.ERROR.getCode(),RespBeanEnum.ERROR.getMessage(),null);
    }

}

以下是枚举类的书写

@Getter
@ToString
@AllArgsConstructor
public enum  RespBeanEnum {
    SUCCESS(200,"SUCCESS"),
    ERROR(500,"服务器异常"),

    //登录模块
    LOGIN_ERROR(500210,"用户名或者密码错误!"),
    MOBILE_ERROR(500211,"手机格式不正确"),
    BIND_ERROR(500212,"参数效验异常");

    private final Integer code;
    private final String message;
}

  1. 在service层会去实现对数据的校验,对了一般的数据效验是写在controller层,主要看你自己的书写格式吧,在这个函数里面显示对数据的最后校验,一般是直接写业务逻辑,但是在公司里面,一般都是单独的去定义校验逻辑。
    以下是从传统的逻辑
       if(StringUtils.isEmpty(mobile)||StringUtils.isEmpty(password)){
            return RespBean.error(RespBeanEnum.LOGIN_ERROR);        }
        if(ValidatorUtil.isMobile(mobile)){
            return RespBean.error(RespBeanEnum.MOBILE_ERROR);
       }

以下的公司常用的书写格式

  • 先定义的手机号码校验的正则表达式
public class ValidatorUtil {
    private  static  final Pattern moblie_patter=Pattern.compile("[1]([3-9])[0-9]{9}$");

    public static boolean isMobile(String moblie){
        if(StringUtils.isEmpty(moblie)){
            return  false;
        }
        Matcher matcher=moblie_patter.matcher(moblie);
        return matcher.matches();
    }
}

  • 定义你的校验注解

@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[] payload() default {};


}
  • 再定义你的校验规则,实现ConstraintValidator接口,重写里面的两个方法
public class IsMobileValidator implements ConstraintValidator {
    private boolean required= false;
    @Override
    public void initialize(IsMobile constraintAnnotation) {
        required=constraintAnnotation.required();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        if(required){
            return ValidatorUtil.isMobile(value);
        }else {
            if (StringUtils.isEmpty(value)){
                return true;
            }else{
                return ValidatorUtil.isMobile(value);
            }
        }
    }
}
  • 在service层做完校验之后,调用mapper去查询数据,对查出的数据做处理,如果用户为空的话,就抛出一个自定义的异常,如果密码错误的话,也抛出一个自定义的异常。
    以下是自定义的异常类

  • 先去定义一个全局异常类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GlobalExecption extends RuntimeException {

    private RespBeanEnum respBeanEnum;

}

  • 再定义一个处理全局异常的类
@RestControllerAdvice
public class GlobalExecptionHandler   {

    @ExceptionHandler(Exception.class)
    public RespBean ExecptionHandler(Exception e){
        if(e instanceof GlobalExecption){
            GlobalExecption globalExecption=(GlobalExecption)e;
            return RespBean.error(globalExecption.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;
        }

        return RespBean.error(RespBeanEnum.ERROR);
    }

}

优化部分Cookie
  1. 先定义cookieUtil类和UUIDUtil类
  2. 在loginController中去定义session,随机生成一个UUid,放到user的session中,在跳转到商品页面的时候,就可以获取到user对象了。
分布式session问题

当项目做了负载均衡以后,如果在session中存了数据,那么就有可能有有些项目取不到session中的数据,这就是分布式session问题。

解决方案
  1. session复制
    优点:不需要修改代码,只需要修改tomcat服务器
    缺点:session同步传输占用内网宽带、多台tomcat同步性能质数下降、session占用内存,无法有效水平扩展
  2. 前端存储
    优点:不占用服务器的内存
    缺点:存在安全问题、数据大小受到cookie的限制、占用外网宽带
  3. session粘滞
    优点:无需修改代码、服务器可以水平扩展
    缺点:增加新的机器,会重新Hash,导致重新登录、需要重新登录
  4. 后端集中存储
    优点:安全、容易水平扩展
    缺点:增加复杂度、需要修改代码
总结

这只是一个登录功能,对于MD5二次加密,一直都有一个问题,就是前台向后台传递过来一个进行过一次加盐的密码字符串,后台需要二次加盐操作与数据库中的数据作比较,密码不对的话,要抛出异常显示密码错误,但是我的一直显示的是服务器错误,我也一直没有弄懂是为什么,如果有大佬看到我的话,麻烦给予我批评指正。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/872432.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号