В прошлой статье "SpringBoot Integrated Redis Distributed Lock Optimization" была оптимизирована проблема взаимоблокировки. Сегодня мы представляем Redisson, официально рекомендованный redis. Redisson - это Java In-Memory Data Grid (In-Memory Data Grid), построенный на основе redis.), основанный на инфраструктуре NIO Netty, с использованием базы данных ключ-значение redis. Он очень мощный и решает многие проблемы в распределенных архитектурах.
Вики-адрес Github:GitHub.com/Редис сын/Горячие…
Официальная документация:GitHub.com/Редис сын/Горячие…
Схема структуры кода проекта:
импортировать зависимости
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.8.0</version>
</dependency>
конфигурация свойства
существуетapplication.properitesДобавить конфигурацию, связанную с одной машиной и часовым, в файл ресурсов
server.port=3000
# redisson lock 单机模式
redisson.address=redis://127.0.0.1:6379
redisson.password=
#哨兵模式
#redisson.master-name= master
#redisson.password=
#redisson.sentinel-addresses=10.47.91.83:26379,10.47.91.83:26380,10.47.91.83:26381
Уведомление:
Если здесь не добавлен префикс redis://, будет сообщено об ошибке построения URI.
Caused by: java.net.URISyntaxException: Illegal character in scheme name at index 0
Более подробную информацию о конфигурации можно посмотреть на официальном сайте
Определите класс определения интерфейса Lock
package com.tuhu.thirdsample.service;
import org.redisson.api.RLock;
import java.util.concurrent.TimeUnit;
/**
* @author chendesheng
* @create 2019/10/12 10:48
*/
public interface DistributedLocker {
RLock lock(String lockKey);
RLock lock(String lockKey, int timeout);
RLock lock(String lockKey, TimeUnit unit, int timeout);
boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);
void unlock(String lockKey);
void unlock(RLock lock);
}
Класс реализации интерфейса блокировки
package com.tuhu.thirdsample.service;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
/**
* @author chendesheng
* @create 2019/10/12 10:49
*/
public class RedissonDistributedLocker implements DistributedLocker{
private RedissonClient redissonClient;
@Override
public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
@Override
public RLock lock(String lockKey, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
return lock;
}
@Override
public RLock lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
@Override
public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
@Override
public void unlock(RLock lock) {
lock.unlock();
}
public void setRedissonClient(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
}
класс сборки атрибута redisson
package com.tuhu.thirdsample.common;
import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author chendesheng
* @create 2019/10/11 20:04
*/
@Configuration
@ConfigurationProperties(prefix = "redisson")
@ConditionalOnProperty("redisson.password")
@Data
public class RedissonProperties {
private int timeout = 3000;
private String address;
private String password;
private int database = 0;
private int connectionPoolSize = 64;
private int connectionMinimumIdleSize=10;
private int slaveConnectionPoolSize = 250;
private int masterConnectionPoolSize = 250;
private String[] sentinelAddresses;
private String masterName;
}
Классы автосвязывания SpringBoot
package com.tuhu.thirdsample.configuration;
import com.tuhu.thirdsample.common.RedissonProperties;
import com.tuhu.thirdsample.service.DistributedLocker;
import com.tuhu.thirdsample.service.RedissonDistributedLocker;
import com.tuhu.thirdsample.util.RedissonLockUtil;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author chendesheng
* @create 2019/10/12 10:50
*/
@Configuration
@ConditionalOnClass(Config.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration {
@Autowired
private RedissonProperties redissonProperties;
/**
* 哨兵模式自动装配
* @return
*/
@Bean
@ConditionalOnProperty(name="redisson.master-name")
RedissonClient redissonSentinel() {
Config config = new Config();
SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redissonProperties.getSentinelAddresses())
.setMasterName(redissonProperties.getMasterName())
.setTimeout(redissonProperties.getTimeout())
.setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize())
.setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize());
if(StringUtils.isNotBlank(redissonProperties.getPassword())) {
serverConfig.setPassword(redissonProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 单机模式自动装配
* @return
*/
@Bean
@ConditionalOnProperty(name="redisson.address")
RedissonClient redissonSingle() {
Config config = new Config();
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(redissonProperties.getAddress())
.setTimeout(redissonProperties.getTimeout())
.setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize());
if(StringUtils.isNotBlank(redissonProperties.getPassword())) {
serverConfig.setPassword(redissonProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 装配locker类,并将实例注入到RedissLockUtil中
* @return
*/
@Bean
DistributedLocker distributedLocker(RedissonClient redissonClient) {
DistributedLocker locker = new RedissonDistributedLocker();
((RedissonDistributedLocker) locker).setRedissonClient(redissonClient);
RedissonLockUtil.setLocker(locker);
return locker;
}
}
Вспомогательный класс блокировки
package com.tuhu.thirdsample.util;
import com.tuhu.thirdsample.service.DistributedLocker;
import org.redisson.api.RLock;
import java.util.concurrent.TimeUnit;
/**
* @author chendesheng
* @create 2019/10/12 10:54
*/
public class RedissonLockUtil {
private static DistributedLocker redissLock;
public static void setLocker(DistributedLocker locker) {
redissLock = locker;
}
/**
* 加锁
* @param lockKey
* @return
*/
public static RLock lock(String lockKey) {
return redissLock.lock(lockKey);
}
/**
* 释放锁
* @param lockKey
*/
public static void unlock(String lockKey) {
redissLock.unlock(lockKey);
}
/**
* 释放锁
* @param lock
*/
public static void unlock(RLock lock) {
redissLock.unlock(lock);
}
/**
* 带超时的锁
* @param lockKey
* @param timeout 超时时间 单位:秒
*/
public static RLock lock(String lockKey, int timeout) {
return redissLock.lock(lockKey, timeout);
}
/**
* 带超时的锁
* @param lockKey
* @param unit 时间单位
* @param timeout 超时时间
*/
public static RLock lock(String lockKey, TimeUnit unit , int timeout) {
return redissLock.lock(lockKey, unit, timeout);
}
/**
* 尝试获取锁
* @param lockKey
* @param waitTime 最多等待时间
* @param leaseTime 上锁后自动释放锁时间
* @return
*/
public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {
return redissLock.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);
}
/**
* 尝试获取锁
* @param lockKey
* @param unit 时间单位
* @param waitTime 最多等待时间
* @param leaseTime 上锁后自动释放锁时间
* @return
*/
public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
return redissLock.tryLock(lockKey, unit, waitTime, leaseTime);
}
}
слой управления
package com.tuhu.thirdsample.task;
import com.tuhu.thirdsample.common.KeyConst;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* @author chendesheng
* @create 2019/10/12 11:03
*/
@RestController
@RequestMapping("/lock")
@Slf4j
public class LockController {
@Autowired
RedissonClient redissonClient;
@GetMapping("/task")
public void task(){
log.info("task start");
RLock lock = redissonClient.getLock(KeyConst.REDIS_LOCK_KEY);
boolean getLock = false;
try {
if (getLock = lock.tryLock(0,5,TimeUnit.SECONDS)){
//执行业务逻辑
System.out.println("拿到锁干活");
}else {
log.info("Redisson分布式锁没有获得锁:{},ThreadName:{}",KeyConst.REDIS_LOCK_KEY,Thread.currentThread().getName());
}
} catch (InterruptedException e) {
log.error("Redisson 获取分布式锁异常,异常信息:{}",e);
}finally {
if (!getLock){
return;
}
//如果演示的话需要注释该代码;实际应该放开
//lock.unlock();
//log.info("Redisson分布式锁释放锁:{},ThreadName :{}", KeyConst.REDIS_LOCK_KEY, Thread.currentThread().getName());
}
}
}
RLock наследуется отjava.util.concurrent.locks.Lock
, его можно понимать как повторную блокировку, которую необходимо заблокировать и снять вручную.
Посмотрите на один из его методов: tryLock(long waitTime, long LeaseTime, блок TimeUnit)
getLock = lock.tryLock(0,5,TimeUnit.SECONDS)
Из параметров tryLock() видно, что при получении блокировки, если блокировка получена другими потоками первыми, он войдет в ожидание, подождет время ожидания и сдастся, если блокировка не была получена до этого. , и вернуть false; если блокировка получена, если только она не снята вызовом unlock, блокировка будет удерживаться до тех пор, пока не истечет время, указанное в LeezeTime.
Вышеизложенное является основным методом Redisson для реализации распределенных блокировок Некоторые люди могут спросить, как вы определяете, что взята одна и та же блокировка, и где находится распределенная блокировка?
В этом сила Redisson. Нижний уровень по-прежнему использует Redis для распределенных блокировок. Экземпляр Redis указан в нашем RedissonManager, и Redisson будет управлять им. Принцип аналогичен принципу ручной реализации распределенных блокировок Redis.
контрольная работа
Запустите проект и введите адрес в почтальоне:http://localhost:3000/lock/task
Первый результат ввода мы можем увидеть в консоли:
Повторный запрос в течение времени истечения блокировки, мы можем увидеть в консоли:
Наконец
Хорошо, SpringBoot интегрировал Redisson, чтобы реализовать распределенную блокировку, которая была написана до конца. Использование redisson больше отражает то, что все является объектом, нам не нужно знать, как его реализовать внутри, просто нужно знать, как его использовать. Конечно, просто знать, как им пользоваться, недостаточно, важнее знать основные принципы!