所有分类
  • 所有分类
  • 未分类

一个注解实现Redisson分布式锁

简介

本文注解组件的作用:方法上加个注解就能使用Redisson分布式锁

为什么使用分布式锁

为了防止重复执行。以退款为例,每笔订单只能退一次款。代码逻辑:先判断数据库是否已经退款,如果还没退款则允许执行,执行结束后将退款记录到数据库内。

假设此时用户点击了两次,项目使用提交读或者重复读,在第一个请求还没执行结束时,第二个请求进来了,由于第一个请求的数据库事务还没提交,所以第二个请求还查不到第一个请求的退款记录,所以第二个请求也执行了。最终退了两次款!

解决方案就是:使用分布式锁,通过Redis来记录。如果Redis中还没有,表示还没有请求,将数据写到Redis,然后执行代码,执行结束后删除Redis数据。

本文注解支持的功能

  1. 可以指定要加锁的字段
  2. 可以指定获取锁的超时时间
  3. 可以指定获取锁失败时的提示信息
  4. 可以指定获取锁失败时抛出的异常

注意:本组件是对Redisson的封装,Redisson的分布式锁是最完美的,对它进行封装即可如虎添翼!

虽然网上有开源的了,比如:lock4j,但它有bug,执行完后不释放锁,根本没法用!

Java初级和高级的做法

Java初级开发的做法

Java初级开发者是这样使用Redisson分布式锁的:

  1. 手动加锁
  2. 将原逻辑放到获取锁成功后的代码块内
    1. 一般锁是在@Transactional之前执行,所以要将原逻辑封装到一个方法中,然后在锁获取成功后调用它
  3. 逻辑执行完后要释放锁

Java高级开发的做法

Java高级开发一眼就能看出问题了:这都是重复性的东西,提取出公共组件,让AOP去处理,原逻辑上边只加个注解即可。

效果展示

Controller

本处为了简单,直接放到了Controller上,实际应该放到Service上。

package com.knife.example.business.product.controller;

import com.knife.example.business.product.bo.ProductSaveBO;
import com.knife.example.business.product.bo.UserBO;
import com.knife.example.common.annotation.DistributionLock;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

@Api(tags = "商品")
@RestController
@RequestMapping("product")
public class ProductController {

    @ApiOperation("新增")
    @DistributionLock(keys = {"#productSaveBO.id", "#productSaveBO.name"})
    @PostMapping("add")
    public String add(@RequestBody ProductSaveBO productSaveBO) {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "success";
    }

    @ApiOperation("编辑")
    // 支持多个入参
    @DistributionLock(keys = {"#productSaveBO.id", "#productSaveBO.name"})
    @PostMapping("edit")
    public String edit(@RequestBody ProductSaveBO productSaveBO,
                       UserBO userBO) {

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        return "success";
    }
}

测试

访问:http://localhost:8080/doc.html(打开两个页面)

两个页面都输入入参,点击完一个页面的“发送”后,快速点击另一个页面的“发送”,模拟并发场景。

结果

第1个页面:(获取到锁,正在执行)

第2个页面:(获取锁失败,报错)

后端结果:

等第一个页面执行结束:(执行成功,释放锁)

Redis里的数据

组件自动加锁时,往Redis里写的数据是这样的:

锁释放后,Redis这个key就没了:

代码

此内容仅限VIP查看,请先
1

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录