上文主要包含的最基础的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<String> crlCommonResponse = distributedLocker.lockRetry(ruleCode, expireTime,TimeUnit.SECON