针对上次的流水号生成有几处细节优化的地方
1、高并发的时候,在哪个位置加锁?
2、加锁的重试机制如何设置?
3、加锁的重试次数与间隔时间范围应该如何?
4、入队列的优化
1、首先获取一次缓存中的序列号,不要直接加分布式锁,毕竟遇到查不到的情况约1000分之一
2、流水号大约1秒多生成,建议超时时长设置为2-3秒,重试次数可以多次,每次间隔500ms即可。间隔过大,会导致撞锁。间隔太小会导致无用的重试次数太多。
3、一次操作时间《加锁的重试次数*间隔时间<两次操作时间。理由是:若重试的次数*间隔时间比一次操作时间短,则会导致加锁失败。若大于两次操作时间,则造成浪费。
4、入队的方式,除了执行一个个命令外,rpush是支持把数组一次性存入缓存的。
附上代码(敏感引入已去除):
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212import 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内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复