单号生成器优化 之前我用Redis开发了一个通用的单号生成器,该单号生成器存了两个key。一个是序号递增的key,一个是记录日期的key。如果当前日期和Redis记录的日期不一致,单号就重置为1,重新从1开始递增。 因为有查日期和自增两个操作,生成序号的功能就需要加锁。起初加的是JVM级别的锁,但对于多实例的系统来说,JVM级别的锁会失效。于是引用了分布式锁。privatelonggetSuffixCode(Stringkey){RedissonClientredissonClientredissonService。getRedissonClient();RAtomicLongatomicVarredissonClient。getAtomicLong(key);获取当前日期StringtodayStrgetTodayStr();获取redis记录的最大日期StringcodeRecordgetCodeRecord(key);创建分布式锁RLocklockredissonClient。getLock(REDISLOCK。concat(key));longvalue0;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);valueatomicVar。incrementAndGet();记录历史StringhistoryKeykey。concat(CODEHISTORYKEY);redisUtils。hset(historyKey,todayStr,String。valueOf(value));}}catch(Exceptione){thrownewServerBizException(500,String。format(单号生成异常,key:〔s〕,key));}finally{if(lock。isLocked()lock。isHeldByCurrentThread()){lock。unlock();}}returnvalue;} 这里使用了RedissonAPI的锁,并且增加功能,记录每天生成的最大序号。核心代码如上。 有兴趣的童鞋可以看看我这篇文章:我写了一个简单通用的单号生成器 现在决定优化下,重新写一个静态调用的单号生成器。 生成器为静态类CodeUtils。新单号生成器CodeUtils 主要调用方法如下:publicstaticStringgenerateFullCode(Stringprefix,intdigit){returngenerateFullCode(prefix,yyyyMMdd,GENERALCODE,true,digit);}单号生成器通用单号生成器格式前缀YYMMDD序号列如generatorCode(D,OVERALL,4),当前日期为:20220812生成为:D202208120001paramprefix前缀parammodule业务模块paramdigit编码位数returnpublicstaticStringgenerateFullCode(Stringprefix,Stringmodule,intdigit){returngenerateFullCode(prefix,yyyyMMdd,module,true,digit);}单号生成器paramprefix单号前缀paramdatePattern日期格式parammodule业务模块paramvalidateTenantId是否需要验证当前租户idparamdigit序号位数returnpublicstaticStringgenerateFullCode(Stringprefix,StringdatePattern,Stringmodule,booleanvalidateTenantId,intdigit){StringsuffixCodeStrgetSuffixCodeStr(prefix,datePattern,module,validateTenantId,digit);StringtodayStrgetTodayStr(datePattern);String〔〕codes{prefix,todayStr,suffixCodeStr};returnStream。of(codes)。collect(Collectors。joining());} 这里我们定义了3种入参的方法generateFullCode,我们开看每种方法的解释:1。generateFullCode(Stringprefix,intdigit)该方法两个入参,默认日期格式为:yyyyMMdd,默认的module为:GENERALCODE。同时开启租户校验。能自定义前缀和定义编号位数。2。generateFullCode(Stringprefix,Stringmodule,intdigit)与方法1的区别在于能自定义module。3。generateFullCode(Stringprefix,StringdatePattern,Stringmodule,booleanvalidateTenantId,intdigit)通用的方法,能自定义单号前缀,日期格式,业务模块,租户id是否验证,序号位数。 这里我们每天按日期生成一个key,每个key对应当天日期。单号生成的核心代码如下:publicstaticlonggeneratorCode(Stringkey){RedissonClientredissonClientredissonService。getRedissonClient();RAtomicLongatomicVarredissonClient。getAtomicLong(key);设置过期时间2天atomicVar。expire(2,TimeUnit。DAYS);创建分布式锁RLocklockredissonClient。getLock(REDISLOCK。concat(key));longvalue0;try{尝试加锁if(lock。tryLock(10,10,TimeUnit。SECONDS)){if(!atomicVar。isExists()){atomicVar。set(0);}valueatomicVar。incrementAndGet();}}catch(Exceptione){thrownewServerBizException(500,String。format(单号生成异常,key:〔s〕,key));}finally{if(lock。isLocked()lock。isHeldByCurrentThread()){lock。unlock();}}returnvalue;} 如上代码,我们只需要一个key,就可以实现单号自增。功能还是每天递增,第二天重置。 我们设置的key如下:privatestaticfinalStringGETNEXTCODEKEYsc:getcode:s:ss:s;privatestaticfinalStringREDISLOCKsccodegeneratorlock; 这里用到了两个key。第一个是用来生成单号的,第二个是用来做分布式锁的。 在Redis上的目录如下: 这里Rediskey组成形式为:sc:getcode固定值,加上租户id,加上单号前缀,加上模块,最后加上日期。 这里key我们保存两天,当前如果你要永远保存,用来看历史记录。key的有效期设置如下:设置过期时间2天atomicVar。expire(2,TimeUnit。DAYS);功能测试 调用示例代码如下:TestpublicvoidtestGeneratorCodeNew(){StringprefixTEST;StringmoduleC;StringdateStryyyyMMdd;StringcodeCodeUtils。generateFullCode(prefix,dateStr,module,false,4);System。out。println(code);} 如上代码,我们生成了单号为TEST202212220001的单号。