最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java并發(fā)請求下數(shù)據(jù)插入重復(fù)問題的解決方法

 更新時(shí)間:2021年11月12日 12:38:32   作者:曉彬喲  
現(xiàn)在遇到一個(gè)項(xiàng)目,移動(dòng)設(shè)備存儲(chǔ)數(shù)據(jù),然后一起上傳,那就出現(xiàn)了許多重復(fù)數(shù)據(jù),這篇文章主要給大家介紹了關(guān)于java并發(fā)請求下數(shù)據(jù)插入重復(fù)問題的解決方法,需要的朋友可以參考下

前言

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

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

解決方案說明:

系統(tǒng)架構(gòu)用的Spring boot,定義一個(gè)Filter過濾器對請求進(jìn)行過濾,然后對請求報(bào)文生成摘要信息并設(shè)置Redis分布式鎖。通過摘要和鎖判斷是否為同一請求。

分布式鎖工具類

public class ContextLJ {
	
	private static final Integer JD = 0;
	
	  /**
	   * 上鎖 使用redis 為分布式項(xiàng)目 加鎖
	   * @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;
	    }
	 }
	 
	  /**
	   * 鎖驗(yàn)證
	   * @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中的請求內(nèi)容 拼裝成 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);
	}
	
}

在過濾器實(shí)現(xiàn)請求攔截

/**
 * 過濾頻繁請求
 */
@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); // 查詢數(shù)據(jù)字典或者文件,直接放行
			return;
		}
		String sign = "sign_" + ContextLJ.getSign(request); // 生成摘要
	    String tiD = RandomUtils.randomCode(3) + "_" + Thread.currentThread().getId(); // 當(dāng)前線程的身份
	    try { 
	    	if (!ContextLJ.lock(sign, tiD)) {
	    		Map<String,String> map = ContextLJ.getRequstMap((HttpServletRequest)request);
	    		log.warn("放棄相同并發(fā)請求【" + sign+ "】【" + tiD+"】"+JSON.toJSONString(map));
	    		frequentlyError(myResp);
	    		return;
	    	}
	    	if (!ContextLJ.checklock(sign, tiD)) {
	    		Map<String,String> map = ContextLJ.getRequstMap((HttpServletRequest)request);
		    	  log.warn("加鎖驗(yàn)證失敗 【" + sign+ "】【" + tiD+"】"+JSON.toJSONString(map));
		    	  frequentlyError(myResp);
		    	  return;
	    	}
	    	chain.doFilter(request, myResp); // 放行
	    } catch (Exception e) { // 捕獲到異常 進(jìn)行異常過濾
		      log.error("", e);
		      myResp.getWriter().write(JSON.toJSONString(ApiRs.asError("服務(wù)器繁忙,請重試")));
	    } 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("稍安勿躁,不要頻繁請求")));
	}

}

總結(jié)

到此這篇關(guān)于java并發(fā)請求下數(shù)據(jù)插入重復(fù)問題的解決方法的文章就介紹到這了,更多相關(guān)java并發(fā)請求數(shù)據(jù)插入重復(fù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于jd-gui啟動(dòng)報(bào)This?program?requires?Java?1.8+的錯(cuò)誤問題及解決方法

    關(guān)于jd-gui啟動(dòng)報(bào)This?program?requires?Java?1.8+的錯(cuò)誤問題及解決方法

    最近,在Mac使用上JD-GUI啟動(dòng)時(shí)總是報(bào)錯(cuò),接下來通過本文給大家介紹關(guān)于jd-gui啟動(dòng)報(bào)this?program?requires?Java?1.8+的錯(cuò)誤問題及解決方法,需要的朋友可以參考下
    2022-05-05
  • Java Big Number操作BigInteger及BigDecimal類詳解

    Java Big Number操作BigInteger及BigDecimal類詳解

    這篇文章主要為大家介紹了Java Big Number操作BigInteger及BigDecimal類詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • java實(shí)現(xiàn)一個(gè)桌球小游戲

    java實(shí)現(xiàn)一個(gè)桌球小游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)一個(gè)桌球小游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • 新建一個(gè)springboot單體項(xiàng)目的教程

    新建一個(gè)springboot單體項(xiàng)目的教程

    這篇文章主要介紹了新建一個(gè)springboot單體項(xiàng)目的教程,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 關(guān)于Java中增強(qiáng)for循環(huán)使用的注意事項(xiàng)

    關(guān)于Java中增強(qiáng)for循環(huán)使用的注意事項(xiàng)

    for循環(huán)語句是java循環(huán)語句中最常用的循環(huán)語句,一般用在循環(huán)次數(shù)已知的情況下使用,這篇文章主要給大家介紹了關(guān)于Java中增強(qiáng)for循環(huán)使用的注意事項(xiàng),需要的朋友可以參考下
    2021-06-06
  • Java使用異或運(yùn)算實(shí)現(xiàn)簡單的加密解密算法實(shí)例代碼

    Java使用異或運(yùn)算實(shí)現(xiàn)簡單的加密解密算法實(shí)例代碼

    這篇文章主要介紹了Java使用異或運(yùn)算實(shí)現(xiàn)簡單的加密解密算法實(shí)例代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • 關(guān)于JDK8中的字符串拼接示例詳解

    關(guān)于JDK8中的字符串拼接示例詳解

    字符串拼接問題應(yīng)該是每個(gè)Java程序員都熟知的事情了,幾乎每個(gè)Java程序員都讀過關(guān)于StringBuffer/StringBuilder來拼接字符串。下面這篇文章主要給大家介紹了關(guān)于JDK8中的字符串拼接的相關(guān)資料,需要的朋友可以參考下。
    2018-04-04
  • Java數(shù)組初始化的五種方式

    Java數(shù)組初始化的五種方式

    數(shù)組是Java中最基礎(chǔ)且常用的數(shù)據(jù)結(jié)構(gòu)之一,其初始化方式多樣且各具特點(diǎn),本文詳細(xì)講解Java數(shù)組初始化的五種方式,分析其適用場景、優(yōu)劣勢對比及注意事項(xiàng),幫助避免常見陷阱并提升代碼質(zhì)量,需要的朋友可以參考下
    2025-04-04
  • JAVA DOM解析XML文件過程詳解

    JAVA DOM解析XML文件過程詳解

    這篇文章主要介紹了JAVA DOM解析XML文件過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Spring Cloud Gateway自定義異常處理Exception Handler的方法小結(jié)

    Spring Cloud Gateway自定義異常處理Exception Handler的方法小結(jié)

    這篇文章主要介紹了Spring Cloud Gateway自定義異常處理Exception Handler的方法,本文通過兩種方法結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08

最新評論

汕尾市| 襄汾县| 五河县| 神木县| 岳普湖县| 石河子市| 泰和县| 平潭县| 芒康县| 冕宁县| 基隆市| 盐亭县| 石阡县| 呈贡县| 北川| 武城县| 江门市| 永丰县| 康马县| 息烽县| 开远市| 芜湖市| 恩施市| 新巴尔虎右旗| 安达市| 都江堰市| 平湖市| 襄垣县| 康马县| 东台市| 北川| 柘荣县| 鄂尔多斯市| 南靖县| 若羌县| 团风县| 永仁县| 新巴尔虎左旗| 翁源县| 沁阳市| 会泽县|