业务需求:今天开发人员向我反馈,说之前的旧系统存在,在高并发的情况下,会产生流水发号重新的现象。
说是迟,写是快。快速写完高并发情况下的流水号生成服务。核心代码如下:
@Controller
@RequestMapping("/api/sys/pip/record")
@Api(value = "流水号记录Controlle", tags = "流水号记录操作服务")
public class SysPipNumberRecordController extends AbstractController {
@Autowired
private SysPipNumberRecordService service;
@ApiOperation(httpMethod = "POST", value = "流水号记录信息保存")
@RequestMapping(value = "/insert", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8")
@ResponseBody
public Result insert(
@RequestBody @ApiParam(name = "流水号规则对象", value = "json格式对象", required = true) SysPipNumberRecord entity) {
// 判断流水规则是否为空
if(!StringUtils.isEmpty(entity.getRuleSid())){
service.insertSelective(entity);
return Result.ok();
} else {
return Result.error("流水规则不存在");
}
}
@Override
public int insertSelective(SysPipNumberRecord record) {
// TODO Auto-generated method stub
// 解决并发请求,发号重复问题:锁定当前代码块
synchronized(this){
// 分布式sid 生成
SnowflakeIdGenerator snowflake = new SnowflakeIdGenerator(0,2);
record.setSid(Long.toBinaryString(snowflake.nextId()));
// 判断流水规则 是否在流水记录中生成
Map<String, Object> parame = new HashMap<String, Object>();
parame.put("ruleSid", record.getRuleSid());
// 公共属性
SysPipNumberRule rule = ruleMapper.selectByPrimaryKey(record.getRuleSid());
record.setCategory(rule.getCategory());
record.setZoneOrgCode(rule.getZoneOrgCode());
List<SysPipNumberRecord> list = mapper.selectAll(parame);
if (list != null && list.size() > 0) {
// 获取流水记录最大值
List<Integer> num = list.stream().map(SysPipNumberRecord::getInitValue).collect(Collectors.toList());
Integer number = Collections.max(num) + 1;
record.setInitValue(number);
// 序列号
String serialNumber = StringFormatUtil.addZeroForNum(String.valueOf(number),
rule.getLength());
StringBuilder builder = new StringBuilder();
// 判断是否启动分类
if(rule.getIsCategory().equalsIgnoreCase("1")){
// 判断是否截取分类
if(rule.getCutLength() != null && rule.getCutLength() > 0){
// 判断截取长度与分类key 长度问题
if(rule.getCategory().length() >= rule.getCutLength()){
builder.append(rule.getCategory().substring(0, rule.getCutLength()));
} else {
builder.append(rule.getCategory());
}
} else {
builder.append(rule.getCategory());
}
// 判断连接符是否配置
if(!StringUtils.isEmpty(rule.getConnector())){
builder.append(rule.getConnector());
}
}
// 判断是否启动日期
if (rule.getIsDate().equalsIgnoreCase("1")) {
SimpleDateFormat format = new SimpleDateFormat(rule.getDateFormat());
builder.append(format.format(new Date()));
if(!StringUtils.isEmpty(rule.getConnector())){
builder.append(rule.getConnector());
}
}
// 添加流水号
builder.append(serialNumber);
record.setPipNumberValue(builder.toString());
} else {
record.setInitValue(rule.getStartValue());
// 序列号
String serialNumber = StringFormatUtil.addZeroForNum(String.valueOf(rule.getStartValue()),
rule.getLength());
StringBuilder builder = new StringBuilder();
// 判断是否启动分类
if(rule.getIsCategory().equalsIgnoreCase("1")){
// 判读是否截取分类
if(rule.getCutLength() != null && rule.getCutLength() > 0){
// 判断截取长度与分类key 长度问题
if(rule.getCategory().length() >= rule.getCutLength()){
builder.append(rule.getCategory().substring(0, rule.getCutLength()));
} else {
builder.append(rule.getCategory());
}
} else {
builder.append(rule.getCategory());
}
if(!StringUtils.isEmpty(rule.getConnector())){
builder.append(rule.getConnector());
}
}
// 判断是否启动日期
if (rule.getIsDate().equalsIgnoreCase("1")) {
SimpleDateFormat format = new SimpleDateFormat(rule.getDateFormat());
builder.append(format.format(new Date()));
if(!StringUtils.isEmpty(rule.getConnector())){
builder.append(rule.getConnector());
}
}
// 添加流水号
builder.append(serialNumber);
record.setPipNumberValue(builder.toString());
}
// 数据入库
return mapper.insertSelective(record);
}
}
核心代码已经写完,如何验证我写的代码,让我很纠结,测试人员提示我,可以使用ab 测试工具 模拟高并发的情况,但是我从我们公司的前端学到一手,通过HTML5 新特性fetch 函数模拟高并发.
前端js 代码如下:
var sends = (num) => {
for(var i =0;i<num;i++) {
fetch("http://localhost:9090/api/sys/pip/record/insert", {"credentials":"include","headers":{"accept":"application/json;charset=UTF-8","accept-language":"zh,en;q=0.9,zh-CN;q=0.8","content-type":"application/json"},"referrer":"http://localhost:9090/swagger-ui.html","referrerPolicy":"no-referrer-when-downgrade","body":"{\"ruleSid\":\"100011000100111000100000010101101000100001000000000000001010\"}","method":"POST","mode":"cors"})
}
}
通过模拟2次,每次都是100 个请求,查看数据库结果,没有重复。