spring 锁_分布式锁-快速实战

**目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题就存在问题,解决方案分布式锁。**

下面介绍两种首先分布式锁的方案:

1. **基于Spring Integration实现分布式锁**

2. **基于redisson实现分布式锁**

优缺点:第一种引入简单,使用方便,但只支持重入锁。第二种较第一种麻烦一点点,但支持重入锁、公平锁、读锁、写锁等多种类型。

**第一种方案**:提供的全局锁目前为以下存储提供了实现

Gemfire

JDBC

Redis

Zookeeper

因为第二种方案基于redis存储,为了方便该方案讲解选择redis作为存储。同时项目都采用springboot方式。

1. 首先创建springboot项目,添加基本依赖及redis、integration-redis依赖。如果选择其他存储方式,添加对应的integration依赖即可,无需更改业务,非常方便。

```

<!--integration-->

<dependency>

<groupId>org.springframework.integration</groupId>

<artifactId>spring-integration-redis</artifactId>

</dependency>

<!--redis-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.session</groupId>

<artifactId>spring-session-data-redis</artifactId>

</dependency>

<dependency>

<groupId>org.redisson</groupId>

<artifactId>redisson-spring-boot-starter</artifactId>

<version>3.9.1</version>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</exclusion>

</exclusions>

</dependency>

```

2. 添加yml配置

```

spring:

http:

encoding:

charset: UTF-8

force: true

enabled: true

server:

port: 8080

mvc:

static-path-pattern: /**

resources:

static-locations: classpath:/static/

redis:

database: 1

host: xx.xx.xx.xx

port: 6379

password: xxx

jedis:

pool:

max-active: 8

max-idle: 8

min-idle: 0

redisson:

address: "xxxx"

password: xxx

```

3. 创建config配置,RedisLockRegistry的第三个参数设置为上锁以后xxx秒自动解锁的时间,默认60s。

```java

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.connection.RedisConnectionFactory;

import org.springframework.integration.redis.util.RedisLockRegistry;

/**

* 描述:spring-integration-redis锁配置

*

* @Auther: gc.x

* @Date: 2019/10/28

*/

@Configuration

public class RedisLockConfiguration {

@Bean

public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory){

return new RedisLockRegistry(redisConnectionFactory,"spring-integration-redis",60000L);

}

}

```

4. 选择基于注解的形式加锁,先自定义锁注解,然后创建对应的切面

**@interface**

```java

/**

* 描述: 基于spring-integration-redis实现分布式锁

* 只支持重入锁ReentrantLock

* @Auther: gc.x

* @Ddate:2019/10/28

*/

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface LockDistributed {

/**

* 锁的名称

* @return

*/

String name() default "";

/**

* 尝试加锁 最多等待时间

* @return

*/

int time() default 30;

/**

* 自定义业务key

* @return

*/

String [] keys() default {};

/**

* 上锁以后xxx秒自动解锁

*

*/

/**

*上锁以后xxx秒自动解锁

* long leaseTime();

* 在RedisLockConfiguration里面配置,如果要使用该参数,需要在切面里面new RedisLockRegistry

* @return

*/

}

```

**Aspect**

主要就是拦截自定义的注解,获取数据生成lockname,及竞争锁时间,

redisLockRegistry.obtain方法获取锁,

lock.tryLock方法竞争锁

由于篇幅省略了部分私有方法,不用纠结,结尾会提供完整源码

---getKeyName是获取自定义的作用于参数上的一个注解,可以将传入的参数作为 lockname的一部分,增强锁的力度。

---getName获取完整路径的方法名。

```java

/**

* 描述:添加了 LzxLockDistributed 注解 的Aop

*

* @Auther: gc

* @Date: 2019/6/18 10:56

*/

@Component

@Aspect

@Slf4j

public class LockAspect {

/**

* 锁前缀名

*/

public static final String LOCK_NAME_PREFIX = "lock";

/**

* 分隔符

*/

public static final String LOCK_NAME_SEPARATOR = ".";

/**

* 表达式解析器

*/

private ExpressionParser parser = new SpelExpressionParser();

/**

* 参数解析

*/

private ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

@Autowired

private RedisLockRegistry redisLockRegistry;

/* @Autowired

private RedisConnectionFactory redisConnectionFactory;*/

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

public Object aroundApi(ProceedingJoinPoint point, LockDistributed lockDistributed) throws Throwable {

MethodSignature signature = (MethodSignature) point.getSignature();

String businessKeyName = getKeyName(point,lockDistributed);

String lockName = LOCK_NAME_PREFIX+LOCK_NAME_SEPARATOR + getName(lockDistributed.name(), signature) + businessKeyName;

log.info("lockName===>"+lockName);

// RedisLockRegistry redisLockRegistry=new RedisLockRegistry(redisConnectionFactory,"spring-integration-redis",lockDistributed.leaseTime());

Lock lock = redisLockRegistry.obtain(lockName);

boolean currentThreadLock = false;

Object proceed = null;

try{

currentThreadLock = lock.tryLock(lockDistributed.time(), TimeUnit.SECONDS);

Home("获取锁====="+currentThreadLock);

if (!currentThreadLock) {

throw new TimeoutException("获取锁资源等待超时");

}

proceed = point.proceed();

}catch (Exception e){

throw e;

}finally {

if(currentThreadLock){

lock.unlock();

}

}

return proceed;

}

}

```

