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

单号生成器优化

之前我用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的单号。

展开阅读全文

页面更新:2024-03-15

标签:单号   生成器   租户   前缀   分布式   位数   序号   日期   代码   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top