多数据源 数据源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()); } }