前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开发一个通用靠谱的单号生成器

开发一个通用靠谱的单号生成器

作者头像
Lvshen
发布2023-03-08 11:36:18
6090
发布2023-03-08 11:36:18
举报

单号生成器优化

之前我用Redis开发了一个通用的单号生成器,该单号生成器存了两个key。一个是序号递增的key,一个是记录日期的key。如果当前日期和Redis记录的日期不一致,单号就重置为1,重新从1开始递增。

因为有查日期和自增两个操作,生成序号的功能就需要加锁。起初加的是JVM级别的锁,但对于多实例的系统来说,JVM级别的锁会失效。于是引用了分布式锁。

代码语言:javascript
复制
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

主要调用方法如下:

代码语言:javascript
复制
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,我们开看每种方法的解释:

代码语言:javascript
复制
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对应当天日期。单号生成的核心代码如下:

代码语言:javascript
复制
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如下:

代码语言:javascript
复制
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的有效期设置如下:

代码语言:javascript
复制
//设置过期时间2天
atomicVar.expire(2 ,TimeUnit.DAYS);

功能测试

调用示例代码如下:

代码语言:javascript
复制
@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的单号。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-12-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Lvshen的技术小屋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 单号生成器优化
  • 新单号生成器CodeUtils
  • 功能测试
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档