抢购秒杀的业务逻辑 redis实现 nginx限流算法

1.秒杀的业务逻辑

  • 秒杀会出现高并发的情况,关系型数据库并发能力较弱,高并发会导致数据库崩溃

  • 使用非关系型数据库

  • 在网关这一块,使用nginx进行负载均衡,保证访问可以被消化,在应用服务器端使用tomcat集群,唯一有问题的是在数据库这一端

    抢购秒杀(限时特惠)
    高并发
    限时
    限量
    实现抢购秒杀 1:服务端(网关、应用服务器)要进行负载均衡
    2: 尽可能的少用或者不用关系型数据库
    3:尽量的提高流程的体验(页面静态化等)

​ 实现思路:基于redis来实现抢购秒杀(抢购秒杀的数据提前存到redis中)

  • redis中的数据格式
  • seckill:seq:active string类型 分期对象。
  • seckill:items:list:+分期id string类型 list
  • seckill:items:count:+商品id list类型
  • seckill:items:users:+商品id set集合

2.定时存放数据

SysProperties 存放系统当中的静态常量

package com.oracle.shop.constant;
//存放程序的公共常量
public class SysProperties {
    public static final String SECKILL = "seckill:seq:active";
    public static final String SECKILL_ITEMS_LIST = "seckill:items:list:";
    public static final String SECKILL_ITEM_COUNT = "seckill:items:count:";
    public static final String SECKILL_ITEMS_USERS="seckill:items:users:";
}

spring-task.xml 定时任务的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:task="http://www.springframework.org/schema/task"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                         http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">
	<!-- 开启spring task的注解支持-->
	<task:annotation-driven></task:annotation-driven>
</beans>

SeckillRedisDataInit 编写定时器,定时将数据放入redis当中

//定时任务将 秒杀商品信息初始化
@Component
@Lazy(false)
public class SeckillRedisDataInit {

    @Autowired
    private SeckillSeqService seckillSeqService;
    @Autowired
    private SeckillItemsService seckillItemsService;

    @Autowired
    private RedisTemplate redisTemplate;

    //每天定时刷新,更新分期秒杀 列表
    @Scheduled(cron = "1 1 1 * * ?")
    public void init(){
        //查询活跃的分期对象
        SeckillSeq seckillSeq = seckillSeqService.findActiveSeckillSeq();
        //放入redis中
        String key = SysProperties.SECKILL;
        redisTemplate.boundValueOps(key).set(seckillSeq);
        //查询该期秒杀活动的商品
        List<SeckillItems>seckillItems= seckillItemsService.findBySeqId(seckillSeq.getId());
        //将商品信息放入redis中
        key=SysProperties.SECKILL_ITEMS_LIST+seckillSeq.getId();
        redisTemplate.boundValueOps(key).set(seckillItems);
        //将每个商品的数量做成list放入redis中
        for(SeckillItems seckillItems1:seckillItems){
            key= SysProperties.SECKILL_ITEM_COUNT +seckillItems1.getId();
            for (int i=1;i<=seckillItems1.getSeckillCount();i++){
                redisTemplate.boundListOps(key).leftPush(i);
            }
        }
    }
}

控制器中 抢购秒杀的逻辑实现

//处理用户的 购买请求
    @RequestMapping("/kill")
    public @ResponseBody
    ResponseVo seckill(@Param("id") int id, HttpSession session){
        ResponseVo responseVo = new ResponseVo();
        //1.是否在秒杀期限内
        SeckillSeq seckillSeq =(SeckillSeq) redisTemplate.boundValueOps(SysProperties.SECKILL).get();
        Date now=new Date();
        if (now.before(seckillSeq.getStartDate())){
            //活动未开始
            responseVo.setCode("201");
        }else if (now.after(seckillSeq.getEndDate())){
            //活动以及结束
            responseVo.setCode("202");
        }else {
            User user = (User)session.getAttribute("user");
            if (redisTemplate.boundSetOps(SysProperties.SECKILL_ITEMS_USERS+id).isMember(user.getId())){
                //用户是否已经抢购过商品
                responseVo.setCode("203");
            }else {
                Object obj = null;
                if ((obj=redisTemplate.boundListOps(SysProperties.SECKILL_ITEM_COUNT+id).rightPop())!=null){
                    //抢购成功,将该用户放入 相应商品购买者的set集合当中
                    redisTemplate.boundSetOps(SysProperties.SECKILL_ITEMS_USERS+id).add(user.getId());
                    //抢购成功
                    responseVo.setCode("200");
                }else {
                    //商品买完了
                    responseVo.setCode("204");
                }
            }
        }
        return responseVo;
    }

3.限流算法

  • 令牌桶算法

img

算法思想是:

​ 令牌以固定速率产生,并缓存到令牌桶中;
​ 令牌桶放满时,多余的令牌被丢弃;
​ 请求要消耗等比例的令牌才能被处理;
​ 令牌不够时,请求被缓存。

  • 漏桶算法

在这里插入图片描述

  • 水(请求)从上方倒入水桶,从水桶下方流出(被处理);
    来不及流出的水存在水桶中(缓冲),以固定速率流出;
    水桶满后水溢出(丢弃)。
    这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃。
    相比漏桶算法,令牌桶算法不同之处在于它不但有一只“桶”,还有个队列,这个桶是用来存放令牌的,队列才是用来存放请求的。
posted @ 2021-11-06 12:58  2333gyh  阅读(693)  评论(0编辑  收藏  举报