基于mysql的分布式锁_基于MySQL实现分布式锁

1、分布式锁服务端

单独启动一个应用,用来提供分布式锁,实现锁的关闭和开启。相当于redis的服务器

1.1、创建锁表

DROP TABLE IF EXISTS `qrtz_distribute_lock`;

CREATE TABLE `qrtz_distribute_lock` (

`id` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '主键',

`key` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '锁名称',

`val` tinyint(255) NULL DEFAULT 1 COMMENT '值',

`remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '描述',

`update` timestamp(0) NULL DEFAULT NULL COMMENT '更新时间',

PRIMARY KEY (`id`) USING BTREE,

UNIQUE INDEX `KEY_INDEX`(`key`) USING BTREE

) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

数据库表如下图所示:

921d76a74153917fee497e244eba998b.png

其中key自行实现唯一性,数据库不做约束

1.2、锁的查询和删除

@Repository

public interface DistributeLockDao extends JpaRepository{

/** 根据key查询锁 **/

DistributeLock findByKey(String key);

/** 根据key删除锁 **/

void deleteByKey(String key);

}

1.3、加锁和解锁

@Service

public class DistributeLockService {

@Autowired

DistributeLockDao lockDao;

@Transactional

public synchronized Boolean lock(DistributeLock lock) {

DistributeLock locked = lockDao.findByKey(lock.getKey());

if(locked == null) { //没有锁,新增锁

lock.setUpdate(new Date());

lock.setVal(true);

lock = lockDao.saveAndFlush(lock);

return true;

}

if(locked.getVal()) { //有锁,且已锁,不可用

locked = new DistributeLock();

locked.setVal(false);

return false;

}

// 有锁,可用

locked.setVal(true);

locked.setUpdate(new Date());

locked = lockDao.saveAndFlush(locked);

return true;

}

/**

* 取消分布式锁

* @return

*/

@Transactional

public synchronized Boolean unLock(DistributeLock lock) {

lockDao.deleteByKey(lock.getKey());

lock.setVal(false);

return true;

}

}

1.4、提供取锁和解锁的接口

@RestController

public class DistributeLockController {

@Autowired

DistributeLockService lockService;

@RequestMapping("lock")

public Boolean lock(@Validated DistributeLock lock) {

return lockService.lock(lock);

}

@RequestMapping("unLock")

public Boolean unLock(@Validated DistributeLock lock) {

return lockService.unLock(lock);

}

}

2、分布式锁客户端

2.1、切点注解

@Documented

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Lock {

/**

* 保证唯一性,默认是全限定类名 + 方法全称(含参数列表)

* @return

*/

String key() default "";

String describe() default "";

/**

* 获取锁超时时间

* @return

*/

int timeout() default 5000;

}

数据库未解锁前的数据:

de21c9dcbcf1f059d6737339faad738e.png

2.1、实现分布式锁切面

@Aspect

@Component

public class DistributeLockAspect {

/** 分布式锁服务端的ip和端口, 服务端contextPath若不为‘distribute’,则需要修改 **/

public static final String DISTRIBUTE_SERVER_ADDRESS = "localhost:9876";

private static final String userAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3409.0 Safari/537.36";

private static final CloseableHttpClient httpclient = HttpClients.createDefault();

/**

* 线程池

*/

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(20);

/**

* 存放key

*/

ThreadLocalkeys = new ThreadLocal<>();

// order = 1

@Before(value = "@annotation(Lock)")

public void doBefore(JoinPoint joinPoint) throws InterruptedException, ExecutionException, TimeoutException {

MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

Method method = methodSignature.getMethod();

String key = null, describe = null;

int timeout = 0;

if (method != null) {

Lock lock = method.getAnnotation(Lock.class);

describe = lock.describe();

timeout = lock.timeout();

key = lock.key();

}

keys.set(getLockKey(key, joinPoint));

String remark = describe;

String lockKey = keys.get();

while (true) {

Futurefuture = executor.submit(() -> {

// 在此处使用keys.get(), key可能会为null

String result = sendGet("http://" + DISTRIBUTE_SERVER_ADDRESS + "/distribute/lock?key=" + lockKey + "&remark=" + remark);;

if(StringUtils.equals(result, "true") || StringUtils.equals(result, "false")) {

return Boolean.parseBoolean(result);

}

return null;

});

Boolean flag = future.get(timeout, TimeUnit.MILLISECONDS);

if(flag == null) {

throw new IllegalArgumentException("network exception");

}

if(flag != null) {

if(future.get()) {

break;

} else {

continue;

}

}

}

}

// order = 2

@Around(value = "@annotation(Lock)")

public Object around(ProceedingJoinPoint proceedingJoinPoint){

try {

Object ret= proceedingJoinPoint.proceed();

return ret;

} catch (Throwable throwable) {

throwable.printStackTrace();

}

return null;

}

// order = 3

@After(value = "@annotation(Lock)")

public void after(JoinPoint joinPoint){

}

// order = 4

@AfterReturning(pointcut = "@annotation(Lock)",returning = "ret")

public void doAfterReturning(Object ret) {

sendGet("http://" + DISTRIBUTE_SERVER_ADDRESS + "/distribute/unLock?key=" + keys.get());

keys.remove();

}

// order = 方法抛出异常时

@AfterThrowing(pointcut = "@annotation(Lock)",throwing = "ex")

public void AfterThrowing(JoinPoint joinPoint,Throwable ex){

MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

Method method = methodSignature.getMethod();

sendGet("http://" + DISTRIBUTE_SERVER_ADDRESS + "/distribute/unLock?key=" + keys.get());

keys.remove();

}

/**

* 分布式锁的key, 默认由全限定类名 + 具体方法(含参数)

* @param key

* @param joinPoint

* @return

*/

private String getLockKey(String key, JoinPoint joinPoint) {

if(StringUtils.isNotBlank(key)) {

return key;

}

MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

Method method = methodSignature.getMethod();

String[] names = StringUtils.split(method.toString(), " ");

for(String name : names) {

if(StringUtils.indexOf(name, "(") > -1 && StringUtils.indexOf(name, ")") > -1) {

return name;

}

}

return null;

}

private String sendGet(String url) {

String result = null;

HttpGet httpGet = new HttpGet(url);

httpGet.setHeader("User-Agent", userAgent);

try(CloseableHttpResponse response = httpclient.execute(httpGet)) {

HttpEntity entity = response.getEntity();

if (entity != null) {

result = EntityUtils.toString(entity);

}

} catch (Exception e) {

e.printStackTrace();

}

return result;

}

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值