利用redis生成自增编号
基本思路
1、利用Redis中RedisAtomicLong类和其方法incrementAndGet()实现自增操作。
2、获取当天剩余的时间转化为秒,设置redis对应key值的失效时间
3、按照编号规则组装数据
遇到的坑
1、编号会存在重复操作,解决方案
(1)在创建RedisAtomicLong类时使用Redisson分布式锁,详见下列代码
(2)、使用下面代码替代RedisAtomicLong操作
Long increment = redisTemplate.opsForValue().increment(key, 1); redisTemplate.expire(key,liveTime, TimeUnit.SECONDS);
2、Redisson分布式锁创建失败,为null,解决方案需要对redisTemplate进行序列化配置
/** * redis key的层级不能超过3层() * 根据前缀+日期+每天的自增编号例如(WF2021041411200001) * * @param prefix * @param key * @param length * @return */ public String getDayIncrCode(String prefix, String key, int length) { String code = ""; String formatDay = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); Long dayEndTime = getAppointDateTimeMills(); //当天失效 long liveTime = (dayEndTime - System.currentTimeMillis()) / 1000; Long incre = getIncre(key, liveTime); String sequence = getSequence(incre, length); if (StrUtil.isNotBlank(prefix)) { code = code + prefix; } code = code + formatDay + sequence; return code; }
获取当天结束的时间戳
/** * 获取指定时间毫秒值 * * @return */ public static Long getAppointDateTimeMills() { Calendar ca = Calendar.getInstance(); //失效的时间 ca.set(Calendar.HOUR_OF_DAY, 23); ca.set(Calendar.MINUTE, 59); ca.set(Calendar.SECOND, 59); long curtime = ca.getTimeInMillis(); return curtime; }
利用Redis中RedisAtomicLong生成自增(核心使用了 redis中的increment方法)
//RedisAtomicLong源码 public long incrementAndGet() { return this.operations.increment(this.key, 1L); }
/** * 获取redis原子自增数据 * * @param key * @param liveTime * @return */ public Long getIncre(String key, long liveTime) { RedisAtomicLong counter = null; RLock lock = null; if (!redisTemplate.hasKey(key)) { try { lock = redissonLock.lock(key, 1); counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); } finally { redissonLock.unlock(lock); } } else { counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); } Long increment = counter.incrementAndGet(); //初始设置过期时间 boolean result = (null == increment || increment.longValue() == 0) && liveTime > 0; if (result) { counter.set(1); counter.expire(liveTime, TimeUnit.SECONDS); increment = 1L; } return increment; }
格式化数据
/** * 补全自增的数据 * * @param seq * @param length * @return */ public static String getSequence(long seq, int length) { String str = String.valueOf(seq); int len = str.length(); // 取决于业务规模,应该不会到达4 if (len >= length) { return str; } int rest = length - len; StringBuilder sb = new StringBuilder(); for (int i = 0; i < rest; i++) { sb.append(0); } sb.append(str); return sb.toString(); }
测试
@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class IdUtilsTest { @Autowired private IdUtils idUtils; @Test public void testSequenceCode() { String key="test:er6666:6666:934"; String formatDay = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); key=key+formatDay; for (int i = 0; i < 20; i++) { String st = idUtils.getDayIncrCode("ST", key, 4); System.out.println(st); } } }
输出结果