基于redis的规则流水号生成(二)

上文主要包含的最基础的redis自增生成规则流水号。

但仔细分析会发现有几个问题:

1、流水号规则调整了怎么办?只能去改代码?

2、redis切换了怎么办?数据不迁移或者redis挂了从头生成流水号会导致重复怎么办?

3、redis异常怎么办?

 

设计方案:

1、建立流水号规则配置表:

CREATE TABLE `crl_serial_number_rule` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `rule_code` varchar(30)  NOT NULL COMMENT '规则编码',
  `rule_desc` varchar(60)  NOT NULL COMMENT '规则描述',
  `prefix` varchar(10)  NULL COMMENT '前缀',
  `date_formatter` varchar(20) NULL COMMENT '日期格式',
  `random_mark` varchar(10)  NOT NULL COMMENT '随机位',
  `number_type` varchar(10)  NOT NULL COMMENT '序号类型,numberTypeEnum:RANDOM 随机;ORDER 顺序',
  `number_length` varchar(2)  NOT NULL COMMENT '序号位数',  
  `number_start` varchar(10)  DEFAULT NULL COMMENT '序号起始位',
  `number_end` varchar(10)  DEFAULT NULL COMMENT '序号结束位',
  `current_number` varchar(10)  DEFAULT NULL COMMENT '当前序号',
  `created_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `modified_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `uniq_idx_rule_code` (`rule_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='流水号规则表';

2、针对redis切换,在1中其实已经设计好数据库表,其中的随机位即可降低切换时的重复概率。

3、针对redis异常可以做出补充方案,确保可以生成一个流水号。

附代码(部分引入涉及敏感工程已去除):

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit;


/**
 * @description:
 * @author: xxx
 * @date: 2018-06-13 下午3:17
 */
@Service
public class CrlSerialNumberServiceImpl implements CrlSerialNumberService {
    private static final Logger logger = LoggerFactory.getLogger(CrlSerialNumberServiceImpl.class);
    /*每次增长数值*/
    private static int increase=100;
    /*超时时间:代码中与TimeUnit.SECONDS共用*/
    private static int expireTime=10;
    /*重试次数*/
    private static int retryCount=5;
    @Resource
    private CrlSerialNumberRuleService crlSerialNumberRuleService;

    @Resource
    private DistributedLocker distributedLocker;

    @Resource
    private CacheBizService cacheClient;

    @Override
    public String generateSerialNumberByRuleCode(String ruleCode){
        String methodDesc = "按规则编码生成流水号";
        StringBuilder crlSerialNumber = new StringBuilder();
        logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|查询流水号规则开始" + "|ruleCode:" + ruleCode);
        CrlSerialNumberRule crlSerialNumberRule = crlSerialNumberRuleService.getSerialNumberRuleByRuleCode(ruleCode);
        if (null == crlSerialNumberRule) {
            logger.warn("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|查询流水号规则为空,请检查流水号规则配置" + "|ruleCode:" + ruleCode);
            return crlSerialNumber.append("ERR").append(new SimpleDateFormat(DateFormatterEnum.yyMMddHHmmss.getCode()).format(new Date())).append(String.valueOf(new Random().nextInt(9))).append(new Random().nextInt(getMaxValueString("8"))).toString();
        }
        logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|查询流水号规则结束" + "|ruleCode:" + ruleCode + "|crlSerialNumberRule:" + crlSerialNumberRule);

        String dateFormatter = crlSerialNumberRule.getDateFormatter();
        String dateString="";
        if (StringUtils.isNotBlank(dateFormatter)) {
            dateString = new SimpleDateFormat(dateFormatter).format(new Date());
        }

        if (NumberTypeEnum.RANDOM.getCode().equals(crlSerialNumberRule.getNumberType())) {
            String randomMark="";
            if(StatusEnum.VALID.getCode().equals(crlSerialNumberRule.getRandomMark())){
                randomMark=String.valueOf(new Random().nextInt(9));
            }
            int random=new Random().nextInt(getMaxValueString(crlSerialNumberRule.getNumberLength()));
            return crlSerialNumber.append(crlSerialNumberRule.getPrefix()).append(dateString).append(randomMark).append(getCurrentValueString(crlSerialNumberRule.getNumberLength(),String.valueOf(random))).toString();
        } else if (NumberTypeEnum.ORDER.getCode().equals(crlSerialNumberRule.getNumberType())) {

            try {
                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|加分布式锁开始" + "|ruleCode:" + ruleCode);
                CrlCommonResponse crlCommonResponse = distributedLocker.lockRetry(ruleCode, expireTime,TimeUnit.SECONDS,retryCount);
                if(!crlCommonResponse.isSuccess()){
                    throw new Exception(methodDesc+"加分布式锁失败");
                }
                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|加分布式锁结束" + "|ruleCode:" + ruleCode + "|crlCommonResponse:"+crlCommonResponse);

                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|获取当前key的value值开始" + "|ruleCode:" + ruleCode+"|dateFormatter:"+dateFormatter);
                String cacheCurrentValue=cacheClient.rpop(crlSerialNumberRule.getPrefix() + dateFormatter + crlSerialNumberRule.getNumberLength());
                if(StringUtils.isBlank(cacheCurrentValue)){
                    /*1 初始化*/
                    int numberStart;
                    int numberEnd;
                    if (StringUtils.isBlank(crlSerialNumberRule.getCurrentNumber())) {
                        logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|数据库当前值不存在|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                        /*1.1 赋初值*/
                        numberStart=1;
                        numberEnd=numberStart+increase;
                        /*1.2 起始值处理*/
                        if (StringUtils.isNotBlank(crlSerialNumberRule.getNumberStart())) {
                            logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了起始值,设置为队列起始值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                            numberStart=Integer.parseInt(crlSerialNumberRule.getNumberStart());
                            numberEnd=numberStart+increase;
                        }else{
                            logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号未限制起始值,设置1为队列起始值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                        }
                        /*1.3 结束值处理*/
                        if (StringUtils.isNotBlank(crlSerialNumberRule.getNumberEnd())) {
                            if(numberEnd>Integer.parseInt(crlSerialNumberRule.getNumberEnd())){
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了结束值,比队列结束值小,设置为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                                numberEnd=Integer.parseInt(crlSerialNumberRule.getNumberEnd());
                            }else{
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号未限制结束值,比队列结束值大,设置队列起始值+99为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                            }
                        }

                    }else{
                        logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|数据库当前值存在|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                        Integer currentNumber= Integer.valueOf(crlSerialNumberRule.getCurrentNumber());
                        /*2.1 赋初值*/
                        numberStart=1+currentNumber;
                        numberEnd=1+currentNumber+increase;
                        /*2.2.1 能够够获取值,左空右配置*/
                        if(StringUtils.isBlank(crlSerialNumberRule.getNumberStart())&&StringUtils.isNotBlank(crlSerialNumberRule.getNumberEnd())){
                            /*2.2.1.1 当前值与流水号结束值*/
                            if (currentNumber>=Integer.parseInt(crlSerialNumberRule.getNumberEnd())) {
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了结束值|数据库当前值达到位数最大值,设置1为队列起始值|ruleCode:" + ruleCode+"|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|currentNumber:" + currentNumber);
                                numberStart=1;
                                numberEnd=numberStart+increase;
                            }else{
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了最大值|数据库当前值未达到位数最大值,设置数据库当前值+1为队列起始值|ruleCode:" + ruleCode+"|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue);
                            }
                            /*2.2.1.2 队列结束值与流水号结束值*/
                            if(numberEnd>Integer.parseInt(crlSerialNumberRule.getNumberEnd())){
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了结束值,比队列结束值小,则设置为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                                numberEnd=Integer.parseInt(crlSerialNumberRule.getNumberEnd());
                            }else{
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了结束值,比队列结束值大,设置队列起始值+99为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                            }

                        }
                        /*2.2.2 能够够获取值,左配置右空*/
                        if(StringUtils.isNotBlank(crlSerialNumberRule.getNumberStart())&&StringUtils.isBlank(crlSerialNumberRule.getNumberEnd())){
                            /*2.2.2.1 当前值与流水号起始值*/
                            if(currentNumbergetMaxValueString(crlSerialNumberRule.getNumberLength())){
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号未限制结束值,长度最大值设置为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                                numberEnd=getMaxValueString(crlSerialNumberRule.getNumberLength());
                            }else{
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号未限制结束值,设置队列起始值+99为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                            }
                        }

                        /*2.2.3 能够够获取值,左右区间未配置*/
                        if(StringUtils.isBlank(crlSerialNumberRule.getNumberStart())&&StringUtils.isBlank(crlSerialNumberRule.getNumberEnd())){
                            /*2.2.3.1 当前值与长度最大值*/
                            if (currentNumber==getMaxValueString(crlSerialNumberRule.getNumberLength())) {
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|未配置流水号区间,数据库当前值达到位数最大值,从1自增|ruleCode:" + ruleCode+"|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|currentNumber:" + currentNumber);
                                numberStart=1;
                                numberEnd=numberStart+increase;
                            }
                            /*2.2.3.2 队列结束值与长度最大值*/
                            if(numberEnd>getMaxValueString(crlSerialNumberRule.getNumberLength())){
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|未配置流水号区间,长度最大值设置为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                                numberEnd=getMaxValueString(crlSerialNumberRule.getNumberLength());
                            }else{
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|未配置流水号区间,设置队列起始值+99为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                            }
                        }
                        /*2.2.4 能够获取值,左右区间配置*/
                        if(StringUtils.isNotBlank(crlSerialNumberRule.getNumberStart())&&StringUtils.isNotBlank(crlSerialNumberRule.getNumberEnd())){
                            /*2.2.4.1 当前值与流水号起始值*/
                            if(currentNumber=Integer.parseInt(crlSerialNumberRule.getNumberEnd())) {
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了结束值|数据库当前值达到位数最大值,设置1为队列起始值|ruleCode:" + ruleCode+"|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|currentNumber:" + currentNumber);
                                numberStart=Integer.parseInt(crlSerialNumberRule.getNumberStart());
                                numberEnd=numberStart+increase;
                            }

                            /*2.2.4.2 队列结束值与流水号结束值*/
                            if(numberEnd>Integer.parseInt(crlSerialNumberRule.getNumberEnd())){
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|配置了流水号区间,比队列结束值小,则设置为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                                numberEnd=Integer.parseInt(crlSerialNumberRule.getNumberEnd());
                            }else{
                                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|配置了流水号区间,比队列结束值大,设置队列起始值+99为队列结束值|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber());
                            }
                        }
                    }
                    /*2 调用公共方法*/
                    cacheCurrentValue = updateAndPushAndPop(ruleCode,crlSerialNumberRule, dateFormatter, cacheCurrentValue, numberStart, numberEnd);
                }
                String randomMark="";
                if(StatusEnum.VALID.getCode().equals(crlSerialNumberRule.getRandomMark())){
                    randomMark=String.valueOf(new Random().nextInt(9));
                }

                String currentValue = getCurrentValueString(crlSerialNumberRule.getNumberLength(), cacheCurrentValue);

                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|释放分布式锁开始" + "|ruleCode:" + ruleCode + "|resData:"+crlCommonResponse.getResData());
                distributedLocker.unlock(ruleCode, crlCommonResponse.getResData());
                logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|释放分布式锁结束" + "|ruleCode:" + ruleCode + "|resData:"+crlCommonResponse.getResData());

                return crlSerialNumber.append(crlSerialNumberRule.getPrefix()).append(dateString).append(randomMark).append(currentValue).toString();

            } catch (Exception e) {
                logger.error("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "异常" + "|ruleCode:" + ruleCode + "|exception:" + e);
                int i = new Random().nextInt(getMaxValueString(crlSerialNumberRule.getNumberLength()));
                return crlSerialNumber.append(crlSerialNumberRule.getPrefix()).append(dateString).append(9).append(String.valueOf(i)).toString();
            }
        }
        return crlSerialNumber.toString();
    }

    /**
     * 更新数据库当前值、队列入队处理、队列出队获取
     * @param ruleCode
     * @param crlSerialNumberRule
     * @param dateFormatter
     * @param cacheCurrentValue
     * @param numberStart
     * @param numberEnd
     * @return
     * @throws Exception
     */
    private String updateAndPushAndPop(String ruleCode,CrlSerialNumberRule crlSerialNumberRule, String dateFormatter, String cacheCurrentValue, Integer numberStart, Integer numberEnd) throws Exception {
        String methodDesc = "按规则编码生成流水号";
        /*2.1 更新数据库当前值为队列结束值*/
        logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|更新流水号规则中当前值开始" + "|ruleCode:" + ruleCode+"|numberEnd:"+numberEnd);
        int updateNum = crlSerialNumberRuleService.updateCurrentNumberByRuleCode(String.valueOf(numberEnd), ruleCode);
        if(updateNum<=0){
            throw new Exception(methodDesc+"更新流水号规则中当前值失败");
        }
        logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|更新流水号规则中当前值结束" + "|ruleCode:" + ruleCode+"|numberEnd:"+numberEnd+"|updateNum:"+updateNum);

        /*2.2 队列入队开始*/
        logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了起始值,入队队列开始|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber()+"|numberStart:"+numberStart+"|numberEnd:"+numberEnd);
        for(int i=numberStart;i<=numberEnd;i++){
            cacheClient.lpush(crlSerialNumberRule.getPrefix() + dateFormatter + crlSerialNumberRule.getNumberLength(), String.valueOf(i));
        }
        logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了起始值,入队队列结束|ruleCode:" + ruleCode + "|prefix:" + crlSerialNumberRule.getPrefix() + "|length:" + crlSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue+"|currentNumber:"+crlSerialNumberRule.getCurrentNumber()+"|numberStart:"+numberStart+"|numberEnd:"+numberEnd);

        /*2.3 二次出队获取当前缓存值*/
        String str = cacheClient.rpop(crlSerialNumberRule.getPrefix() + dateFormatter + crlSerialNumberRule.getNumberLength());
        if (StringUtils.isNotBlank(str)) {
            cacheCurrentValue=str;
        }else{
            throw new Exception(methodDesc + "入队数据后rpop命令执行失败");
        }
        return cacheCurrentValue;
    }

    /**
     * 字符串补足方法
     * @param length
     * @param currentValue
     * @return
     */
    private  String getCurrentValueString(String length, String currentValue) {
        String cValue=currentValue;
        for(int i=cValue.length();i

 

你可能感兴趣的:(java开发,redis,流水号生成,redis,可配置)