在实际的生产环境中,也确实可以看到分布式锁的身影,本节将以实际项目中典型业务场景“书籍抢购模块”为案例,巩固之前的知识
整体业务流程分析
上图中,不难发现,核心流程有三个
1.判读用户是否抢购过该商品
2.商品库存是否充足
3.更新书籍的库存并插入用户的抢购记录中
因此,又可以分成以下两大块
数据库设计
CREATE TABLE `book_stock` (
`id` int(11) NOT NULL,
`book_no` int(11) DEFAULT NULL COMMENT '书籍编号',
`stock` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '库存',
`isActive` smallint(2) DEFAULT NULL COMMENT '是否上架(1上,0不是)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE `book_rob` (
`id` int(8) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`user_id` int(8) DEFAULT NULL COMMENT '用户id',
`book_no` int(8) DEFAULT NULL COMMENT '书籍编号',
`rob_time` datetime DEFAULT NULL COMMENT '抢购时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
package com.learn.boot.controller;
import com.learn.boot.dto.BookRobDto;
import com.learn.boot.mapper.redis.book.BookRobMapper;
import com.learn.boot.mapper.redis.book.BookStockMapper;
import com.learn.boot.mapper.redis.log.SysLogMapper;
import com.learn.boot.mapper.test.AMapper;
import com.learn.boot.model.*;
import com.learn.boot.resultVo.ResultVo;
import com.learn.boot.service.DemoService;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* author: zlx
* date: 2020-1-1 17:17
* desc:
**/
@RestController
public class BookController {
private static final Logger log = LoggerFactory.getLogger(BookController.class);
@Autowired
private BookRobMapper bookRobMapper;
@Autowired
private BookStockMapper bookStockMapper;
/**
* 使用CuratorFramework实现分布式锁,在CuratorFrameworkConfig重写了
*/
@Autowired
private CuratorFramework curatorFramework;
@RequestMapping(value = "/bookRob",method = RequestMethod.POST)
@Transactional(rollbackFor = Exception.class)
public ResultVo main(@RequestBody BookRobDto dto, BindingResult bindingResult) {
try {
if (bindingResult.hasErrors()) {
return ResultVo.error("请求参数异常");
}
// 判断此书籍是否能够被抢购
BookStock bookStock = bookStockMapper.selectBookRobByBookNo(dto.getBookNo());
if (bookStock == null || Integer.valueOf(bookStock.getStock()) <= 0) {
return ResultVo.error("库存不足");
}
// 先查询用户是否抢购过此书籍
Integer buyedTotal = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo());
if (buyedTotal == null) {
return ResultVo.error("您已抢购过该书籍");
}
// 抢购成功,更新库存
String lockName = "/local/book";
InterProcessSemaphoreMutex lock = new InterProcessSemaphoreMutex (curatorFramework, lockName);
if (lock.acquire(15L, TimeUnit.SECONDS)) {
log.info("开始抢购");
bookStock.setStock(String.valueOf(Integer.valueOf(bookStock.getStock()) - 1));
int res = bookStockMapper.updateByPrimaryKey(bookStock);
log.info("更新库存完成");
if (res > 0 ) {
// 创建书籍抢购记录实体信息
BookRob entity=new BookRob();
// 从提交的用户抢购请求实体信息中对应的字段取值
// 复制到新创建的书籍抢购记录实体的相应字段中
BeanUtils.copyProperties(dto,entity);
// 设置抢购时间
entity.setRobTime(new Date());
// 插入用户注册信息
bookRobMapper.insertSelective(entity);
log.info("插入抢购记录成功");
}
return ResultVo.success("抢购成功");
}
return ResultVo.error("抢购失败");
}catch (Exception ex){
log.info("异常1",ex);
return ResultVo.error();
}
}
}