5. 测试锁

```java

@RestController

public class DistributedLock {

@LockDistributed

@RequestMapping("/redisLockTest")

public void redisLockTest() {

System.out.println("获取锁,执行");

try {

Thread.sleep(20*1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("结束-------------------->>>>>>>>>>>>>>");

}

}

```

**第二种方案**:提供多种锁实现

1. 同理导入依赖

2. 配置yml

3. 创建config

```java

@Configuration

@ConditionalOnClass(Config.class)

@EnableConfigurationProperties(RedissonProperties.class)

public class RedissonAutoConfiguration {

@Autowired

private RedissonProperties redssionProperties;

/**

* 单体的

* @return

*/

@Bean

@ConditionalOnProperty(value = "redisson.address")

public RedissonClient redissonSingle(){

Config config = new Config();

SingleServerConfig serverConfig = config.useSingleServer().setAddress(redssionProperties.getAddress())

.setTimeout(redssionProperties.getTimeout())

.setDatabase(redssionProperties.getDatabase())

.setConnectionPoolSize(redssionProperties.getConnectionPoolSize())

.setConnectionMinimumIdleSize(redssionProperties.getConnectionMinimumIdleSize());

if(StringUtils.isNotEmpty(redssionProperties.getPassword())){

serverConfig.setPassword(redssionProperties.getPassword());

}

return Redisson.create(config);

}

/**

* 集群的

* @return

*/

@Bean

@ConditionalOnProperty(value = "redisson.masterAddresses")

public RedissonClient redissonSentinel(){

Config config = new Config();

ClusterServersConfig serverConfig = config.useClusterServers().addNodeAddress(redssionProperties.getSentinelAddresses())

.setTimeout(redssionProperties.getTimeout())

//设置集群扫描时间

.setScanInterval(redssionProperties.getScanInterval())

//主节点线程池数量

.setMasterConnectionPoolSize(redssionProperties.getMasterConnectionPoolSize())

//从节点线程池数量

.setSlaveConnectionPoolSize(redssionProperties.getSlaveConnectionPoolSize());

if(StringUtils.isNotEmpty(redssionProperties.getPassword())){

serverConfig.setPassword(redssionProperties.getPassword());

}

return Redisson.create(config);

}

```

4. 自定义注解,属性多了锁类型

```java

/**

* 描述: 基于redisson实现分布式锁

* 支持多种锁类型

* @Auther: gc.x

* @Ddate:2019/10/28

*/

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface LockDistributed2 {

/**

* 锁的名称

* @return

*/

String name() default "";

/**

* 锁类型,默认可重入锁

* @return

*/

LockType lockType() default LockType.Reentrant;

/**

* 尝试加锁,最多等待时间

* @return

*/

long waitTime() default 60;

/**

*上锁以后xxx秒自动解锁

* @return

*/

long leaseTime() default 60*5;

/**

* 自定义业务key

* @return

*/

String [] keys() default {};

}

```

5. 切面,和方案一类似,主要根据注解上的锁类型获取对应的redisson提供的锁。

该实现内容较多不贴出来了,结构如下

![在这里插入图片描述](https://img-blog.csdnimg.cn/2019102915232952.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDYwMTk4,size_16,color_FFFFFF,t_70)

```java

/**

* @Auther: gc.x

* @Date: 2019/10/28

* @Description:

*/

@Aspect

@Component

@Slf4j

public class LockAspect2 {

/**

* 锁前缀名

*/

public static final String LOCK_NAME_PREFIX = "lock";

/**

* 分隔符

*/

public static final String LOCK_NAME_SEPARATOR = ".";

/**

* 表达式解析器

*/

private ExpressionParser parser = new SpelExpressionParser();

/**

* 参数解析

*/

private ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

@Autowired

private LockFactory lockFactory;

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

public Object around(ProceedingJoinPoint joinPoint, LockDistributed2 lockDistributed2) throws Throwable{

// 获取方法

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

// 获取锁类型

LockType type= lockDistributed2.lockType();

String businessKeyName = getKeyName(joinPoint,lockDistributed2);

String lockName = LOCK_NAME_PREFIX+LOCK_NAME_SEPARATOR + getName(lockDistributed2.name(), signature) + businessKeyName;

Home("lockName===>"+lockName);

long waitTime = lockDistributed2.waitTime();

long leaseTime =lockDistributed2.leaseTime();

LockInfo lockInfo = new LockInfo(type,lockName,waitTime,leaseTime);

ILock ilock = lockFactory.getLock(lockInfo);

boolean currentThreadLock = false;

try {

currentThreadLock = ilock.acquire();

if (!currentThreadLock) {

throw new TimeoutException("获取锁资源等待超时");

}

return joinPoint.proceed();

} finally {

if (currentThreadLock) {

ilock.release();

}

}

}

```

6. 同理测试,结束

附上码云:[https://gitee.com/giteeClass/project](https://gitee.com/giteeClass/project)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值