Springboot redissionを使用してredisベースの分散ロックを実現
現在、多くのインターネット会社は分散アーキテクチャ導入システムを採用しており、分散シーンにおけるデータ整合性の問題は非常に重要な問題であり、分散CAP理論は、どの分散システムも整合性(consistency)可用性(avaliability)パーティションフォールトトレランス(partition tolerance)を同時に満たすことができないことを示しています.最大2つしか満足できません.インターネットの分野では、一貫性を犠牲にして高可用性を取り替えることが多く、システムは最終的なデータの一貫性だけを保証し、この最終的な時間がユーザーが受け入れることができる範囲内である限り.
分散アーキテクチャに基づいてデータ整合性を実現する方法は、3つある1データベースに基づいて実現される分散ロック2キャッシュに基づいて分散ロック3 zookeeperに基づいて分散ロックを実現する
本稿では,redisに基づいて分散ロックを実装するサードパーティライブラリredissionを用いてredisの動作を実現するが,mavenはredissionを以下に導入する.
RedissionツールクラスRedissLockUtil
redisson属性アセンブリクラスRedissonProperties、プロファイル内のredission構成情報を読み込む
対応アプリケーションpropertiesプロファイルの内容
redissionの自動アセンブリクラスRedissonAutoConfiguration
Springbootのサービス実装の一部
参照リンク:https://blog.52itstyle.vip/
分散アーキテクチャに基づいてデータ整合性を実現する方法は、3つある1データベースに基づいて実現される分散ロック2キャッシュに基づいて分散ロック3 zookeeperに基づいて分散ロックを実現する
本稿では,redisに基づいて分散ロックを実装するサードパーティライブラリredissionを用いてredisの動作を実現するが,mavenはredissionを以下に導入する.
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.11.4</version>
</dependency>
RedissionツールクラスRedissLockUtil
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class RedissLockUtil {
private static RedissonClient redissonClient;
public void setRedissonClient(RedissonClient locker) {
redissonClient = locker;
}
/**
*
* @param lockKey
* @return
*/
public static RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
/**
*
* @param lockKey
*/
public static void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
/**
*
* @param lock
*/
public static void unlock(RLock lock) {
lock.unlock();
}
/**
*
* @param lockKey
* @param timeout :
*/
public static RLock lock(String lockKey, int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, TimeUnit.SECONDS);
return lock;
}
/**
*
* @param lockKey
* @param unit
* @param timeout
*/
public static RLock lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
/**
*
* @param lockKey
* @param waitTime
* @param leaseTime
* @return
*/
public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return false;
}
}
/**
*
* @param lockKey
* @param unit
* @param waitTime
* @param leaseTime
* @return
*/
public static 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;
}
}
}
redisson属性アセンブリクラスRedissonProperties、プロファイル内のredission構成情報を読み込む
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConditionalOnProperty("redisson.password")
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
private int timeout = 3000;
private String address;
private String password;
private int connectionPoolSize = 64;
private int connectionMinimumIdleSize=10;
private int slaveConnectionPoolSize = 250;
private int masterConnectionPoolSize = 250;
private String[] sentinelAddresses;
private String masterName;
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getSlaveConnectionPoolSize() {
return slaveConnectionPoolSize;
}
public void setSlaveConnectionPoolSize(int slaveConnectionPoolSize) {
this.slaveConnectionPoolSize = slaveConnectionPoolSize;
}
public int getMasterConnectionPoolSize() {
return masterConnectionPoolSize;
}
public void setMasterConnectionPoolSize(int masterConnectionPoolSize) {
this.masterConnectionPoolSize = masterConnectionPoolSize;
}
public String[] getSentinelAddresses() {
return sentinelAddresses;
}
public void setSentinelAddresses(String sentinelAddresses) {
this.sentinelAddresses = sentinelAddresses.split(",");
}
public String getMasterName() {
return masterName;
}
public void setMasterName(String masterName) {
this.masterName = masterName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}
public int getConnectionMinimumIdleSize() {
return connectionMinimumIdleSize;
}
public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) {
this.connectionMinimumIdleSize = connectionMinimumIdleSize;
}
}
対応アプリケーションpropertiesプロファイルの内容
# redisson lock
redisson.address=redis://127.0.0.1:6379
redisson.password=123456
redissionの自動アセンブリクラスRedissonAutoConfiguration
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
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;
@Configuration
@ConditionalOnClass(Config.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration {
@Autowired
private RedissonProperties redssionProperties;
/**
*
* @return
*/
// @Bean
// @ConditionalOnProperty(name="redisson.master-name")
// RedissonClient redissonSentinel() {
// Config config = new Config();
// SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redssionProperties.getSentinelAddresses())
// .setMasterName(redssionProperties.getMasterName())
// .setTimeout(redssionProperties.getTimeout())
// .setMasterConnectionPoolSize(redssionProperties.getMasterConnectionPoolSize())
// .setSlaveConnectionPoolSize(redssionProperties.getSlaveConnectionPoolSize());
//
// if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
// serverConfig.setPassword(redssionProperties.getPassword());
// }
// return Redisson.create(config);
// }
/**
*
* @return
*/
@Bean
@ConditionalOnProperty(name="redisson.address")
RedissonClient redissonSingle() {
Config config = new Config();
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(redssionProperties.getAddress())
.setTimeout(redssionProperties.getTimeout())
.setConnectionPoolSize(redssionProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redssionProperties.getConnectionMinimumIdleSize());
if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
serverConfig.setPassword(redssionProperties.getPassword());
}
return Redisson.create(config);
}
/**
* locker , RedissLockUtil
* @return
*/
@Bean
RedissLockUtil redissLockUtil(RedissonClient redissonClient) {
RedissLockUtil redissLockUtil = new RedissLockUtil();
redissLockUtil.setRedissonClient(redissonClient);
return redissLockUtil;
}
}
Springbootのサービス実装の一部
@Transactional
public Result startSeckilRedisLock(long seckillId,long userId) {
boolean res=false;
try {
/**
* , 3 , 20 ( , )、 , .
* , , AOP 。
*/
res = RedissLockUtil.tryLock(seckillId+"", TimeUnit.SECONDS, 3, 20);
if(res){
String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";
Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
Long number = ((Number) object).longValue();
if(number>0){
SuccessKilled killed = new SuccessKilled();
killed.setSeckillId(seckillId);
killed.setUserId(userId);
killed.setState((short)0);
killed.setCreateTime(new Timestamp(new Date().getTime()));
dynamicQuery.save(killed);
nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=? AND number>0";
dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
}else{
return Result.error(SeckillStatEnum.END);
}
}else{
return Result.error(SeckillStatEnum.MUCH);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
if(res){//
RedissLockUtil.unlock(seckillId+"");
}
}
return Result.ok(SeckillStatEnum.SUCCESS);
}
参照リンク:https://blog.52itstyle.vip/