91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么解決java并發請求下數據插入重復問題

發布時間:2021-11-12 15:22:27 來源:億速云 閱讀:151 作者:iii 欄目:開發技術

本篇內容介紹了“怎么解決java并發請求下數據插入重復問題”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

前言

前段時間發現數據庫里經常會存在兩條相同的用戶數據,導致數據查詢異常。查了原因,發現前端微信小程序在授權登錄時,有時會出現同時發送了兩條一模一樣的請求(也就是常說的并發)。雖然后端代碼有做防重復的判斷,但是避免不了并發時候的重復性操作。于是就開始考慮并發的解決方案,解決方案有很多,從攔截請求到數據庫層面都可以入手。

我們采用了對請求報文生成摘要信息+Redis分布式鎖的方案。運行了一段時間,功能很可靠,代碼也很簡潔。于是上來做下記錄以便后續參考。

解決方案說明:

系統架構用的Spring boot,定義一個Filter過濾器對請求進行過濾,然后對請求報文生成摘要信息并設置Redis分布式鎖。通過摘要和鎖判斷是否為同一請求。

分布式鎖工具類

public class ContextLJ {
	
	private static final Integer JD = 0;
	
	  /**
	   * 上鎖 使用redis 為分布式項目 加鎖
	   * @param sign
	   * @param tiD
	   * @return
	   * @throws Exception
	   */
	  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;
	    }
	 }
	 
	  /**
	   * 鎖驗證
	   * @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);
	  }
	 
	  /**
	   * 去掉鎖
	   * @param sign
	   * @param tiD
	   */
	  public static void clent (String sign, String tiD){
		    if (checklock(sign, tiD)) {
		    	CacheManager.clearOnly(sign);
		    }
	  }
	 
	  /**
	   * 獲取摘要
	   * @param request
	   */
	  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
@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); // 查詢數據字典或者文件,直接放行
			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);
	    }
	}

	@Override
	public 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并發請求下數據插入重復問題”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宕昌县| 周口市| 沾益县| 罗源县| 确山县| 福清市| 宝清县| 余姚市| 搜索| 突泉县| 天峻县| 福泉市| 大冶市| 友谊县| 永寿县| 遂平县| 榆林市| 浮山县| 宁阳县| 元谋县| 潜江市| 家居| 邓州市| 姚安县| 屏东市| 宣恩县| 从化市| 澄江县| 民勤县| 清水县| 白河县| 色达县| 两当县| 遵义市| 宜都市| 颍上县| 浙江省| 延吉市| 保康县| 新绛县| 皮山县|