开发一个通用靠谱的单号生成器
单号生成器优化
之前我用Redis开发了一个通用的单号生成器,该单号生成器存了两个key。一个是序号递增的key,一个是记录日期的key。如果当前日期和Redis记录的日期不一致,单号就重置为1,重新从1开始递增。
因为有查日期和自增两个操作,生成序号的功能就需要加锁。起初加的是JVM级别的锁,但对于多实例的系统来说,JVM级别的锁会失效。于是引用了分布式锁。 private long getSuffixCode(String key) { RedissonClient redissonClient = redissonService.getRedissonClient(); RAtomicLong atomicVar = redissonClient.getAtomicLong(key); //获取当前日期 String todayStr = getTodayStr(); //获取redis记录的最大日期 String codeRecord = getCodeRecord(key); //创建分布式锁 RLock lock = redissonClient.getLock(REDIS_LOCK.concat(key)); long value = 0; try { //尝试加锁 if (lock.tryLock(10, 10, TimeUnit.SECONDS)) { if (!atomicVar.isExists()) { atomicVar.set(0); } if (StringUtils.isNotBlank(codeRecord)) { if (!isSameDay(todayStr, codeRecord)) { atomicVar.set(0); } } //记录日期 saveCodeRecord(key, todayStr); value = atomicVar.incrementAndGet(); //记录历史 String historyKey = key.concat(CODE_HISTORY_KEY); redisUtils.hset(historyKey, todayStr, String.valueOf(value)); } } catch (Exception e) { throw new ServerBizException("500", String.format("单号生成异常,key:[%s]", key)); } finally { if (lock.isLocked() && lock.isHeldByCurrentThread()) { lock.unlock(); } } return value; }
这里使用了Redisson API的锁,并且增加功能,记录每天生成的最大序号。核心代码如上。
有兴趣的童鞋可以看看我这篇文章: 我写了一个简单通用的单号生成器
现在决定优化下,重新写一个静态调用的单号生成器。
生成器为静态类CodeUtils。 新单号生成器CodeUtils
主要调用方法如下: public static String generateFullCode(String prefix, int digit) { return generateFullCode(prefix, "yyyyMMdd", "GENERAL_CODE", true, digit); } /** * 单号生成器 * 通用单号生成器 格式 前缀 + YYMMDD + 序号 * * 列如 generatorCode("D","OVERALL",4),当前日期为:2022-08-12 * * 生成为:D202208120001 * * @param prefix 前缀 * @param module 业务模块 * @param digit 编码位数 * @return */ public static String generateFullCode(String prefix, String module, int digit) { return generateFullCode(prefix, "yyyyMMdd", module, true, digit); } /** * 单号生成器 * @param prefix 单号前缀 * @param datePattern 日期格式 * @param module 业务模块 * @param validateTenantId 是否需要验证当前租户id * @param digit 序号位数 * @return */ public static String generateFullCode(String prefix, String datePattern, String module, boolean validateTenantId, int digit) { String suffixCodeStr = getSuffixCodeStr(prefix, datePattern, module, validateTenantId, digit); String todayStr = getTodayStr(datePattern); String[] codes = {prefix, todayStr, suffixCodeStr}; return Stream.of(codes).collect(Collectors.joining()); }
这里我们定义了3种入参的方法generateFullCode,我们开看每种方法的解释: 1.generateFullCode(String prefix, int digit) 该方法两个入参,默认日期格式为:"yyyyMMdd",默认的module为:"GENERAL_CODE"。同时开启租户校验。能自定义前缀和定义编号位数。 2.generateFullCode(String prefix, String module, int digit) 与方法1的区别在于能自定义module。 3.generateFullCode(String prefix, String datePattern, String module, boolean validateTenantId, int digit) 通用的方法,能自定义单号前缀,日期格式,业务模块,租户id是否验证,序号位数。
这里我们每天按日期生成一个key,每个key对应当天日期。单号生成的核心代码如下: public static long generatorCode(String key) { RedissonClient redissonClient = redissonService.getRedissonClient(); RAtomicLong atomicVar = redissonClient.getAtomicLong(key); //设置过期时间2天 atomicVar.expire(2 ,TimeUnit.DAYS); //创建分布式锁 RLock lock = redissonClient.getLock(REDIS_LOCK.concat(key)); long value = 0; try { //尝试加锁 if (lock.tryLock(10, 10, TimeUnit.SECONDS)) { if (!atomicVar.isExists()) { atomicVar.set(0); } value = atomicVar.incrementAndGet(); } } catch (Exception e) { throw new ServerBizException("500", String.format("单号生成异常,key:[%s]", key)); } finally { if (lock.isLocked() && lock.isHeldByCurrentThread()) { lock.unlock(); } } return value; }
如上代码,我们只需要一个key,就可以实现单号自增。功能还是每天递增,第二天重置。
我们设置的key如下: private static final String GET_NEXT_CODE_KEY = "sc:get_code:%s:%s_%s:%s"; private static final String REDIS_LOCK = "sc_code_generator_lock_";
这里用到了两个key。第一个是用来生成单号的,第二个是用来做分布式锁的。
在Redis上的目录如下:
这里Redis key组成形式为:sc:get_code固定值,加上租户id,加上单号前缀,加上模块,最后加上日期。
这里key我们保存两天,当前如果你要永远保存,用来看历史记录。key的有效期设置如下: //设置过期时间2天 atomicVar.expire(2 ,TimeUnit.DAYS);功能测试
调用示例代码如下: @Test public void testGeneratorCodeNew() { String prefix ="TEST"; String module = "C"; String dateStr = "yyyyMMdd"; String code = CodeUtils.generateFullCode(prefix, dateStr, module, false, 4); System.out.println(code); }
如上代码,我们生成了单号为TEST202212220001的单号。
初生辄以冷水浸杀在古代,为何有些重视生育的古人会亲手弑子?阅读此文前,麻烦您点击一下关注,既方便您进行讨论与分享,又给您带来不一样的参与感,感谢您的支持。引言逢年过节时,许多人家中总贴有抱鲶鱼,亦或是脚踩莲花的孩童年画。这些看起来既幼态,
南洲牙刷寄来日,去垢涤烦一金值从古人牙齿保护看古代经济发展阅读此文前,麻烦您点击一下关注,既方便您进行讨论与分享,又给您带来不一样的参与感,感谢您的支持。牙齿是人类重要身体部位之一,许多人的身体是否健康,牙齿的好坏占很大因素。这一点不是现
古代由战马引发战争的深层原因人类历史上,爆发了许许多多的战争。而在这些战争的背后,也有着各种各样的理由。其中,有些战争的理由看上去是颇为奇葩。在中国历史上的西汉时期,就曾经因为马匹而引发了一场大战。一换马不成
凯撒大帝为什么会被暗杀?这是一个很好的问题可以稍微启发性的改变一下问法,例如为什么凯撒大帝被杀(在消灭庞培之后),而屋大维奥古斯都没有(在消灭安东尼之后)?他是被一群罗马参议员刺死的,凯撒被暗杀是因为他正
历史上的今天2023。2。6节日新西兰的国庆日大事件1227年宋理宗向大臣推崇朱熹的四书集注。1689年英国发生光荣革命事件。1909年我国延续二千多年的奴婢制度宣告终结。1922年侵犯中国主权的九国公约在华
一文说清280年乱世五胡乱华十六国南北朝的民族混战和政权更迭从西晋末年匈奴人刘渊建立十六国的第一个政权前赵算起,到公元588年隋文帝杨坚灭南朝的陈,一统南北,这280多年的时间,就是历史上的五胡十六国南北朝并立时期,一个中国历史上的大乱世。
日耳曼人大迁徙对中世纪西欧政治格局有何影响?在整个西欧的发展史上,日耳曼人的大迁徙是西欧发展的一个重大的转折点。日耳曼人作为曾经生活在东欧平原上的蛮族,他们在当时都还没有自己的文字,在体系上还延续着古老的部落体系。但是在迁徙
唐朝的酒政是如何发展起来的?唐朝的酒税,对经济发展有何影响?在阅读此文前,诚邀您点击一下关注,既方便您进行讨论与分享,又给您带来不一样的参与感,感谢您的支持。引言酒在中国历史中占据很高的位置,在物质匮乏的古代,从平民到贵族,每个阶层都离不开
清朝的保甲制度,为何在入关之后难以推广开来?封建王朝时期,统治者为了保障自己的王权统治会想尽各种办法,比如利用儒家文化统一老百姓的思想以便于自己的控制,像这样的手段流传下来的有很多。不过,不同时期有着不同的历史背景,所以统治
从历史上看东北到底有多重要在我国上下五千年历史中,东北地区是不可或缺的一极,但是东北地区是什么时候可以影响到中国的走向的,并在历代中扮演着怎样的角色。高句丽早期及隋唐时期东北真正影响到中国历史进程的要从高句
抗日时期的中国海军舰艇编成前言本文主要叙述在1937年江阴海战前夕中国国民革命海军舰队编制以及各主力舰艇背景民国海军以最悲壮的方式拉开抗日持久战序幕1937年8月12日49艘中国主力军舰进入了长江待命,准备