单号生成器优化
之前我用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
的单号。
原文始发于微信公众号(Lvshen的技术小屋):开发一个通用靠谱的单号生成器
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/261664.html