秒杀系统个人总结

       对于高并发的项目,处理原则是尽量在上游处理,优先级:客户端-》后端内存-》redis-》数据库;如果能在客户端处理的,尽量不交给tomcat,能在tomcat处理的,尽量不要交给redis,毕竟tomcat-redis也存在网络交互,能在redis处理的,尽量不交给数据库,一般高并发的瓶颈都是在数据库。

      另外可以使用nignx做负载均衡,横向扩展处理服务器,这时session一般就要保存在redis,这个也是分布式session;可以使用rabbitMQ作为订单的的处理,rabbitMQ是分布式消息队列系统,需要安装,个人感觉什么分布式开源系统,原理都是统一处理多个来源信息,支持集群;

     在高并发环境下,可以限制用户某时间段内的访问次数,加验证码等手段减轻服务器压力;另外在高并发下也很容易出现数据错误的情况,例如这里的多个用户同时获得剩余商品都是1,然后就出现订单多于总商品的情况,对于这种情况,可以:1,在数据库表中合适的添加唯一值索引,2,利用rabbitMQ来延迟处理订单,rabbitMQ是通过队列的处理消息,成功进入队列的订单给用户返回排队中的提示信息,后面用户方面可以通过轮询的方式去查找数据库,如果数据库存在该用户的订单信息,即提示秒杀成功,如果数据库的商品为0且订单表不存在该用户信息,提示秒杀失败,否则提示排队中

1、页面静态化,页面缓存:

将页面的写成html,所有的数据都通过js异步获取,并且在页面中加入缓存功能,设置缓存时间,springboot提供缓存统一设置:

#static
spring.resources.add-mappings=true
spring.resources.cache-period= 3600
spring.resources.chain.cache=true
spring.resources.chain.enabled=true
spring.resources.chain.gzipped=true
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/

2、nignx负载均衡

  upstream server_pool_miaosha{
        server 127.0.0.1:8080 weight=1;
server 127.0.0.1:8081 weight=1;
    }
    server {
        listen       81;
        server_name  localhost;
   
        location / {
   proxy_pass http://server_pool_miaosha;

        }

3、分布式session 

,随机生成一个token作为key,当前用户user作为value保存在redis中,并且将该token保存在客户端的cookie中,对于客户端每次请求都会自动的将cookie信息传递到服务器,这时我们就可以通过CooKi_NAME-TOKEN获取token,进而去redis获取用户信息

redisService.set(MiaoshaUserKey.token, token, user);
Cookie cookie = new Cookie(COOKI_NAME_TOKEN, token);
cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds());
cookie.setPath("/");
response.addCookie(cookie);
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
    HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
    HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);

    String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN);
    String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN);
    if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
        return null;
    }
    String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
    return userService.getByToken(response, token);
}
private String getCookieValue(HttpServletRequest request, String cookiName) {
    Cookie[]  cookies = request.getCookies();
    if(cookies == null || cookies.length <=0){
        return null;
    }
    for(Cookie cookie : cookies) {
        if(cookie.getName().equals(cookiName)) {
            return cookie.getValue();
        }
    }
    return null;
}
4、隐藏秒杀地址

主要是通过客户端在调用秒杀接口前要先到服务器获取一个随机数,这个随机数会保存在redis中,然后在访问地址中带上这个随机数

@RequestMapping(value="/{path}/do_miaosha", method= RequestMethod.POST)
@ResponseBody
public Result<Integer> miaosha(Model model, MiaoshaUser user,
                         @RequestParam("goodsId")long goodsId,
                        @PathVariable("path") String path) {

客户端调用秒杀接口前:

$.ajax({
    url:"/miaosha/path",
    type:"GET",
    data:{
        goodsId:goodsId,
        verifyCode:$("#verifyCode").val()
    },
    success:function(data){
        if(data.code == 0){
            var path = data.data;
            console.info("path:"+path);
            doMiaosha(path);
        }else{
            layer.msg(data.msg);
        }
    },

客户端调用秒杀接口:

$.ajax({
   url:"/miaosha/"+path+"/do_miaosha",
   type:"POST",
   data:{
      goodsId:$("#goodsId").val(),
   },
   success:function(data){
      if(data.code == 0){
               getMiaoshaResult($("#goodsId").val());
         //window.location.href="/order_detail.htm?orderId="+data.data.id;
      }else{
         layer.msg(data.msg);
      }
   },

秒杀系统个人总结这个即能达到隐藏接口的功能

5、接口限流:

秒杀系统个人总结

public class AccessInterceptor  extends HandlerInterceptorAdapter{
   
   @Autowired
   MiaoshaUserService userService;
   
   @Autowired
   RedisService redisService;
   
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {
      if(handler instanceof HandlerMethod) {
         MiaoshaUser user = getUser(request, response);
         UserContext.setUser(user);
         HandlerMethod hm = (HandlerMethod)handler;
         AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
         if(accessLimit == null) {
            return true;
         }
         int seconds = accessLimit.seconds();
         int maxCount = accessLimit.maxCount();
         boolean needLogin = accessLimit.needLogin();
         String key = request.getRequestURI();

通过自定义注解,添加拦截器,redis记录通过设置超时时间来限制该用户的某时间段内的访问次数

6、jmeter工具压测

一个完整的jmeter压测包括线程组,HTTP请求默认值,HTTP请求,CSV Data Set Config ,聚合报告

秒杀系统个人总结秒杀系统个人总结秒杀系统个人总结秒杀系统个人总结秒杀系统个人总结