分布式锁的实现方式 |
您所在的位置:网站首页 › 使用mysql实现分布式锁定端口的方法有哪些优点 › 分布式锁的实现方式 |
一、分布式锁应该具备哪些条件 1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行; 2、高可用高性能的获取锁和释放锁; 3、具备可重入特性; 4、具备锁失效机制,防止死锁; 5、具备非阻塞锁特性,没有获取到锁就直接返回获取锁失败。 二、分布式锁的实现方式 在很多场景中,我们为了保证数据的最终一致性,需要使用分布式事务,分布式锁等。我们需要保证一个方法在同一时间只能被同一个线程执行。 基于数据库实现分布式锁; 基于缓存(Redis)实现分布式锁; 基于Zookeeper实现分布式锁。 不同的业务要根据自己的情况进行选型,选择合适的方案。 三 、基于数据库的分布式锁 在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上加唯一索引,想要执行这个方法,就使用这个方法名向表中插入数据,成功插入则获得锁,执行完成后删除对应数据释放锁。 (1)创建一个表: lock_name字段作为唯一性索引 CREATE TABLE `cluster_lock` ( `lock_name` varchar(255) CHARACTER SET latin1 NOT NULL, `ttl` int(11) NOT NULL, `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY `lock_name_unique` (`lock_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin 获取锁,释放锁的方法 @Service public class LockServiceImpl implements LockService { @Autowired private LockMapper lockMapper; public static final int THREE_SECOND_MILLISECOND = 3 * 1000; private void deleteLock(String lockName) { ClusterLockDO clusterLockDO = clusterLockMapper.selectByName(lockName); if (clusterLockDO != null) { //即使主动释放锁,也必须要锁3秒钟以上 if ((new Date().getTime() - clusterLockDO.getGmtCreate().getTime()) > THREE_SECOND_MILLISECOND) { //删除锁 lockMapper.deleteByLockName(lockName); } else { //更新锁 (时间) lockMapper.updateTtlByLockName(lockName, THREE_SECOND_MILLISECOND); } } } public boolean getLock(String lockName, int ttl) { Preconditions.checkNotNull(lockName, "lockName"); Preconditions.checkArgument(ttl > 0, "ttl必须大于0"); ClusterLockDO clusterLockDO = lockMapper.selectByName(lockName); if (clusterLockDO != null) { //锁已经超时了 if ((new Date().getTime() - clusterLockDO.getGmtCreate().getTime()) > clusterLockDO.getTtl()) { //删除锁 deleteLock(lockName); } } ClusterLockDO tryInsert = new ClusterLockDO(); tryInsert.setLockName(lockName); tryInsert.setTtl(ttl); boolean holdLock; try { clusterLockMapper.insertLock(tryInsert); //插入锁成功 holdLock = true; } catch (DuplicateKeyException duplicateKeyException) { holdLock = false; } catch (Exception e) { holdLock = false; } return holdLock; } public boolean getLock(String lockName) { return getLock(lockName, 60 * 1000); } public void releaseLock(String lockName) { Preconditions.checkNotNull(lockName, "lockName"); deleteLock(lockName); } } 将方法打包成公共包 @FeignClient(name = "service") //服务名 @RequestMapping("/lock") //服务请求接口 public interface LockApi { @GetMapping(value = "/{ttl}") HttpResult getLock(@RequestParam("lockName") String lockName, @PathVariable("ttl") int ttl); @GetMapping(value = "") HttpResult getLock(@RequestParam("lockName") String lockName); @PostMapping(value = "/release") HttpResult releaseLock(@RequestParam("lockName") String lockName); } 在方法中使用锁的方法,进行fegin调用服务 //获取锁 if (!lockApi.getLock(triggerLockKey, 30*1000).getData().booleanValue()) { return false; } try { addTriggerRecord(); return true; } finally { //释放锁 lockApi.releaseLock(triggerLockKey); } 四、基于Redis实现分布式锁 1、Redis实现分布式锁的优点。 (1)Redis有很高的性能; (2)Redis命令对此支持较好,实现起来比较方便。 2、常用命令 (1) SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。 (2) expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。 (3) delete key:删除key 3、实现思想 (1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。 (2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。 (3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。 4、简单代码实现 @Component public class JedisLockUtil { private static final Logger log = LoggerFactory.getLogger(LockUtil.class); private static final String LOCK_SUCCESS = "OK"; private static final String SET_IF_NOT_EXIST = "NX"; private static final String SET_WITH_EXPIRE_TIME = "PX"; private static final String LOCK_PREFIX = "LOCK-"; private static final String DEFAULT_VALUE = "default_value"; private static final Integer DEFAULT_EXPIRE_TIME = 30000; private static Boolean jedisFlag = null; public LockUtil() { } private static Boolean isJedis() { if (jedisFlag == null) { JedisConnectionFactory bean = (JedisConnectionFactory)SpringUtil.getBean(JedisConnectionFactory.class); if (bean == null) { throw new RuntimeException("jedisConnectionFactory 获取失败"); } if (bean.getConnection().getNativeConnection() instanceof Jedis) { jedisFlag = true; } else if (bean.getConnection().getNativeConnection() instanceof JedisCluster) { jedisFlag = false; } } return jedisFlag; } public static RedisConnection getRedisConnection() { JedisConnectionFactory bean = (JedisConnectionFactory)SpringUtil.getBean(JedisConnectionFactory.class); if (bean == null) { throw new RuntimeException("jedisConnectionFactory 获取失败"); } else { return bean.getConnection(); } } public static boolean lock(String key, String requiredId, int expireTime) { RedisConnection redisConnection = getRedisConnection(); boolean var5; try { if (isJedis() == null) { throw new RuntimeException("redis链接获取失败"); } String result = ""; if (isJedis()) { Jedis jedis = (Jedis)redisConnection.getNativeConnection(); result = jedis.set("LOCK-" + key, requiredId, "NX", "PX", expireTime); } else { JedisCluster cluster = (JedisCluster)redisConnection.getNativeConnection(); result = cluster.set("LOCK-" + key, requiredId, "NX", "PX", (long)expireTime); } if (!"OK".equals(result)) { var5 = false; return var5; } var5 = true; return var5; } catch (Exception var9) { log.error("分布式锁加锁失败 key:" + key + " required:" + requiredId, var9); var5 = false; } finally { if (redisConnection != null) { redisConnection.close(); } } return var5; } public static boolean lock(String key, String requiredId) { return lock(key, requiredId, DEFAULT_EXPIRE_TIME); } public static boolean lock(String key, int expireTime) { return lock(key, "default_value", expireTime); } public static boolean lock(String key) { return lock(key, "default_value", DEFAULT_EXPIRE_TIME); } public static boolean unLock(String lockKey, String requestId) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; RedisConnection redisConnection = getRedisConnection(); boolean var5; try { Object result = null; if (isJedis() == null) { throw new RuntimeException("redis链接获取失败"); } if (isJedis()) { Jedis jedis = (Jedis)redisConnection.getNativeConnection(); result = jedis.eval(script, Collections.singletonList("LOCK-" + lockKey), Collections.singletonList(requestId)); } else { JedisCluster cluster = (JedisCluster)redisConnection.getNativeConnection(); result = cluster.eval(script, Collections.singletonList("LOCK-" + lockKey), Collections.singletonList(requestId)); } if (result == null) { var5 = false; return var5; } long rt = 0L; rt = (Long)result; boolean var7; if (rt > 0L) { var7 = true; return var7; } var7 = false; return var7; } catch (Exception var11) { log.error("分布式锁解锁失败!! lockKey:" + lockKey + " requestId:" + requestId, var11); var5 = false; } finally { if (redisConnection != null) { redisConnection.close(); } } return var5; } public static boolean unLock(String key) { return unLock(key, "default_value"); } } 在方法中的使用 String key = CaseService.class.getCanonicalName() + "-createCase-" + caseGroup.getBusinessId() + caseGroup.getTitle(); String rid = UUIDUtil.getUuid(); if (LockUtil.lock(key, rid)) { try { caseService.createCase(caseGroup); return HttpResult.build(true); } catch (Exception e) { return HttpResult.build(false ,e.getMessage()); } finally { LockUtil.unLock(key, rid); } ———————————————— 版权声明:本文为CSDN博主「zxucheng」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_27182767/article/details/90414177 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |