多数据源 数据源1为锁控制,数据源2自定义,可用于存储。

  锁:当出现并发的时候为了保证数据的一致性,不会出现并发问题,假设,用户1修改一条信息,用户2也同时修改,会按照顺序覆盖自修改的值,为了避免这种情况的发生,使用redis锁,实现控制。只可以一个用户去修改那条数据,当出现多个用户,会报错,抛出异常提示。

依赖:

<dependencies>
<!--######################### 定义 redis 版本 #########################-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--######################### 定义 jedis 版本 #########################-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!--######################### 定义 json 版本 #########################-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<!--######################### 定义 lang3 版本 #########################-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>

redis配置类

package cn.lsr.redis.core;

/**
* @Description: redis参数配置类
* @Package: lsr-microservice
* @author: Hacker_lsr@126.com
* @version: V1.0
**/
public class RedisPropertiesConfig {
/**
* redis 数据库
*/
private Integer database; /**
* redis 主机地址
*/
private String host; /**
* 端口
*/
private Integer port; /**
* 密码
*/
private String password; /**
* 驱动类名
*/
private Integer timeout; private Pool pool; //get set public static class Pool { private Integer maxActive; private Integer minIdle; private Integer maxIdle; private Integer maxWait; //get set
}
}

redis工厂基类:

package cn.lsr.redis.core;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig; import java.time.Duration; /**
* @Description: redis配置基类
* @Package: lsr-microservice
* @author: Hacker_lsr@126.com
* @version: V1.0
**/
public class LSRBaseRedisConfig {
/**
* jedis 连接工厂
* @param redisPropertiesConfig
* @return
*/
public JedisConnectionFactory buildJedisConnectionFactory(RedisPropertiesConfig redisPropertiesConfig) {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setDatabase(redisPropertiesConfig.getDatabase());
jedisConnectionFactory.setHostName(redisPropertiesConfig.getHost());
jedisConnectionFactory.setPort(redisPropertiesConfig.getPort());
jedisConnectionFactory.setPassword(redisPropertiesConfig.getPassword());
jedisConnectionFactory.setTimeout(redisPropertiesConfig.getTimeout()); JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(redisPropertiesConfig.getPool().getMaxIdle());
poolConfig.setMinIdle(redisPropertiesConfig.getPool().getMinIdle());
poolConfig.setMaxTotal(redisPropertiesConfig.getPool().getMaxActive());
poolConfig.setMaxWaitMillis(redisPropertiesConfig.getPool().getMaxWait());
poolConfig.setTestOnBorrow(true); jedisConnectionFactory.setPoolConfig(poolConfig);
return jedisConnectionFactory; }
private Duration timeToLive = Duration.ZERO;
public void setTimeToLive(Duration timeToLive) {
this.timeToLive = timeToLive;
} /**
* 缓存
* @param factory
* @return
*/
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题)
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
//
// RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
// return redisCacheManager;
//} /**
* RedisTemplate 初始化 序列化和反序列化
* @param redisConnectionFactory
* @return
*/
public RedisTemplate buidRedisTemplate(RedisConnectionFactory redisConnectionFactory) { /* Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
template.setConnectionFactory(redisConnectionFactory);
//template.setKeySerializer(jackson2JsonRedisSerializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
*/
RedisSerializer stringSerializer = new StringRedisSerializer();
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
return redisTemplate;
}
}

数据库操作工厂:

package cn.lsr.redis.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; /**
* @Description: 数据操作redis配置
* @Package: lsr-microservice
* @author: Hacker_lsr@126.com
* @version: V1.0
**/
@Configuration
public class LSRDBRedisConfig extends LSRBaseRedisConfig {
private static final Logger log = LoggerFactory.getLogger(LSRDBRedisConfig.class);
/**
* 初始化 jedis lsrDBRedisProperties 连接工厂 -- lsrDBJedisConnectionFactory
* @param redisPropertiesConfig
* @return
*/
@Bean(name = "lsrDBJedisConnectionFactory")
@Override
public JedisConnectionFactory buildJedisConnectionFactory(@Qualifier("lsrDBRedisProperties")RedisPropertiesConfig redisPropertiesConfig) {
log.info("lsrDBRedisConfig RedisPropertiesConfig:{}",redisPropertiesConfig);
return super.buildJedisConnectionFactory(redisPropertiesConfig);
} /**
* 初始化工厂中 lsrDBJedisConnectionFactory 的 lsrDBRedisTemplate
* @param redisConnectionFactory
* @return
*/
@Bean(name = "lsrDBRedisTemplate")
@Override
public RedisTemplate <Object, Object> buidRedisTemplate(@Qualifier("lsrDBJedisConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
return super.buidRedisTemplate(redisConnectionFactory);
} // @Bean
// @Override
// public CacheManager cacheManager(@Qualifier("lbsRedisTemplate")RedisTemplate redisTemplate) {
// return super.cacheManager(redisTemplate);
// } /**
* 启动加载配置文件 yml redis 连接参数
* @return
*/
@Bean(name = "lsrDBRedisProperties")
@ConfigurationProperties(prefix = "spring.redis.db")
public RedisPropertiesConfig getBaseDBProperties() {
return new RedisPropertiesConfig();
} }

锁实现工厂:

package cn.lsr.redis.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; /**
* @Description: 锁的配置
* @Package: lsr-microservice
* @author: Hacker_lsr@126.com
* @version: V1.0
**/
@Configuration
public class LSRLockRedisConfig extends LSRBaseRedisConfig {
private static final Logger log = LoggerFactory.getLogger(LSRLockRedisConfig.class); /**
* 初始化 jedis lsrLockRedisConfig 连接工厂 -- lsrLockJedisConnectionFactory
* @param redisPropertiesConfig
* @return
*/
@Primary
@Bean(name = "lsrLockJedisConnectionFactory")
@Override
public JedisConnectionFactory buildJedisConnectionFactory(@Qualifier("lsrLockRedisConfig")RedisPropertiesConfig redisPropertiesConfig) {
log.info("MasterRedisConfig RedisPropertiesConfig:{}",redisPropertiesConfig);
return super.buildJedisConnectionFactory(redisPropertiesConfig);
} /**
* 初始化工厂中 lsrLockJedisConnectionFactory 的 lsrLockRedisTemplate
* @param redisConnectionFactory
* @return
*/
@Bean(name = "lsrLockRedisTemplate")
@Override
public RedisTemplate<Object, Object> buidRedisTemplate(@Qualifier("lsrLockJedisConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
return super.buidRedisTemplate(redisConnectionFactory);
} // @Bean
// @Override
// public CacheManager cacheManager(@Qualifier("lbsRedisTemplate")RedisTemplate redisTemplate) {
// return super.cacheManager(redisTemplate);
// } /**
* 启动加载配置文件 yml redis 连接参数
* @return
*/
@Bean(name = "lsrLockRedisConfig")
@ConfigurationProperties(prefix = "spring.redis.lock")
public RedisPropertiesConfig getBaseDBProperties() {
return new RedisPropertiesConfig();
} }

定义redis 锁实现逻辑

package cn.lsr.redis.core;

import cn.lsr.redis.utils.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import javax.annotation.Resource; /**
* @Description: 用redis实现分布式锁
* @Package: lsr-microservice
* @author: Hacker_lsr@126.com
* @version: V1.0
**/
@Component
public class RedisLock {
/**
* 不写默认使用带有@Primary的lsrLockRedisTemplate
*/
@Resource(name = "lsrLockRedisTemplate")
private RedisTemplate redisTemplate;
@Resource(name = "lsrDBRedisTemplate")
private RedisTemplate redisTemplate2; /**
* 加锁
* @param key id
* @param value 时间戳
* @return
*/
public boolean lock(String key, String value) {
//setIfAbsent相当于jedis中的setnx,如果能赋值就返回true,如果已经有值了,就返回false
//即:在判断这个key是不是第一次进入这个方法
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
//第一次,即:这个key还没有被赋值的时候
return true; }
String current_value = (String) redisTemplate.opsForValue().get(key);
if (!StringUtils.object2String(current_value).equals("")
//超时了
&& Long.parseLong(current_value) < System.currentTimeMillis()) {//①
//并发 重置value 让其获得锁失败!
redisTemplate.opsForValue().getAndSet(key, value);//②
String newValue = (String) redisTemplate.opsForValue().get(key);
if (!StringUtils.object2String(newValue).equals("")
//如果两个线程同时调用这个方法,当同时走到①的时候,
// 无论怎么样都有一个线程会先执行②这一行,
//假设线程1先执行②这行代码,那redis中key对应的value就变成了value
//然后线程2再执行②这行代码的时候,获取到的old_value就是value,
//那么value显然和他上面获取的current_value是不一样的,则线程2是没法获取锁的
&& newValue.equals(current_value)) {
return true;
}
}
return false;
} /**
* 释放锁
* @param key id
* @param value 时间戳
*/
public void unlock(String key, String value) {
try {
if (StringUtils.object2String(redisTemplate.opsForValue().get(key)).equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
e.printStackTrace();
}
} }

封装redis锁实现为借口:

package cn.lsr.redis.lock;

import cn.lsr.redis.utils.RedisResult;

/**
* @Description: redis接口
* @Package: lsr-microservice
* @author: Hacker_lsr@126.com
* @version: V1.0
**/
public interface RedisInterFace {
/**
* 获取锁服务
* @param id 唯一标识
* @param value 时间戳
* @return
*/
public RedisResult lock(String id, String value); /**
* 解锁服务
* @param id 唯一标识
* @param value 时间戳
*/
public RedisResult unlock(String id, String value);
}

redis锁借口实现:

package cn.lsr.redis.lock.imp;

import cn.lsr.redis.core.RedisLock;
import cn.lsr.redis.lock.RedisInterFace;
import cn.lsr.redis.utils.RedisResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; /**
* @Description: redis锁实现
* @Package: lsr-microservice
* @author: Hacker_lsr@126.com
* @version: V1.0
**/
@Service
public class RedisInterFaceImp implements RedisInterFace {
private static final Logger log = LoggerFactory.getLogger(RedisInterFaceImp.class);
@Autowired
private RedisLock redisLock;
@Override
public RedisResult lock(String id, String value) {
if (!redisLock.lock(id,value)){
log.info("获取redis失败 错误!!标识为:"+id);
return RedisResult.error(false,"获得redis锁错误!!!! 标识为:"+id);
//throw new RuntimeException("活得锁失败!");
}
log.info("获得redis锁成功 标识为:"+id);
return RedisResult.success(true,"获得redis锁成功 标识为:"+id);
} @Override
public RedisResult unlock(String id, String value) {
log.info("释放redis锁成功 标识为:"+id);
redisLock.unlock(id,value);
return RedisResult.success(true,"释放redis锁成功 标识为:"+id);
}
}

调用使用模拟

package cn.lsr.user.controller.user;

/**
* = = 用户控制器
*
* @Version: 1.0
* @Author: Hacker_lsr@126.com
*/
@Api(tags = "用户信息控制器")
@Controller
public class UserController {
private TestServerPollThread testServerPollThread;
/**
* 注入redis服务
*/
@Resource
private RedisInterFace redisInterFace; }
/**
* 功能描述: <br>
* 〈〉根据主键删除
* @Param: [uid]
* @Return: com.lsr.common.utils.Result
* @Author: Hacker_lsr@126.com
*/
@RequiresPermissions("delete")
@RequestMapping("/delete/user")
@ResponseBody
public Result deleteUser(String uid){
String time = System.currentTimeMillis()+"";
RedisResult lock = redisInterFace.lock(uid, time);
if (lock.getStatus()==200){
//userMapper.deleteByPrimaryKey(uid);
userMapper.selecTest("admin");
}else {
throw new RuntimeException(lock.getMessages());
}
RedisResult unlock = redisInterFace.unlock(uid, time);
log.info("reids锁释放:{}",unlock.getMessages());
return Result.success("操作成功");
}
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.0.104",6379);
jedis.ping();
System.out.println(jedis.ping());
}
}

基于redis实现锁控制的更多相关文章

  1. RedLock.Net - 基于Redis分布式锁的开源实现

    工作中,经常会遇到分布式环境中资源访问冲突问题,比如商城的库存数量处理,或者某个事件的原子性操作,都需要确保某个时间段内只有一个线程在访问或处理资源. 因此现在网上也有很多的分布式锁的解决方案,有数据 ...

  2. 基于Redis分布式锁的正确打开方式

    分布式锁是在分布式环境下(多个JVM进程)控制多个客户端对某一资源的同步访问的一种实现,与之相对应的是线程锁,线程锁控制的是同一个JVM进程内多个线程之间的同步.分布式锁的一般实现方法是在应用服务器之 ...

  3. Redis并发锁控制

    为了防止用户在页面上重复点击或者同时发起多次请求,请求处理需要操作redis缓存,这个时候需要对并发边界进行并发锁控制,实现思路: 由于每个页面发起的请求带的token具备唯一性,可以将token作为 ...

  4. 基于Redis分布式锁(获取锁及解锁)

    目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency).可用性( ...

  5. c# 基于redis分布式锁

    在单进程的系统中,当存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量. 而同步的本质是通过锁来实现的.为了实现多个线程在 ...

  6. 基于redis分布式锁实现“秒杀”

    转载:http://blog.5ibc.net/p/28883.html 最近在项目中遇到了类似“秒杀”的业务场景,在本篇博客中,我将用一个非常简单的demo,阐述实现所谓“秒杀”的基本思路. 业务场 ...

  7. 基于redis分布式锁实现“秒杀”(转载)

    转载:http://blog.csdn.net/u010359884/article/details/50310387 最近在项目中遇到了类似“秒杀”的业务场景,在本篇博客中,我将用一个非常简单的de ...

  8. 基于 Redis 分布式锁

    1.主流分布式锁实现方案 基于数据库实现分布式锁 基于缓存(redis 等) 基于 Zookeeper 2.根据实现方式分类 : 类 CAS 自旋式分布式锁:询问的方式,类似 java 并发编程中的线 ...

  9. 基于Redis的分布式锁真的安全吗?

    说明: 我前段时间写了一篇用consul实现分布式锁,感觉理解的也不是很好,直到我看到了这2篇写分布式锁的讨论,真的是很佩服作者严谨的态度, 把这种分布式锁研究的这么透彻,作者这种技术态度真的值得我好 ...

随机推荐

  1. 初步了解URL

    URl的定义: 在webs上每种可用的资源(比如:HTML文档,图像,视频片段,程序等)都可以由一个通用的资源标志符(Universal Resource Identifier)进行定位.URl的组成 ...

  2. 在执行 php artisan key:generate ,报 Could not open input file: artisan 错误

    Could not open input file: artisan 必须保证命令是在项目根目录,如下图所示:

  3. MFC 屏蔽esc跟enter键

    BOOL CMenuOperate::PreTranslateMessage(MSG* pMsg) { if(pMsg->message == WM_KEYDOWN && pMs ...

  4. PHP实现简单的双色球机选号码

    <?php header('Content-Type: text/html; charset=utf-8'); //PHP实现双色球机选号码 $red = range(1, 33);//初次设定 ...

  5. BZOJ 3876 [Ahoi2014&Jsoi2014]支线剧情

    题解: 带下界的费用流 对于x->y边权为z Addedge(x,t,1,0) Addedge(s,y,1,z) Addedge(x,y,inf,0) 然后对每个点Addedge(i,1,inf ...

  6. c++ STD Gems07

    reverse.rotate.permutation #include <iostream> #include <vector> #include <string> ...

  7. python-局域网内实现web页面用户端下载文件,easy!

    好久没有发博客了,但是也没闲着,最近疫情原因一直在家远程办公,分享一下今天的干货 先说需求:某个文件压缩之后可以供用户点击下载 没想到特别好的办法,在网上搜索大多都是通过socket实现的,然后我这个 ...

  8. 吴裕雄--天生自然JAVA SPRING框架开发学习笔记:Spring体系结构详解

    Spring 框架采用分层架构,根据不同的功能被划分成了多个模块,这些模块大体可分为 Data Access/Integration.Web.AOP.Aspects.Messaging.Instrum ...

  9. MySQL数据类型使用总结,浮点使用注意事项

    1.对于精度要求较高的应用中,建议使用定点数来存储数值,以保证结果的准确性. 2.对于字符类型,要根据存储引擎进行相应的选择 3.对含有TEXT和BOLB字段的表,如果经常做删除和修改记录的操作要定时 ...

  10. CTF -bugku-web-web基础$_GET和$_POST

    ---恢复内容开始--- GET那题 就算没有学过php也会看懂if条件语句 于是我们在url后面直接加 ?what = flag 这样echo输出flag POST那题 直接火狐 要装hackbar ...