文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么解决java并发请求下数据插入重复问题

2023-06-25 14:56

关注

本篇内容介绍了“怎么解决java并发请求下数据插入重复问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

前言

前段时间发现数据库里经常会存在两条相同的用户数据,导致数据查询异常。查了原因,发现前端微信小程序在授权登录时,有时会出现同时发送了两条一模一样的请求(也就是常说的并发)。虽然后端代码有做防重复的判断,但是避免不了并发时候的重复性操作。于是就开始考虑并发的解决方案,解决方案有很多,从拦截请求到数据库层面都可以入手。

我们采用了对请求报文生成摘要信息+Redis分布式锁的方案。运行了一段时间,功能很可靠,代码也很简洁。于是上来做下记录以便后续参考。

解决方案说明:

系统架构用的Spring boot,定义一个Filter过滤器对请求进行过滤,然后对请求报文生成摘要信息并设置Redis分布式锁。通过摘要和锁判断是否为同一请求。

分布式锁工具类

public class ContextLJ {private static final Integer JD = 0;    public static boolean lock(String sign, String tiD) {    synchronized (JD) { // 加锁    Cache<String> cache = CacheManager.getCommonCache(sign);    if(cache == null || StringUtils.isBlank(cache.getValue())) {    CacheManager.putCommonCacheInfo(sign, tiD, 10000);    return true;}    return false;    } }     public static boolean checklock(String sign, String tiD){  Cache<String> cache = CacheManager.getCommonCache(sign);  String uTid = StringUtils.replace(cache.getValue(), "\"", "");  return tiD.equals(uTid);  }     public static void clent (String sign, String tiD){    if (checklock(sign, tiD)) {    CacheManager.clearOnly(sign);    }  }     public static String getSign(ServletRequest request){    // 此工具是将 request中的请求内容 拼装成 key=value&key=value2 的形式 源码在线面    String sign = null;    try {    Map<String, String> map =  getRequstMap((HttpServletRequest) request);    // 生成摘要    sign = buildRequest(map);    } catch (Exception e) {    e.printStackTrace();    }    return sign;  }    public static Map<String, String> getRequstMap(HttpServletRequest req) throws Exception{     Map<String,String> params = new HashMap<String,String>();     params.put("uri", req.getRequestURI());    Map<String, String[]> requestParams = req.getParameterMap();    for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {      String name = (String) iter.next();      String[] values = (String[]) requestParams.get(name);      String valueStr = "";      for (int i = 0; i < values.length; i++) {        valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";      }      params.put(name, valueStr);    }    return params;}   private static String buildRequest(Map<String, String> map) { List<String> signList = new ArrayList<>(); for(Entry<String, String> entry : map.entrySet()) { signList.add(entry.getKey() + "=" + entry.getValue()); } String sign = StringUtils.join(signList, "&"); return DigestUtils.md5Hex(sign);}}

在过滤器实现请求拦截

@Slf4j@Componentpublic class MyFilter implements Filter{@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse myResp, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;Boolean isDict = StringUtils.contains(req.getRequestURI(), "/dict/getDatas");Boolean isFile = StringUtils.contains(req.getRequestURI(), "/files/file");if(isDict || isFile) {chain.doFilter(request, myResp); // 查询数据字典或者文件,直接放行return;}String sign = "sign_" + ContextLJ.getSign(request); // 生成摘要    String tiD = RandomUtils.randomCode(3) + "_" + Thread.currentThread().getId(); // 当前线程的身份    try {     if (!ContextLJ.lock(sign, tiD)) {    Map<String,String> map = ContextLJ.getRequstMap((HttpServletRequest)request);    log.warn("放弃相同并发请求【" + sign+ "】【" + tiD+"】"+JSON.toJSONString(map));    frequentlyError(myResp);    return;    }    if (!ContextLJ.checklock(sign, tiD)) {    Map<String,String> map = ContextLJ.getRequstMap((HttpServletRequest)request);      log.warn("加锁验证失败 【" + sign+ "】【" + tiD+"】"+JSON.toJSONString(map));      frequentlyError(myResp);      return;    }    chain.doFilter(request, myResp); // 放行    } catch (Exception e) { // 捕获到异常 进行异常过滤      log.error("", e);      myResp.getWriter().write(JSON.toJSONString(ApiRs.asError("服务器繁忙,请重试")));    } finally {    ContextLJ.clent(sign, tiD);    }}@Overridepublic void destroy() {}private void frequentlyError(ServletResponse myResp) throws IOException {  ((HttpServletResponse) myResp).setHeader("Content-type", "text/html;charset=UTF-8");  myResp.getWriter().write(JSON.toJSONString(ApiRs.asError("稍安勿躁,不要频繁请求")));}}

“怎么解决java并发请求下数据插入重复问题”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