The Solution to the Problem of Data Insertion Repeated in java Concurrent Request

  • 2021-12-12 04:13:09
  • OfStack

Directory preface distributed lock tool class implements request interception summary in filter

Preface

Some time ago, it was found that there are often two identical user data in the database, which leads to abnormal data query. After checking the reasons, it is found that when the front-end WeChat applet authorizes login, it sometimes sends two requests with one model and one sample at the same time (which is often called concurrency). Although the back-end code has made anti-repetitive judgments, it cannot avoid repetitive operations when concurrent. So we started thinking about concurrent solutions. There are many solutions, from intercepting requests to the database level.

We adopt the scheme of generating digest information + Redis distributed lock for request message. After running for 1 period of time, the function is very reliable and the code is very concise. So I came up and made a record for subsequent reference.

Solution Description:

Spring boot is used in the system architecture. One Filter filter is defined to filter the request, and then the summary information is generated for the request message and Redis distributed lock is set. Determine whether it is the same 1 request by digest and lock.

Distributed lock tool class


public class ContextLJ {
	
	private static final Integer JD = 0;
	
	  /**
	   *  Lock   Use redis  For distributed projects   Lock 
	   * @param sign
	   * @param tiD
	   * @return
	   * @throws Exception
	   */
	  public static boolean lock(String sign, String tiD) {
	    synchronized (JD) { //  Lock 
	    	Cache<String> cache = CacheManager.getCommonCache(sign);
	    	if(cache == null || StringUtils.isBlank(cache.getValue())) {
	    		CacheManager.putCommonCacheInfo(sign, tiD, 10000);
	    		return true;
			}
	    	return false;
	    }
	 }
	 
	  /**
	   *  Lock verification 
	   * @param sign
	   * @param tiD
	   * @return
	   */
	  public static boolean checklock(String sign, String tiD){
		  Cache<String> cache = CacheManager.getCommonCache(sign);
		  String uTid = StringUtils.replace(cache.getValue(), "\"", "");
		  return tiD.equals(uTid);
	  }
	 
	  /**
	   *  Remove the lock 
	   * @param sign
	   * @param tiD
	   */
	  public static void clent (String sign, String tiD){
		    if (checklock(sign, tiD)) {
		    	CacheManager.clearOnly(sign);
		    }
	  }
	 
	  /**
	   *  Get a summary 
	   * @param request
	   */
	  public static String getSign(ServletRequest request){
	    //  This tool is the  request Content requested in   Assemble into  key=value&key=value2  Form of   Source code on-line surface 
	    String sign = null;
	    try {
	    	Map<String, String> map =  getRequstMap((HttpServletRequest) request);
	    	//  Generate summary 
	    	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);
	}
	
}

Implement request interception in filters


/**
 *  Filter frequent requests 
 */
@Slf4j
@Component
public class MyFilter implements Filter{
	
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		
	}

	@Override
	public 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); //  Query the data dictionary or file and release it directly 
			return;
		}
		String sign = "sign_" + ContextLJ.getSign(request); //  Generate summary 
	    String tiD = RandomUtils.randomCode(3) + "_" + Thread.currentThread().getId(); //  The identity of the current thread 
	    try { 
	    	if (!ContextLJ.lock(sign, tiD)) {
	    		Map<String,String> map = ContextLJ.getRequstMap((HttpServletRequest)request);
	    		log.warn(" Discard the same concurrent request " " + sign+ " " " + tiD+" " "+JSON.toJSONString(map));
	    		frequentlyError(myResp);
	    		return;
	    	}
	    	if (!ContextLJ.checklock(sign, tiD)) {
	    		Map<String,String> map = ContextLJ.getRequstMap((HttpServletRequest)request);
		    	  log.warn(" Lock verification failed   " " + sign+ " " " + tiD+" " "+JSON.toJSONString(map));
		    	  frequentlyError(myResp);
		    	  return;
	    	}
	    	chain.doFilter(request, myResp); //  Release 
	    } catch (Exception e) { //  Exception caught   Perform anomaly filtering 
		      log.error("", e);
		      myResp.getWriter().write(JSON.toJSONString(ApiRs.asError(" Server is busy, please try again ")));
	    } finally {
	    	ContextLJ.clent(sign, tiD);
	    }
	}

	@Override
	public void destroy() {
		
	}
	
	/**
	 *  Frequent requests 
	 */
	private void frequentlyError(ServletResponse myResp) throws IOException {
	  ((HttpServletResponse) myResp).setHeader("Content-type", "text/html;charset=UTF-8");
	  myResp.getWriter().write(JSON.toJSONString(ApiRs.asError(" Be calm and don't ask frequently ")));
	}

}

Summarize


Related articles: