基于ZooKeeper的分布式锁——抢购书本

目录

    • 简要
    • config层
    • controller层
    • dto层
    • mapper和model层
    • service层
    • util
    • 过程

简要

记录一个小项目,通过ZooKeeper加锁实现并发控制。

config层

@Configuration
public class CuratorConfig {
    @Autowired
    private Environment env;

    @Bean
    public CuratorFramework curatorFramework() {
        CuratorFramework curatorFramework =
                CuratorFrameworkFactory.builder().
                        connectString(env.getProperty("zk.host")).
                        namespace(env.getProperty("zk.namespace")).
                        retryPolicy(new RetryNTimes(5, 1000)).build();
        curatorFramework.start();
        return curatorFramework;
    }

}

controller层

@RestController
public class BookRobController {

    //定义日志
    private static final Logger log= LoggerFactory.getLogger(BookRobController.class);
    //定义请求前缀
    private static final String prefix="book/rob";

    //定义核心逻辑处理器服务类 不加锁
    @Autowired
    private BookRobService bookRobService;


    @RequestMapping(value=prefix+"/request",method= RequestMethod.GET)
    public BaseResponse takeMoney(BookRobDto dto){
        //校验参数的合法性
        if(Strings.isNullOrEmpty(dto.getBookNo())|| dto.getUserId()==null||dto.getUserId()<=0){
            return new BaseResponse(StatusCode.InvalidParams);

        }
        BaseResponse baseResponse=new BaseResponse(StatusCode.Success);
        try {
            bookRobService.robWithZKLock(dto);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return baseResponse;
    }
}

dto层

@Data
@ToString
public class BookRobDto implements Serializable {
    private Integer userId;

    private String bookNo;
}

mapper和model层

使用mybatis生成代码

service层

@Service
public class BookRobService {

    //定义日志实例
    private static final Logger log = LoggerFactory.getLogger(BookRobService.class);
    //定义书籍库存实体操作接口Mapper实例
    @Autowired
    private BookStockMapper bookStockMapper;
    //定义书籍抢购实体操作接口Mapper实例
    @Autowired
    private BookRobMapper bookRobMapper;

    //定义Zookeeper客户端CuratorFramework实例
    @Autowired
    private CuratorFramework client;
    //Zookeeper分布式锁的实现原则是由ZNode节点的创建,删除与监听器构成的
    //而ZNode节点将对应一个具体的路径-根Unix文件路径类似-需要以/开头
    private static final String pathPrefix = "/zkLock/";


    /**
     * 处理书籍抢购逻辑-加Zookeeper分布式锁
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    public void robWithZKLock(BookRobDto dto) throws Exception {
        InterProcessMutex interProcessMutex = new InterProcessMutex(client, pathPrefix + dto.getBookNo() + dto.getUserId() + "---lock");
        //获取锁
        try {
            if (interProcessMutex.acquire(20, TimeUnit.SECONDS)) {

                //看此书是否还存在
                BookStock bs = bookStockMapper.selectByBookNo(dto.getBookNo());
                //此人是否已经抢过了
                int total = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo());
                if (bs != null && bs.getStock() > 0 && total <= 0) {
                    int res = bookStockMapper.updateStock(dto.getBookNo());
                    if (res > 0) {
                        //增加用户抢购记录
                        BookRob rob = new BookRob();

                        BeanUtils.copyProperties(dto, rob);

                        rob.setRobTime(new Date());

                        bookRobMapper.insertSelective(rob);
                    }
                }
            } else {
                log.error("--------获取Zookeeper分布式锁失败!!");
                throw new RuntimeException("获取Zookeeper分布式锁失败!");

            }
        } catch (Exception e) {
            log.error("--------获取Zookeeper分布式锁失败!!{}----------", e.getMessage());
        } finally {
            //不管发生何种情况 在处理完核心业务逻辑之后,需要释放该分布式锁
            interProcessMutex.release();
        }

    }
}

util

public class BaseResponse<T> {
    //状态码
    private Integer code;
    //描述信息
    private String msg;
    //响应数据-采用泛型表示可以接受通用的数据类型
    private T data;
    //重载的构造方法一
    public BaseResponse(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    //重载的构造方法二
    public BaseResponse(StatusCode statusCode) {
        this.code = statusCode.getCode();
        this.msg = statusCode.getMsg();
    }
    //重载的构造方法三
    public BaseResponse(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
public enum StatusCode {
    //以下是暂时设定的几种状态码类
    Success(0,"成功"),
    Fail(-1,"失败"),
    InvalidParams(201,"非法的参数!"),
    InvalidGrantType(202,"非法的授权类型");

    //状态码
    private Integer code;
    //描述信息
    private String msg;
    //重载的构造方法
    StatusCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

过程

  1. 开启ZooKeeper
    List item
  2. 开启spring
    基于ZooKeeper的分布式锁——抢购书本_第1张图片
  3. 使用jmeter高并发请求
    基于ZooKeeper的分布式锁——抢购书本_第2张图片
    基于ZooKeeper的分布式锁——抢购书本_第3张图片
    user.csv文件内容:
    基于ZooKeeper的分布式锁——抢购书本_第4张图片
    现在6个人抢购10本书。
    未加锁:
    在这里插入图片描述
    加锁:
    在这里插入图片描述

你可能感兴趣的:(ZooKeeper)