秒杀java代码 - 简单版本v2

v1版本的缺点是:

    如某商品有100库存,但是有10000人抢购的话就会有1w个请求去数据库进行查询库存(效率低且数据库可能宕机)。

解决:

    1. 在后台提前生成好商品对应的库存数量保存到redis中,采用list命令

    2. 用户点击抢购接口后,先去redis中查询是否能获取到数据,获取到数据就代表抢到了商品继续走流程代码,反之代表商品已售完。

v2版本秒杀实现步骤:

· 后台管理

  1. 提供一个接口:把商品库存数量放入redis中。

· 抢购接口:

  1. 限制同一个用户3秒内只能抢一次
  2. 从redis中读取库存商品是否还存在
  3. 将用户抢购成功信息存入redis
  4. 异步-减库存
  5. 异步-添加用户抢购成功记录表

· 查询用户是否抢购成功接口

具体流程:

1. 在后台把商品库存保存到redis中,采用list命令(获取特性:从左获取并移除),有多少库存就for循环多少次添加到list中,保存格式:(库存id,for循环中的自增id),
2. 用户请求抢购接口
2.1 使用redis的setnx命令限制用户5秒内只能请求一次
2.2 获取redis中提前存好的库存,能获取到就代表抢购成功,执行以下流程,反之返回该商品已售完。
2.3 去数据库修改库存数量,采用乐观锁防止库存<0,sql:update 库存数量 set 库存数量 = 库存数量 - 1 where 库存id = #{库存id} and 库存数量 > 0
2.4 修改库存成功后,异步保存当前'用户id'及'库存id'保存到用户抢购成功记录表中,
2.5 前端定时实时ajax请求后台“查询用户抢购成功记录表接口”是否抢购成功。

注意:

v1版本和当前版本的最后一步前端采用定时实时查询当前用户是否抢购成功这一功能是为了最终版本做准备,v1和当前版本可忽略这一步,直接返回抢购成功、失败即可。

在v1版本基础上进行修改

1. 后台生成商品库存到redis:http://127.0.0.1:81/addInventoryToRedis?inventoryId=110&quantity=100
2. 抢购接口:http://127.0.0.1:81/subtractInventory?phone=18713901666&inventoryId=110
3. 查询是否抢购成功:http://127.0.0.1:81/getInventoryLog?phone=18713901666&inventoryId=110

1. 添加生成库存到redis中的接口:

	/**
       http://127.0.0.1:81/addInventoryToRedis?inventoryId=110&quantity=100
	 * 提前在后台管理中把库存数量放入redis中,采用list命令(特性从左获取并移除)
	 * @param inventoryId 库存id
	 * @param quantity 库存数量
	 * @return
	 */
	@RequestMapping("addInventoryToRedis")
	public String addInventoryToRedis(Integer inventoryId,int quantity){
		for (int i = 1; i <= quantity; i++) {
			redisTemplate.opsForList().leftPush(inventoryId+"", i);
		}
		// 从左获取,并删除
		// Object leftPop = redisTemplate.opsForList().leftPop(inventoryId+"");
		// System.out.println(leftPop);
		return "ok";
	}

2. 业务流程代码:

package com.chuangqi.defense.controller;

import java.util.Date;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.chuangqi.defense.mapper.Test_01Mapper;
import lombok.extern.log4j.Log4j2;

/**
 1.抢购:http://127.0.0.1:81/subtractInventory?phone=18713901060&inventoryId=110
 2.查询是否抢购成功:http://127.0.0.1:81/getInventoryLog?phone=18713901060&inventoryId=110
 *
 * @author qizhentao
 * @version V1.0
 */
@Log4j2
@RestController
public class Test_01 {

	@Resource
	private RedisTemplate<String, Object> redisTemplate;
	
	@Autowired
	private Test_01Mapper mapper;
	
    /**
	 * 抢购接口
	 *  1.减库存
	 *  2.记录抢购成功用户
	 * @param inventoryId
	 * @param phone
	 * @return
	 */
	@RequestMapping("subtractInventory")
	public String add(Integer inventoryId, String phone){
		// 1.限制同一用户5秒内只能请求一次,使用setNX命令:已存在返回false,不存在就保存并返回true,超时时间5000毫秒
		Boolean setIfAbsent = redisTemplate.opsForValue().setIfAbsent(phone, inventoryId, 5000, TimeUnit.MILLISECONDS);
		if(!setIfAbsent){
			return "操作频繁,请稍后再尝试抢购...";
		}

            // 2. 从redis获取令牌,能获取到数据就代表抢到了商品从而执行以下流程,否则返回售无。
		Object leftPop = redisTemplate.opsForList().leftPop(inventoryId+"");
		if(leftPop == null){
			log.info("未抢到{}该商品的用户有{}个", inventoryId, ++not);
			return "该商品已售无...";
		}
		
		// 3.减库存
		int i = mapper.subtractInventory(inventoryId, phone);
		if(i == 1){
			// 4.异步添加用户抢购成功记录表
			addInventoryLog(inventoryId, phone);
			return "ok";
		}else{
			return "no";
		}
	}
	
	/**
	 * 减库存后,添加用户抢购成功记录表中
	 * 	1. 并且添加到redis中,
	 *  2. 在前台实时查询是否抢购成功的时候去查询redis即可
	 * @param inventoryId
	 * @param phone
	 * @return
	 */
	@Async
	public boolean addInventoryLog(int inventoryId, String phone){
		// 1.添加数据到数据库
		int i = mapper.addInventoryLog(inventoryId, phone, new Date());
		// 2.也可同步到redis中
		// redisTemplate.opsForValue().set(phone, inventoryId);
		return i == 1 ? true : false;
	}
	
	/**
	 * 前台定时ajax实时查询是否抢购成功
	 * @param inventoryId
	 * @param phone
	 * @return
	 */
	@RequestMapping("getInventoryLog")
	public String getInventoryLog(int inventoryId, String phone){
		// 查询数据库,也可改为查询redis
		Integer id = mapper.getInventoryLog(inventoryId, phone);
		// redisTemplate.opsForValue().get(inventoryId)
		if(id != null){
			return "恭喜抢购成功!";
		}else{
			return null;
		}
	}

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

祁_z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值