项目为单机项目 接口中是有重复验证的
- 当前功能的重复验证是在功能开头进行的
 - 验证重复后,做了一大堆的其他工作在进行保存的
 
问题叙述
甲方在视图展示页面发现了不该有的重复。 我方检查接口和测试功能,并未复现问题。
问题产生原因
测试发现无问题之后,删除重复数据,不久后甲方有反应相同的问题。 这次阴差阳错的由于远程处理卡顿问题,连续点击测试N次功能点,错误数据由此产生
问题排除
已经确认为并发问题,但是具体造成原因还不明朗 最后通过反复测试,最终发现两个可疑点
- 功能中验证重复是在一开始进行的,途中做了很多很多无关的工作后,才进行了最终的save,可能期间过程有些延迟导致的
 
- 其中使用到了图片上传,但是不是前端调用的文件服务而是选择让后端处理
 - 其中使用到了查询第三方远程库,且数据量优点大,同时
 sql已经无法在进行优化了
- 由于各种原因,对各系统的数据库为同一个且为单机数据库,可能存储的延迟造成的。
 最后通过模拟线上数据库的环境进行测试,发现疑点二可以消除。
疑点一的环境比较复杂只能通过 睡眠等待 来模拟环境
问题确认跟处理办法
- 通过 睡眠等待 来模拟 
数据重复验证的系类操作,发现就是这里出的问题 - 最终处理
- 用最终的存储数据库直接兜底推荐,在不可重复字段上加入了唯一索引
 
 - 其他处理方式参考
 
- 对于一些数据的强验证,一定注意数据录入和数据验证之间的延迟。
 - 对唯一行的数据一定要进行数据库兜底
 - 对增改的操作,中需要大量验证的时候,考虑下锁
synchronized - 分布式下问题参考( 极客时间~每日一课
 
@RestController
@RequestMapping(“/front”)
public class BookController {
    @Autowired
    private BookService bookService;
    @RequestMapping(value=”/formBuySubmit”,method=RequestMethod.POST)
    public ResultUtil
public interface BookService {
    ResultUtil
@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao;
/*** 初步怀疑是判断重复之后接下来的其他操作导致了延迟的增加* @param bean* @return*/@SuppressWarnings("unlikely-arg-type")@Overridepublic synchronized ResultUtil<BookBean> formBuy(BookBean bean) {ResultUtil<BookBean> res = new ResultUtil<BookBean>();
// BookDao.sleep(1000); if(BookDao.verify(bean.getBookName())){ res.setResult(false); res.setResultInfo(“重复了”); // throw new RuntimeException(“存在重复”); }else { // 进行延时 - 模拟期间的 上传文件,查询第三方库等操作 BookDao.sleep(5000); // BookDao.sleep(1000); BookBean book = bookDao.save(bean); res.setResult(true); res.setResultInfo(“荐购成功,请等待管理员审核”); res.setResponseData(book); } return res; } }
@Component
@Slf4j
public class BookDao {
    static List
public static Boolean verify(String name){if(names.contains(name)){log.info("name:"+name);log.info("nameList"+ StringUtils.join(names.toArray(),","));return true;}return false;}public static void sleep(long millis ){try {Thread.sleep(millis);} catch (InterruptedException e) {e.printStackTrace();}}
}
public class ResultUtil
private boolean result; //正确与否private String resultInfo;//回调信息private T responseData; //回调数据public boolean isResult() {return result;}public void setResult(boolean result) {this.result = result;}public T getResponseData() {return responseData;}public void setResponseData(T responseData) {this.responseData = responseData;}public String getResultInfo() {return resultInfo;}public void setResultInfo(String resultInfo) {this.resultInfo = resultInfo;}
}
public class TimeFormat { /**
* yyyy-MM-dd HH:mm:ss*/public static final SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static String currentTime(){String str="";Date date = new Date();try {str = format1.format(date);} catch (Exception e) {System.out.println("转换时间格式异常(yyyy-MM-dd):"+str);str="";e.printStackTrace();}return str;}
}
@Data public class BookBean implements Serializable{ private static final long serialVersionUID = 1L; private String bookName; //名称 private String createTime; //创建时间
}
- 模拟测试并发```http###POST http://localhost:8080/front/formBuySubmitContent-Type: application/x-www-form-urlencodedbookName=1231###POST http://localhost:8080/front/formBuySubmitContent-Type: application/x-www-form-urlencodedbookName=1231
小知识
- 测试并发的过程中发现
postMan的压测不是并发执行,他是串行的气死个人 
