概述
针对上次的流水号生成有几处细节优化的地方
1、高并发的时候,在哪个位置加锁?
2、加锁的重试机制如何设置?
3、加锁的重试次数与间隔时间范围应该如何?
4、入队列的优化
1、首先获取一次缓存中的序列号,不要直接加分布式锁,毕竟遇到查不到的情况约1000分之一
2、流水号大约1秒多生成,建议超时时长设置为2-3秒,重试次数可以多次,每次间隔500ms即可。间隔过大,会导致撞锁。间隔太小会导致无用的重试次数太多。
3、一次操作时间《加锁的重试次数*间隔时间<两次操作时间。理由是:若重试的次数*间隔时间比一次操作时间短,则会导致加锁失败。若大于两次操作时间,则造成浪费。
4、入队的方式,除了执行一个个命令外,rpush是支持把数组一次性存入缓存的。
附上代码(敏感引入已去除):
import org.apache.commons.lang3.StringUtils;
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: wzh
* @date: 2019-01-21 上午10:44
*/
@Service("dubheSerialNumberCenterServiceImpl")
public class DubheSerialNumberCenterServiceImpl implements DubheSerialNumberCenterService {
private static final Logger logger = LoggerFactory.getLogger(DubheSerialNumberCenterServiceImpl.class);
/*超时时间:代码中与TimeUnit.SECONDS共用*/
private static long expireTime = 3;
/*重试次数*/
private static int retryCount = 10;
@Resource
private DubheSerialNumberRuleService dubheSerialNumberRuleService;
@Resource
private DistributedLocker distributedLocker;
@Resource
private R2mClusterClient r2mClusterClient;
@Override
public CommonResponse<String> generateSerialNumber(CommonRequest<SerialNumberRequest> req) {
String methodDesc = "DubheSerialNumberCenterServiceImpl|generateSerialNumber|按规则编码生成流水号|";
SerialNumberRequest serialNumberRequest = req.getRequestData();
String ruleCode = serialNumberRequest.getRuleCode();
StringBuilder crlSerialNumber = new StringBuilder();
logger.info(methodDesc + "查询流水号规则开始" + "|ruleCode:" + ruleCode);
DubheSerialNumberRule dubheSerialNumberRule = dubheSerialNumberRuleService.getSerialNumberRuleByParams(serialNumberRequest.getRuleCode());
if (null == dubheSerialNumberRule) {
logger.warn(methodDesc + "查询流水号规则为空,请检查流水号规则配置" + "|ruleCode:" + ruleCode);
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();
return CommonResponse.buildResponse(req, RespCodeEnum.SERIAL_NO_RULE_NOT_EXIST, crlSerialNumber.toString());
}
logger.info(methodDesc +"查询流水号规则结束" + "|ruleCode:" + ruleCode + "|dubheSerialNumberRule:" + dubheSerialNumberRule);
String dateFormatter = dubheSerialNumberRule.getDateFormatter();
String dateString = "";
if (StringUtils.isNotBlank(dateFormatter)) {
dateString = new SimpleDateFormat(dateFormatter).format(new Date());
}
String randomMark = "";
if (0<dubheSerialNumberRule.getRandomMark()) {
int maxNumber=getMaxValueString(dubheSerialNumberRule.getRandomMark());
randomMark = format(dubheSerialNumberRule.getRandomMark(),new Random().nextInt(maxNumber));
}
int random = new Random().nextInt(getMaxValueString(dubheSerialNumberRule.getNumberLength()));
if (NumberTypeEnum.RANDOM.getCode().equals(dubheSerialNumberRule.getNumberType())) {
crlSerialNumber.append(dubheSerialNumberRule.getPrefix()).append(dateString).append(randomMark).append(format(dubheSerialNumberRule.getNumberLength(), random)).toString();
return CommonResponse.buildResponse(req, RespCodeEnum.SUCCESS, crlSerialNumber.toString());
} else if (NumberTypeEnum.ORDER.getCode().equals(dubheSerialNumberRule.getNumberType())) {
/*拼接前缀+时间长度+随机位数*/
crlSerialNumber.append(dubheSerialNumberRule.getPrefix()).append(dateString).append(randomMark);
int increase = dubheSerialNumberRule.getQueueNumber();
String cacheKey=dubheSerialNumberRule.getPrefix() + dateFormatter + dubheSerialNumberRule.getNumberLength();
String redisKey="";
try {
logger.info(methodDesc + "|获取当前key的value值开始" + "|ruleCode:" + ruleCode + "|cacheKey:" + cacheKey);
String cacheCurrentValue = r2mClusterClient.rpop(cacheKey);
if(StringUtils.isNotBlank(cacheCurrentValue)){
logger.info(methodDesc + "|获取当前key的value值|出参|cacheCurrentValue:",cacheCurrentValue,"|入参|cacheKey:" + cacheKey + "|ruleCode:" + ruleCode);
crlSerialNumber.append(format(dubheSerialNumberRule.getNumberLength(), Integer.parseInt(cacheCurrentValue))).toString();
logger.info(methodDesc + "|返回流水号|出参|crlSerialNumber:",crlSerialNumber.toString(),"|入参|ruleCode:" + ruleCode);
return CommonResponse.buildResponse(req, RespCodeEnum.SUCCESS, crlSerialNumber.toString());
}
logger.info(methodDesc + "|获取当前key的value值不存在|出参|cacheCurrentValue:",cacheCurrentValue,"|入参|cacheKey:" + cacheKey + "|ruleCode:" + ruleCode);
/*1 未能从缓存查询,先加锁*/
logger.info(methodDesc + "|加分布式锁开始" + "|ruleCode:" + ruleCode);
CommonResponse<String> crlCommonResponse = distributedLocker.lockRetry(ruleCode, expireTime, TimeUnit.SECONDS, retryCount);
if (!crlCommonResponse.isSuccess()) {
throw new BizException(RespCodeEnum.SERIAL_NO_LOCK_FAIL);
}
redisKey=crlCommonResponse.getResponseData();
logger.info(methodDesc + "|加分布式锁结束" + "|ruleCode:" + ruleCode + "|crlCommonResponse:" + crlCommonResponse);
/*2 再获取一次缓存*/
logger.info(methodDesc + "|获取当前key的value值开始" + "|ruleCode:" + ruleCode + "|cacheKey:" + cacheKey);
cacheCurrentValue = r2mClusterClient.rpop(cacheKey);
if (StringUtils.isNotBlank(cacheCurrentValue)) {
logger.info(methodDesc + "|获取当前key的value值|出参|cacheCurrentValue:", cacheCurrentValue, "|入参|cacheKey:" + cacheKey + "|ruleCode:" + ruleCode);
crlSerialNumber.append(format(dubheSerialNumberRule.getNumberLength(), Integer.parseInt(cacheCurrentValue))).toString();
logger.info(methodDesc + "|返回流水号|出参|crlSerialNumber:", crlSerialNumber.toString(), "|入参|ruleCode:" + ruleCode);
return CommonResponse.buildResponse(req, RespCodeEnum.SUCCESS, crlSerialNumber.toString());
}
logger.info(methodDesc + "|获取当前key的value值不存在|出参|cacheCurrentValue:",cacheCurrentValue,"|入参|cacheKey:" + cacheKey + "|ruleCode:" + ruleCode);
/*3 处理起始值与结束值*/
int numberStart;
int numberEnd;
if (StringUtils.isBlank(dubheSerialNumberRule.getCurrentNumber())) {
logger.info(methodDesc + "|当前缓存值不存在|数据库当前值不存在|ruleCode:" + ruleCode + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue + "|currentNumber:" + dubheSerialNumberRule.getCurrentNumber());
/*3.1 数据库当前值不存在处理*/
numberStart = dubheSerialNumberRule.getNumberStart();
numberEnd = numberStart + increase;
/*3.1.1 结束值处理*/
if (numberEnd > dubheSerialNumberRule.getNumberEnd()) {
logger.info(methodDesc + "|当前缓存值不存在|流水号限制了结束值,比队列结束值小,设置为队列结束值|ruleCode:" + ruleCode + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue + "|currentNumber:" + dubheSerialNumberRule.getCurrentNumber());
numberEnd = dubheSerialNumberRule.getNumberEnd();
} else {
logger.info(methodDesc + "|当前缓存值不存在|流水号未限制结束值,比队列结束值大,设置队列起始值+99为队列结束值|ruleCode:" + ruleCode + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue + "|currentNumber:" + dubheSerialNumberRule.getCurrentNumber());
}
} else {
logger.info(methodDesc + "|当前缓存值不存在|数据库当前值存在|ruleCode:" + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue + "|currentNumber:" + dubheSerialNumberRule.getCurrentNumber());
Integer currentNumber = Integer.valueOf(dubheSerialNumberRule.getCurrentNumber());
/*3.2 数据库当前值存在处理*/
numberStart = 1 + currentNumber;
numberEnd = 1 + currentNumber + increase;
/*3.2.1 当前值与流水号起始值*/
if (currentNumber < dubheSerialNumberRule.getNumberStart()) {
logger.info(methodDesc + "|当前缓存值不存在|配置了流水号区间|可能由于修改了规则导致数据库当前值比左区间小,缓存按起始值set|ruleCode:" + ruleCode + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|currentNumber:" + currentNumber + "|numberStart:" + dubheSerialNumberRule.getNumberStart());
numberStart = dubheSerialNumberRule.getNumberStart();
numberEnd = numberStart + increase;
}
/*3.2.2 当前值与流水号结束值*/
else if (currentNumber >= dubheSerialNumberRule.getNumberEnd()) {
logger.info(methodDesc + "|当前缓存值不存在|流水号限制了结束值|数据库当前值达到位数最大值,设置1为队列起始值|ruleCode:" + ruleCode + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|currentNumber:" + currentNumber);
numberStart = dubheSerialNumberRule.getNumberStart();
numberEnd = numberStart + increase;
}
/*3.2.3 队列结束值与流水号结束值*/
if (numberEnd > dubheSerialNumberRule.getNumberEnd()) {
logger.info(methodDesc + "|当前缓存值不存在|配置了流水号区间,比队列结束值小,则设置为队列结束值|ruleCode:" + ruleCode + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue + "|currentNumber:" + dubheSerialNumberRule.getCurrentNumber());
numberEnd = dubheSerialNumberRule.getNumberEnd();
} else {
logger.info(methodDesc + "|当前缓存值不存在|配置了流水号区间,比队列结束值大,设置队列起始值+99为队列结束值|ruleCode:" + ruleCode + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue + "|currentNumber:" + dubheSerialNumberRule.getCurrentNumber());
}
}
/*4 入队列并更新数据库*/
cacheCurrentValue = updateAndPushAndPop(dubheSerialNumberRule, cacheKey, cacheCurrentValue, numberStart, numberEnd);
crlSerialNumber.append(format(dubheSerialNumberRule.getNumberLength(), Integer.parseInt(cacheCurrentValue))).toString();
/*5 返回流水号*/
logger.info(methodDesc + "|返回流水号|出参|crlSerialNumber:", crlSerialNumber.toString(), "|入参|ruleCode:" + ruleCode);
return CommonResponse.buildResponse(req, RespCodeEnum.SUCCESS, crlSerialNumber.toString());
} catch (Exception e) {
logger.error(methodDesc + "异常|入参|ruleCode:" + ruleCode,"|exception:",e);
crlSerialNumber.append(dubheSerialNumberRule.getPrefix()).append(dateString).append(9).append(format(dubheSerialNumberRule.getNumberLength(),random)).toString();
return CommonResponse.buildResponse(req, RespCodeEnum.SUCCESS, crlSerialNumber.toString());
}finally {
if(StringUtils.isNotBlank(redisKey)){
logger.info(methodDesc + "|释放分布式锁开始" + "|ruleCode:" + ruleCode + "|redisKey:" + redisKey);
distributedLocker.unlock(ruleCode, redisKey);
logger.info(methodDesc + "|释放分布式锁结束" + "|ruleCode:" + ruleCode + "|redisKey:" + redisKey);
}
}
}
return CommonResponse.buildResponse(req, RespCodeEnum.SUCCESS, crlSerialNumber.toString());
}
/**
* String.format效率较低
* @param length
* @param data
* @return
*/
private String format(int length, int data) {
char[] result = new char[length];
for(int i=result.length-1;i>=0;i--) {
result[i] = (char) (data % 10 + 48);
data/=10;
}
return new String(result);
}
/**
* 更新数据库当前值、队列入队处理、队列出队获取
*
* @param dubheSerialNumberRule
* @param cacheKey
* @param cacheCurrentValue
* @param numberStart
* @param numberEnd
* @return
* @throws Exception
*/
private String updateAndPushAndPop(DubheSerialNumberRule dubheSerialNumberRule, String cacheKey, String cacheCurrentValue, Integer numberStart, Integer numberEnd) throws Exception {
String methodDesc = "按规则编码生成流水号";
/*2.1 更新数据库当前值为队列结束值*/
logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|更新流水号规则中当前值开始" + "|ruleCode:" + dubheSerialNumberRule.getRuleCode() + "|numberEnd:" + numberEnd);
int updateNum = dubheSerialNumberRuleService.updateCurrentNumberByParams(String.valueOf(numberEnd), dubheSerialNumberRule.getRuleCode());
if (updateNum <= 0) {
throw new Exception(methodDesc + "更新流水号规则中当前值失败");
}
logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|更新流水号规则中当前值结束" + "|ruleCode:" + dubheSerialNumberRule.getRuleCode() + "|numberEnd:" + numberEnd + "|updateNum:" + updateNum);
/*2.2 队列入队开始*/
logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了起始值,入队队列开始|ruleCode:" + dubheSerialNumberRule.getRuleCode() + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue + "|currentNumber:" + dubheSerialNumberRule.getCurrentNumber() + "|numberStart:" + numberStart + "|numberEnd:" + numberEnd);
String[] info = new String[numberEnd - numberStart];
logger.info("缓存入队:start{},end{}",numberStart+1,numberEnd);
for (int i = 0; i < info.length; i++) {
info[i] =format(dubheSerialNumberRule.getNumberLength(), i + numberStart + 1);
}
r2mClusterClient.lpush(cacheKey, info);
logger.info("CrlSerialNumberRuleServiceImpl|getSerialNumberRuleByRuleCode|" + methodDesc + "|当前缓存值不存在|流水号限制了起始值,入队队列结束|ruleCode:" + dubheSerialNumberRule.getRuleCode() + "|prefix:" + dubheSerialNumberRule.getPrefix() + "|length:" + dubheSerialNumberRule.getNumberLength() + "|cacheCurrentValue:" + cacheCurrentValue + "|currentNumber:" + dubheSerialNumberRule.getCurrentNumber() + "|numberStart:" + numberStart + "|numberEnd:" + numberEnd);
/*2.3 起始值直接返回*/
if (StringUtils.isBlank(cacheCurrentValue)) {
return String.valueOf(numberStart);
}
return cacheCurrentValue;
}
/**
* 字符串最大值
*
* @param length
* @return
*/
private int getMaxValueString(int length) {
String value = "";
for (int i = 0; i < length; i++) {
value = "9" + value;
}
return Integer.parseInt(value);
}
}
最后
以上就是迷人柚子为你收集整理的基于redis的规则流水号生成(三)的全部内容,希望文章能够帮你解决基于redis的规则流水号生成(三)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复