基于Redis的redisson框架实现分布式单号,按照有序生成分布式ID(自定义规则生成)

    技术2022-07-12  60

    一、一些业务背景下,业务要求单号需要按照不同的业务进行生成不同前缀单号。那么在分布式的架构下如何自定义单号而且还能保证唯一呢?

    二、当我们在设计分布式锁的时候,我们应该考虑分布式锁至少要满足的一些条件,同时考虑如何高效的设计分布式锁,这里我认为以下几点是必须要考虑的。

    1、互斥

    在分布式高并发的条件下,我们最需要保证,同一时刻只能有一个线程获得锁,这是最基本的一点。

    2、防止死锁

    在分布式高并发的条件下,比如有个线程获得锁的同时,还没有来得及去释放锁,就因为系统故障或者其它原因使它无法执行释放锁的命令,导致其它线程都无法获得锁,造成死锁。

    所以分布式非常有必要设置锁的有效时间,确保系统出现故障后,在一定时间内能够主动去释放锁,避免造成死锁的情况。

    3、性能

    对于访问量大的共享资源,需要考虑减少锁等待的时间,避免导致大量线程阻塞。

    所以在锁的设计时,需要考虑两点。

    1、锁的颗粒度要尽量小。比如你要通过锁来减库存,那这个锁的名称你可以设置成是商品的ID,而不是任取名称。这样这个锁只对当前商品有效,锁的颗粒度小。

    2、锁的范围尽量要小。比如只要锁2行代码就可以解决问题的,那就不要去锁10行代码了。

    4、重入

    我们知道ReentrantLock是可重入锁,那它的特点就是:同一个线程可以重复拿到同一个资源的锁。重入锁非常有利于资源的高效利用。关于这点之后会做演示。

    针对以上Redisson都能很好的满足。下面就代码来实现。

    三、我这边业务是按照 字母+数字   比如W0000001 按照这个顺序自增。

    代码实例

    1、maven坐标

    <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.13.1</version> </dependency>

    2、、创建FormNoTypeEnum 枚举类,业务不同前缀枚举类,用户编号和供应商编号,可以自行扩展。以用户举例

    /** * 生成规则 = 1位字母+7位数字流水码,1开始 */ public enum FormNoTypeEnum { /** * 用户单号: * 固定前缀:S */ USER_ORDER("S" ), /** * 供应商单号: * 固定前缀:P */ SUPPLIER_ORDER("P"), ; /** * 单号前缀 * 为空时填"" */ private String prefix; FormNoTypeEnum(String prefix ) { this.prefix = prefix; } public String getPrefix() { return prefix; } } 3、在application.yml 配置: system: user: key: CURRENT_MAX_USER_CODE_KEY # 用户编号锁存在redis中 key lock: USER_CODE_INC_LOCK # 获取用户编号锁 supplier: key: CURRENT_MAX_SUPPLIER_CODE_KEY # 供应商编码存在redis中 key lock: SUPPLIER_CODE_INC_LOCK # 获取供应商编号锁 4、在service层注入: @Autowired private RedissonClient redissonClient; @Autowired private RedisCache redisCache; // 用户编号存在redis中key @Value("${system.user.key}") private String key; // 用户编号锁 @Value("${system.user.lock}") private String userLock; @Transactional(propagation = Propagation.REQUIRED) public void addUserBO ( UserBO userBO,String supplierId){ BUser buser = new BUser(); BeanUtils.copyProperties(userBO,buser); buser.setsNumber(supplierId); buser.setCreateTime(new Date()); buser.setUserId(sid.nextShort()); String newMaxValue = null; String PRE_USER_CODE = FormNoTypeEnum.USER_ORDER.getPrefix(); //生成编号前缀 RLock lock = redissonClient.getLock(userLock); try { lock.lock(10, TimeUnit.SECONDS); String MaxValue = redisCache.getCacheObject(key); //获取当前最大编码值 if(StringUtils.isNull(MaxValue)){ //从数据库获取 MaxValue = bUserMapper.getUserNumberMax(); if(StringUtils.isNull(MaxValue)){ //没有查询到则初始化编码 MaxValue = PRE_USER_CODE + "0000000"; } } int currentMaxNum = Integer.parseInt(MaxValue.substring(MaxValue.indexOf(PRE_USER_CODE)+1)); currentMaxNum = currentMaxNum + 1; newMaxValue = PRE_USER_CODE + String.format("d", currentMaxNum); //4、将新的最大值同步到redis缓存 redisCache.setCacheObject(key, newMaxValue, 30, TimeUnit.MINUTES); //30分钟 buser.setUserNumber(newMaxValue); bUserMapper.insertBUser(buser); }catch (Exception e){ e.printStackTrace(); throw new CustomException("获取redis分布式锁异常,请联系系统管理员"); }finally { log.info("生成用户编号,释放redis分布式锁"); lock.unlock(); //释放锁 } }

     

    redisCache类; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.*; import org.springframework.stereotype.Component; import java.util.*; import java.util.concurrent.TimeUnit; /** * spring redis 工具类 **/ @SuppressWarnings(value = { "unchecked", "rawtypes" }) @Component public class RedisCache { @Autowired public RedisTemplate redisTemplate; /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @return 缓存的对象 */ public <T> ValueOperations<String, T> setCacheObject(String key, T value) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); operation.set(key, value); return operation; } /** * 缓存基本的对象,Integer、String、实体类等 * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @return 缓存的对象 */ public <T> ValueOperations<String, T> setCacheObject(String key, T value, Integer timeout) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); operation.set(key, value, timeout, TimeUnit.SECONDS); return operation; } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @param timeUnit 时间颗粒度 * @return 缓存的对象 */ public <T> ValueOperations<String, T> setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); operation.set(key, value, timeout, timeUnit); return operation; } /** * 获得缓存的基本对象。 * * @param key 缓存键值 * @return 缓存键值对应的数据 */ public <T> T getCacheObject(String key) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); return operation.get(key); } /** * 删除单个对象 * * @param key */ public void deleteObject(String key) { redisTemplate.delete(key); } /** * 删除集合对象 * * @param collection */ public void deleteObject(Collection collection) { redisTemplate.delete(collection); } /** * 缓存List数据 * * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */ public <T> ListOperations<String, T> setCacheList(String key, List<T> dataList) { ListOperations listOperation = redisTemplate.opsForList(); if (null != dataList) { int size = dataList.size(); for (int i = 0; i < size; i++) { listOperation.leftPush(key, dataList.get(i)); } } return listOperation; } /** * 获得缓存的list对象 * * @param key 缓存的键值 * @return 缓存键值对应的数据 */ public <T> List<T> getCacheList(String key) { List<T> dataList = new ArrayList<T>(); ListOperations<String, T> listOperation = redisTemplate.opsForList(); Long size = listOperation.size(key); for (int i = 0; i < size; i++) { dataList.add(listOperation.index(key, i)); } return dataList; } /** * 缓存Set * * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */ public <T> BoundSetOperations<String, T> setCacheSet(String key, Set<T> dataSet) { BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); Iterator<T> it = dataSet.iterator(); while (it.hasNext()) { setOperation.add(it.next()); } return setOperation; } /** * 获得缓存的set * * @param key * @return */ public <T> Set<T> getCacheSet(String key) { Set<T> dataSet = new HashSet<T>(); BoundSetOperations<String, T> operation = redisTemplate.boundSetOps(key); dataSet = operation.members(); return dataSet; } /** * 缓存Map * * @param key * @param dataMap * @return */ public <T> HashOperations<String, String, T> setCacheMap(String key, Map<String, T> dataMap) { HashOperations hashOperations = redisTemplate.opsForHash(); if (null != dataMap) { for (Map.Entry<String, T> entry : dataMap.entrySet()) { hashOperations.put(key, entry.getKey(), entry.getValue()); } } return hashOperations; } /** * 获得缓存的Map * * @param key * @return */ public <T> Map<String, T> getCacheMap(String key) { Map<String, T> map = redisTemplate.opsForHash().entries(key); return map; } /** * 获得缓存的基本对象列表 * * @param pattern 字符串前缀 * @return 对象列表 */ public Collection<String> keys(String pattern) { return redisTemplate.keys(pattern); } }

    参考:https://www.cnblogs.com/qdhxhz/p/11046905.html

     

     

     

    Processed: 0.011, SQL: 10