spring boot redis 分布式锁
生活随笔
收集整理的這篇文章主要介紹了
spring boot redis 分布式锁
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?
yml文件
redis:host: 127.0.0.1port: 40197password: 123456timeout: 5000database: 0jedis:pool:min-idle: 0max-idle: 8max-active: 8max-wait: -1?
RedisConfig.java import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.*; import redis.clients.jedis.JedisPoolConfig;import java.io.Serializable; import java.time.Duration;/*** @author */ @Configuration @ConditionalOnProperty("spring.redis.host") @EnableCaching @Slf4j public class RedisConfig extends CachingConfigurerSupport {@Value("${spring.redis.host}")String redisHost;@Value("${spring.redis.port}")int redisPort;@Value("${spring.redis.password}")String redisPassword;@Value("${spring.redis.timeout}")int redisTimeout;@Value("${spring.redis.database}")int redisDatabase;@Value("${spring.redis.jedis.pool.min-idle}")int jedisMinIdle;@Value("${spring.redis.jedis.pool.max-idle}")int jedisMaxIdle;@Value("${spring.redis.jedis.pool.max-active}")int jedisMaxActive;@Value("${spring.redis.jedis.pool.max-wait}")int jedisMaxWait;/*** 自定義緩存key生成策略* @return KeyGenerator*/@Bean@Overridepublic KeyGenerator keyGenerator() {return (target, method, params) -> {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getName());sb.append(method.getName());for (Object obj : params) {sb.append(obj.toString());}return sb.toString();};}@Beanpublic RedisConnectionFactory connectionFactory() {JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(jedisMaxActive);poolConfig.setMaxIdle(jedisMaxIdle);poolConfig.setMaxWaitMillis(jedisMaxWait);poolConfig.setMinIdle(jedisMinIdle);poolConfig.setTestOnBorrow(true);poolConfig.setTestOnReturn(false);poolConfig.setTestWhileIdle(true);JedisClientConfiguration clientConfig = JedisClientConfiguration.builder().usePooling().poolConfig(poolConfig).and().readTimeout(Duration.ofMillis(redisTimeout)).build();// 單點redisRedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();log.debug(JSON.toJSONString(redisStandaloneConfiguration));redisStandaloneConfiguration.setHostName(redisHost);redisStandaloneConfiguration.setPassword(RedisPassword.of(redisPassword));redisStandaloneConfiguration.setPort(redisPort);redisStandaloneConfiguration.setDatabase(redisDatabase);return new JedisConnectionFactory(redisStandaloneConfiguration, clientConfig);}/*** 緩存管理器* @param redisConnectionFactory RedisConnectionFactory* @return CacheManager*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {// 設置緩存有效期一小時int hour = 1;RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(hour));return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)).cacheDefaults(redisCacheConfiguration).build();}@Beanpublic RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){RedisTemplate<String, String> template = new RedisTemplate<>();template.setConnectionFactory(factory);//設置序列化工具setSerializer(template);template.afterPropertiesSet();return template;}private void setSerializer(RedisTemplate<String, String> template) {RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();RedisSerializer<Serializable> genericToStringSerializer = new GenericToStringSerializer<>(Serializable.class);log.debug(String.valueOf(genericToStringSerializer));RedisSerializer<Object> jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();log.debug(String.valueOf(jdkSerializationRedisSerializer));Jackson2JsonRedisSerializer<Serializable> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Serializable.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);RedisSerializer<Object> genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();RedisSerializer<Serializable> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Serializable.class);template.setEnableDefaultSerializer(true);template.setDefaultSerializer(fastJsonRedisSerializer);template.setStringSerializer(stringRedisSerializer);template.setKeySerializer(stringRedisSerializer);template.setValueSerializer(genericJackson2JsonRedisSerializer);template.setHashKeySerializer(stringRedisSerializer);template.setHashValueSerializer(fastJsonRedisSerializer);}}?
?
應用Java
import com.alibaba.fastjson.JSON; import com.chinamobile.framework.redis.enums.BaseRedisEnum; import com.chinamobile.framework.redis.service.RedisObjectService; import com.chinamobile.framework.redis.vo.BaseRedisVo; import com.chinamobile.scm.order.service.RedisService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.support.atomic.RedisAtomicLong; import org.springframework.stereotype.Service; import org.springframework.util.Assert;import java.io.Serializable; import java.util.*; import java.util.concurrent.TimeUnit;@Service @ConditionalOnProperty("spring.redis.host") @Slf4j public class RedisServiceImpl<T extends Serializable> implements RedisService<T> {@Value("${spring.application.name}")String app;@AutowiredRedisTemplate<String, String> redisTemplate;@Overridepublic final String getApp() {return app;}@Overridepublic final RedisTemplate<String, String> getRedisTemplate() {return redisTemplate;}@Overridepublic void set(String key, String value) {Assert.notNull(key, "參數id不能為空");log.debug("set key:" + key + ",value:" + value);redisTemplate.opsForValue().set(key, value);}@Overridepublic String get(String key) {Assert.notNull(key, "參數key不能為空");log.info("get id:" + key +" start get value");Object obv ="";try {obv = redisTemplate.opsForValue().get(key);}catch (Exception exp){String messageerr=exp.getMessage();if(messageerr.indexOf("loginUserId")>-1){int idxloginUserId=messageerr.indexOf("loginUserId");String loginstartidx=messageerr.substring(idxloginUserId-1);int idxsencond=loginstartidx.indexOf(",");String endindexstr=loginstartidx.substring(0,idxsencond);String valuestr="{"+endindexstr.replaceAll(",","")+"}";obv=valuestr;}}// Could not read JSON: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'\n at [Source: (byte[])\"{\"timestamp\":\"1607414522055\",\"loginUserId\":\"zhanghuan6@tj.cmcc\",\"loginIp\":\"220.196.49.26\",\"loginAddrCode\":\"TJ\",\"loginType\":\"LOGINPAGE\"}\String vo=(String)obv;log.info("get id:" + key + ",value:" + vo);return vo;} public static void main(String[] args) {String messageerr="Could not read JSON: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'\n at [Source: (byte[])\"{\"timestamp\":\"1607414522055\",\"loginUserId\":\"zhanghuan6@tj.cmcc\",\"loginIp\":\"220.196.49.26\",\"loginAddrCode\":\"TJ\",\"loginType\":\"LOGINPAGE\"}\"; line: 1, column: 135]; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'\n at [Source: (byte[])\"{\"timestamp\":\"1607414522055\",\"loginUserId\":\"zhanghuan6@tj.cmcc\",\"loginIp\":\"220.196.49.26\",\"loginAddrCode\":\"TJ\",\"loginType\":\"LOGINPAGE\"}\"; line: 1, column: 135]";if(messageerr.indexOf("loginUserId")>-1){int idxloginUserId=messageerr.indexOf("loginUserId");String loginstartidx=messageerr.substring(idxloginUserId-1);int idxsencond=loginstartidx.indexOf(",");String endindexstr=loginstartidx.substring(0,idxsencond);String valuestr="{"+endindexstr.replaceAll(",","")+"}";System.out.println(valuestr);}}}?
?
基礎Service Redis類
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.chinamobile.framework.common.constant.CommonConstant; import com.chinamobile.framework.redis.enums.BaseRedisEnum; import com.chinamobile.framework.redis.vo.BaseRedisVo; import lombok.SneakyThrows; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils;import java.io.Serializable; import java.util.*; import java.util.concurrent.TimeUnit;@SuppressWarnings("unused") public interface BaseRedisService {String getApp();RedisTemplate<String, String> getRedisTemplate();/*** 根據module和id生成redis的key* @param module 業務場景code* @param id 主鍵* @return key*/default String generateKey(BaseRedisEnum module, String id) {StringBuilder buffer = new StringBuilder();Assert.hasText(getApp(), "配置spring.application.name錯誤");Assert.notNull(module, "參數module錯誤");Assert.notNull(id, "參數id不能為空");Assert.isTrue(!id.startsWith("\"") && !id.endsWith("\""), "id不能以引號開始且不能以引號結束");buffer.append(getApp()).append(CommonConstant.COLON).append(module.getKey());if (!StringUtils.isEmpty(id)) {return buffer.append(CommonConstant.COLON).append(id).toString();}return "\"" + buffer.toString() + "\"";//必須加引號,超過32個字符}default Collection<String> generateKeys(BaseRedisEnum module, Collection<String> ids) {if (ids == null) {return null;}Collection<String> keys = new ArrayList<>();for (String id : ids) {keys.add(generateKey(module, id));}return keys;}/*** 刪除緩存 根據key精確匹配刪除* @param module 業務場景code* @param id 主鍵*/default void delete(BaseRedisEnum module, String id) {this.delete(module, new String[]{id});}/*** 刪除緩存 根據keys批量精確匹配刪除* @param module 業務場景code* @param ids 主鍵數組*/default void delete(BaseRedisEnum module, String[] ids) {if (ids != null && ids.length > 0) {List<String> delKeys = new ArrayList<>();for (String id : ids) {if (!StringUtils.hasText(id)) {delKeys.add(generateKey(module, id));}}getRedisTemplate().delete(delKeys);}}/*** 指定緩存的失效時間* @param module 業務場景code* @param id 主鍵* @param second 有效時間,單位秒* @return 是否成功*/default Boolean expire(BaseRedisEnum module, String id, long second) {return expire(module, id, second, TimeUnit.SECONDS);}default Boolean expire(BaseRedisEnum module, String id, long time, TimeUnit unit) {Assert.notNull(id, "參數id不能為空");String key = generateKey(module, id);if (time > 0) {return getRedisTemplate().expire(key, time, unit);}return Boolean.FALSE;}/*** 查看key是否存在* @param module 業務場景code* @param id 主鍵* @return redis的key集合*/default Set<String> keys(BaseRedisEnum module, String id) {StringBuilder pattern = new StringBuilder();Assert.hasText(getApp(), "配置spring.application.name錯誤");Assert.notNull(module, "參數module錯誤");pattern.append(getApp()).append(CommonConstant.COLON).append(module.getKey());if (!StringUtils.isEmpty(id)) {pattern.append(CommonConstant.COLON).append(id);}pattern.append("*");return getRedisTemplate().keys(pattern.toString());}/*** 檢查key是否存在* @param module 業務場景code* @param id 主鍵* @return 是否存在,true存在,false不存在*/default boolean exist(BaseRedisEnum module, String id) {Assert.notNull(id, "參數id不能為空");String key = generateKey(module, id);Boolean bool = getRedisTemplate().hasKey(key);if (bool == null) {return false;}return bool;}default <T extends Serializable> Map<String, String> generateMap(BaseRedisEnum module, Map<String, T> data) {Map<String, String> map = new HashMap<>();for (Map.Entry<String, T> entry : data.entrySet()) {BaseRedisVo<T> vo = new BaseRedisVo<>(entry.getValue());map.put(generateKey(module, entry.getKey()), JSON.toJSONString(vo));}return map;}@SuppressWarnings("unchecked")@SneakyThrowsdefault <T> T transfer(String vo) {if (vo == null) {return null;}JSONObject json = JSON.parseObject(vo);BaseRedisVo<JSONObject> v = json.toJavaObject(BaseRedisVo.class);Class<T> clazz = (Class<T>) Class.forName(v.getClazzT());JSONObject data = v.getData();return data.toJavaObject(clazz);}@SneakyThrowsdefault <T extends Serializable> List<T> transfer(List<String> list) {List<T> result = new ArrayList<>();if (CollectionUtils.isEmpty(list)) {return result;}for (String vo : list) {if (vo != null) {result.add(transfer(vo));}}return result;}@SneakyThrowsdefault <T extends Serializable> Set<T> transfer(Set<String> set) {Set<T> result = new HashSet<>();if (CollectionUtils.isEmpty(set)) {return result;}for (String vo : set) {if (vo != null) {result.add(transfer(vo));}}return result;}}調用方分類
import com.alibaba.fastjson.JSON; import com.chinamobile.framework.redis.enums.BaseRedisEnum; import com.chinamobile.framework.redis.service.RedisObjectService; import com.chinamobile.framework.redis.vo.BaseRedisVo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.support.atomic.RedisAtomicLong; import org.springframework.stereotype.Service; import org.springframework.util.Assert;import java.io.Serializable; import java.util.*; import java.util.concurrent.TimeUnit;@Service @ConditionalOnProperty("spring.redis.host") @Slf4j public final class RedisObjectServiceImpl<T extends Serializable> implements RedisObjectService<T> {@Value("${spring.application.name}")String app;@AutowiredRedisTemplate<String, String> redisTemplate;@Overridepublic final String getApp() {return app;}@Overridepublic final RedisTemplate<String, String> getRedisTemplate() {return redisTemplate;}@Overridepublic final void set(BaseRedisEnum module, String id, T value) {Assert.notNull(id, "參數id不能為空");BaseRedisVo<T> vo = new BaseRedisVo<>(value);String v = JSON.toJSONString(vo);log.debug("set id:" + id + ",value:" + v);redisTemplate.opsForValue().set(generateKey(module, id), v);}@Overridepublic final void set(BaseRedisEnum module, String id, T value, long second) {Assert.notNull(id, "參數id不能為空");BaseRedisVo<T> vo = new BaseRedisVo<>(value);String key = generateKey(module, id);redisTemplate.opsForValue().set(key, JSON.toJSONString(vo), second, TimeUnit.SECONDS);}@Overridepublic final Boolean setIfAbsent(BaseRedisEnum module, String id, T value) {Assert.notNull(id, "參數id不能為空");BaseRedisVo<T> vo = new BaseRedisVo<>(value);return redisTemplate.opsForValue().setIfAbsent(generateKey(module, id), JSON.toJSONString(vo));}@Overridepublic final void multiSet(BaseRedisEnum module, Map<String, T> data) {Assert.notEmpty(data, "參數data不能為空");redisTemplate.opsForValue().multiSet(generateMap(module, data));}@Overridepublic final Boolean multiSetIfAbsent(BaseRedisEnum module, Map<String, T> data) {Assert.notEmpty(data, "參數data不能為空");return redisTemplate.opsForValue().multiSetIfAbsent(generateMap(module, data));}@Overridepublic final T get(BaseRedisEnum module, String id) {Assert.notNull(id, "參數id不能為空");String vo = redisTemplate.opsForValue().get(generateKey(module, id));log.debug("get id:" + id + ",value:" + vo);return transfer(vo);}@Overridepublic final T getAndSet(BaseRedisEnum module, String id, T value) {Assert.notNull(id, "參數id不能為空");BaseRedisVo<T> v = new BaseRedisVo<>(value);String vo = redisTemplate.opsForValue().getAndSet(generateKey(module, id), JSON.toJSONString(v));return transfer(vo);}@Overridepublic final List<T> multiGet(BaseRedisEnum module, Collection<String> ids) {Assert.notEmpty(ids, "參數ids不能為空");List<String> list = redisTemplate.opsForValue().multiGet(generateKeys(module, ids));return transfer(list);}@Overridepublic final long generate(BaseRedisEnum module, String id) {Assert.notNull(id, "參數id不能為空");return generate(module, id, 1);}@Overridepublic final long generate(BaseRedisEnum module, String id, Date expireTime) {Assert.notNull(id, "參數id不能為空");return generate(module, id, 1, expireTime);}@Overridepublic final long generate(BaseRedisEnum module, String id, int increment) {Assert.notNull(id, "參數id不能為空");RedisAtomicLong counter = new RedisAtomicLong(generateKey(module, id), Objects.requireNonNull(redisTemplate.getConnectionFactory()));return counter.addAndGet(increment);}@Overridepublic final long generate(BaseRedisEnum module, String id, int increment, Date expireTime) {Assert.notNull(id, "參數id不能為空");RedisAtomicLong counter = new RedisAtomicLong(generateKey(module, id), Objects.requireNonNull(redisTemplate.getConnectionFactory()));counter.expireAt(expireTime);return counter.addAndGet(increment);}}?
分布式鎖
import com.chinamobile.framework.common.constant.CommonConstant; import com.chinamobile.framework.common.exception.CommonException; import com.chinamobile.framework.redis.enums.CommonDistributedLockEnum; import com.chinamobile.framework.redis.service.CommonDistributedLockService; import com.chinamobile.framework.redis.service.RedisObjectService; import com.chinamobile.framework.utils.ProjectUtil; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils;import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit;@Service @ConditionalOnProperty("spring.redis.host") @Slf4j public class CommonDistributedLockServiceImpl implements CommonDistributedLockService {private static final long DEFAULT_EXPIRE = -1;@Resourceprivate ApplicationContext applicationContext;@Resourceprivate RedisObjectService<String> redisObjectService;@Overridepublic boolean lock(String id) {log.debug("lock:id=" + id);return this.lock(id, DEFAULT_EXPIRE);}@SneakyThrows@Overridepublic boolean lock(String id, long second) {log.debug("lock:id=" + id + ",time(s)=" + second);if (!isValidLock(id)) {throw new CommonException(CommonConstant.TOO_FREQUENT_CODE, "操作太頻繁,請稍候再試。");}String ip = ProjectUtil.getIp(applicationContext.getEnvironment());String port = ProjectUtil.getPort(applicationContext.getEnvironment());String value = ip + "_" + port;if (!redisObjectService.setIfAbsent(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id, value)) {throw new CommonException(CommonConstant.TOO_FREQUENT_CODE, "操作太頻繁,請稍候再試。");}if (second > 0) {return redisObjectService.expire(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id, second);}return true;}@Overridepublic void unlock(String id) {log.debug("unlock:id=" + id); // redisObjectService.delete(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id);redisObjectService.expire(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id, 1, TimeUnit.NANOSECONDS);//改成1毫秒過期}@SneakyThrows@Overridepublic void cleanLock() {List<String> deletes = new ArrayList<>();Set<String> keys = redisObjectService.keys(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, null);if (!CollectionUtils.isEmpty(keys)) {String ip = ProjectUtil.getIp(applicationContext.getEnvironment());String port = ProjectUtil.getPort(applicationContext.getEnvironment());String data = ip + "_" + port;for (String key : keys) {String vo = redisObjectService.getRedisTemplate().opsForValue().get(key);if (vo != null && data.equals(redisObjectService.transfer(vo))) {deletes.add(key);redisObjectService.getRedisTemplate().expire(key, 1, TimeUnit.NANOSECONDS);}}log.info("cleanLock:ids=" + deletes); // redisObjectService.getRedisTemplate().delete(deletes);}}@Overridepublic boolean isValidLock(String id) {log.debug("isValidLock:id=" + id);return !redisObjectService.exist(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id);}}?
總結
以上是生活随笔為你收集整理的spring boot redis 分布式锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CompareAndSwap原子操作原理
- 下一篇: CAS的ABA问题描述 AtomicSt