您当前的位置: 首页 > 

杨林伟

暂无认证

  • 0浏览

    0关注

    3337博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

淘东电商项目(76) -秒杀系统(完整代码实现)

杨林伟 发布时间:2020-05-26 11:35:59 ,浏览量:0

引言

本文代码已提交至Github(版本号:2c985822b282756e3fd70490cb0ba6f4f2140e47),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop

秒杀系统在前面已经讲解了“前端优化”以及“防止库存超卖”的功能,但是在效率这一块还是很慢的,那么后台的秒杀完整代码流程是如何的呢?本文来讲解下,阅读前,童鞋们可以先阅读之前写的博客:

  • 《淘东电商项目(73) -秒杀系统(前端优化)》
  • 《淘东电商项目(74) -秒杀系统(库存超卖解决方案》
  • 《淘东电商项目(75) -秒杀系统(用户操作频率限制)》

本文目录结构: l____引言 l____ 1.秒杀原理图 l____ 2. 后台核心代码 l________ 2.1 令牌桶生成接口 l________ 2.2 秒杀接口(核心) l________________ 2.2.1 MQ配置 l________________ 2.2.2 生产者 l________________ 2.2.3 消费者 l________ 2.3 用户查询接口 l____ 3. 测试

1.秒杀原理图

下面贴上我自己整理的原理图,如下:

在这里插入图片描述 从原理图,可以看到秒杀的流程大致如下:

  1. 商户添加秒杀商品的时候,后台会自动从Redis里生成令牌桶,如商品A的库存有100个,那么当用户修改商品时会去Redis里添加一条数据,格式:商品id+List令牌桶(数量是库存数量)。
  2. 用户抢购时,会从令牌桶里获取令牌,如果能获取成功,则通过MQ去异步修改数据库里面的订单表以及秒杀表。
  3. 抢购完成后,会提示用户“正在排队中…”,用户需要自己主动的去查询抢购结果。
2. 后台核心代码 2.1 令牌桶生成接口

令牌桶生成接口核心代码:

@Override
public BaseResponse addSpikeToken(Long seckillId, Long tokenQuantity) {
	// 1.验证参数
	if (seckillId == null) {
		return setResultError("商品库存id不能为空!");
	}
	if (tokenQuantity == null) {
		return setResultError("token数量不能为空!");
	}
	SeckillEntity seckillEntity = seckillMapper.findBySeckillId(seckillId);
	if (seckillEntity == null) {
		return setResultError("商品信息不存在!");
	}
	// 2.使用多线程异步生产令牌
	createSeckillToken(seckillId, tokenQuantity);
	return setResultSuccess("令牌正在生成中.....");
}

@Async
public void createSeckillToken(Long seckillId, Long tokenQuantity) {
	generateToken.createListToken("seckill_", seckillId + "", tokenQuantity);
}

Redis令牌桶生成工具类:

①GenerateToken

public void createListToken(String keyPrefix, String redisKey, Long tokenQuantity) {
    List listToken = getListToken(keyPrefix, tokenQuantity);
    redisUtil.setList(redisKey, listToken);
}

public List getListToken(String keyPrefix, Long tokenQuantity) {
    List listToken = new ArrayList();
    for (int i = 0; i  0 ? true : false;
    }

    // 消费者获取到消息之后 手动签收 通知MQ删除该消息
    private void basicNack(Message message, Channel channel) throws IOException {
        channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
    }
}
2.3 用户查询接口
@RestController
public class OrderSeckillServiceImpl extends BaseApiService implements OrderSeckillService {
	@Autowired
	private OrderMapper orderMapper;

	@Override
	public BaseResponse getOrder(String phone, Long seckillId) {
		if (StringUtils.isEmpty(phone)) {
			return setResultError("手机号码不能为空!");
		}
		if (seckillId == null) {
			return setResultError("商品库存id不能为空!");
		}
		OrderEntity orderEntity = orderMapper.findByOrder(phone, seckillId);
		if (orderEntity == null) {
			return setResultError("正在排队中.....");
		}
		return setResultSuccess("恭喜你秒杀成功!");
	}
}
3. 测试

①模拟用户修改商品库存,更新令牌桶,浏览器访问:http://localhost:9800/addSpikeToken?seckillId=100001&tokenQuantity=100 在这里插入图片描述 可以看到Redis里生成商品key id为100001,值为list,大小为100的集合: 在这里插入图片描述 ②模拟抢购,浏览器访问:http://localhost:9800/spike?phone=13800000001&seckillId=100001 在这里插入图片描述 可以看到数据库库存减一: 在这里插入图片描述 订单并生成了一条记录: 在这里插入图片描述 Redis减少了一个令牌: 在这里插入图片描述 ③模拟用户查询抢购结果,浏览器访问: 在这里插入图片描述

关注
打赏
1662376985
查看更多评论
立即登录/注册

微信扫码登录

0.2559s