应用springboot和redis写的秒杀系统

应用springboot和redis写的秒杀系统,第1张

应用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/*Mapper.xml
 #配置mybatis数据返回类型别名(默认别名是类名)
 type-aliases-package: com.shmily.seckill.pojo
 
 
#mybatis sql打印(方法接口所在的包,不是mapper.xml所在包)
logging:
 level:
   com.shmily.seckill.mapper: debug
  1. 创建数据库
第一个数据表
CREATE TABLE t_user(
`id`BIGINT(20) NOT NULL COMMENT'用户ID手机号码',
`nickname`VARCHAR(255) NOT NULL,
`password`VARCHAR(255) DEFAULT NULL COMMENT 'MD5(MD5(pass明文+固定salt)+salt)',
`salt` VARCHAR(10) DEFAULT NULL,
`head` VARCHAR(128) DEFAULT NULL COMMENT '头像',
`register_date` datetime DEFAULT NULL COMMENT '注册时间',
`last_login_date` datetime DEFAULT NULL COMMENT '最后一次登录时间',
`login_count` int(11) DEFAULT '0' COMMENT '登录的次数',
PRIMARY KEY(`id`)
)
二、分布式session 1.用户登录功能 MD5的加密:
package com.shmily.seckill.utils;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
/**
 1. MD5加密
 */
@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;

    /**
     * 成功返回对象
     * @return
     */
    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);
    }

    /**
     * 失败返回结果
     * @return
     */
    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<? extends Payload>[] payload() default {};


}
  • 再定义你的校验规则,实现ConstraintValidator接口,重写里面的两个方法
/**
 - 手机号码效验规则
 */
public class IsMobileValidator implements ConstraintValidator<IsMobile ,String> {
    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二次加密,一直都有一个问题,就是前台向后台传递过来一个进行过一次加盐的密码字符串,后台需要二次加盐 *** 作与数据库中的数据作比较,密码不对的话,要抛出异常显示密码错误,但是我的一直显示的是服务器错误,我也一直没有弄懂是为什么,如果有大佬看到我的话,麻烦给予我批评指正。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/889891.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-14
下一篇 2022-05-14

发表评论

登录后才能评论

评论列表(0条)

保存