基于Redis自增函数生成分布式单号

1. 背景

在分布式系统中,不可避免的需要协调多个服务器节点生成分布式的单号问题,如果采取单节点的方式,可能会出现单号重复的问题。从具体的实现方式而言,可以采取数据库的方案,但这个可能存在多节点同时争抢同一条数据造成死锁的问题。我们这里采取分布式中间件Redis的自增函数,实现分布式单号的生成。

2. 业务要求

分布式的单号生成组件,需要根据不同的业务要求生成不同规则的业务单号,并且在高并发的场景下保证单号唯一。

3. 分布式单号组件的实现

3.1 单号的生成业务规则接口
/**
 * 分布式单号的格式化前缀接口
 */
public interface DistributedSeqNoFormat<T>{

    /**
     * 根据特定的业务对象类型,进行定制化的单号前缀格式化输出
     * @param t 单号的前缀格式化操作对象
     * @return 单号的前缀
     * @author chenyz18
     */
    String format(T t);
}
3.2 单号生成器
/**
 * 分布式单号生成器
 *
 */
public class DistributedSeqNoGenerator <T>{

    private static final Logger logger = LoggerFactory.getLogger(DistributedSeqNoGenerator.class);

    /**
     * 根据业务定制化的前缀格式化生成器,生成分布式的业务单号
     * @param format 格式化生成器的实现实例
     * @param expiredTime 单号的前缀超时时间, 如果设置为-1则不会超时
     * @param t 格式化生成器的格式化业务操作类型实例对象
     //* @param <T> 格式化生成器的格式化业务操作类型
     * @return 分布式的单号
     */
    public String getBizSerialNo(DistributedSeqNoFormat<T> format, T t, Long expiredTime) {
        StringBuilder serialNo = new StringBuilder();
        String prefix = format.format(t);
        serialNo.append(prefix);
        serialNo.append(getCommonSerialNo(prefix, expiredTime));
        return serialNo.toString();
    }

    /**
     * 根据业务定制化的前缀格式化生成器,生成定长的分布式业务单号
     * @param format 格式化生成器的实现实例
     * @param t 格式化生成器的格式化业务操作类型实例对象
     * @param maxSerialNo 序列化的对打长度,如果生成的序列号不足该长度,则补零
     * @param expiredTime 单号的前缀超时时间, 如果设置为-1则不会超时
     //* @param <T> 格式化生成器的格式化业务操作类型
     * @return 分布式的单号
     */
    public   String getFixedLengthBizSerialNo(DistributedSeqNoFormat<T> format, T t, Integer maxSerialNo, Long expiredTime) {
        String prefix = format.format(t);
        Long seqNo = getCommonSerialNo(prefix, expiredTime);

        int startNoLen = Long.toString(seqNo).length();
        if (startNoLen <= maxSerialNo) {
            String tmpStr = "";
            //空缺的为止用0补全
            for (int i = 0; i < (maxSerialNo - startNoLen); i++) {
                tmpStr = tmpStr + "0";
            }
            String serialNo = tmpStr + seqNo;
            serialNo = prefix + serialNo;
            return serialNo;
        } else {
            return getFixedLengthBizSerialNo(format, t, maxSerialNo, expiredTime);
        }
    }

    /**
     * 根据redis生成分布式的序列号
     *
     * @param prefix      序列号前缀
     * @param expiredTime 序列号的过期时间
     * @return 根据序列号前缀生成的序列号
     */
    public static Long getCommonSerialNo(String prefix, Long expiredTime) {
        if (StringUtils.isBlank(prefix)) {
            throw new IllegalArgumentException("单号前缀不能为空!");
        }
        long resultSeq;

        //get redis client implements instantce 
        CodisImpl cache = (CodisImpl) SpringUtil.getBean("redisBean");
        resultSeq = cache.incr(prefix);

        if (expiredTime > 0) {
            cache.expire(prefix, expiredTime);
        }

        return resultSeq;
    }
}
3.3测试类
	public static String getBillNO( String prefix){
		DistributedSeqNoGenerator<String> generator = new DistributedSeqNoGenerator<>();
		return generator.getFixedLengthBizSerialNo(new DistributedSeqNoFormat<String>() {
			@Override
			public String format(String s) {
				SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
				String str = dateFormat.format(new Date());
				return s+str;
			}
		},prefix, maxSeriaNo,86400L);
	}

实际测试,单秒的并发量维持在8500+的水平,不同的业务场景,可以根据DistributedSeqNoFormat并使用泛型对象来组装不同的业务单号前缀,指定单号的长度,从而满足不同的业务需求。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值