欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Boot實現(xiàn)數(shù)據(jù)訪問計數(shù)器方案詳解

 更新時間:2021年08月09日 08:38:18   作者:阿拉伯1999  
在Spring Boot項目中,有時需要數(shù)據(jù)訪問計數(shù)器,怎么實現(xiàn)數(shù)據(jù)訪問計數(shù)器呢?下面小編給大家?guī)砹薙pring Boot數(shù)據(jù)訪問計數(shù)器的實現(xiàn)方案,需要的朋友參考下吧

1、數(shù)據(jù)訪問計數(shù)器

  在Spring Boot項目中,有時需要數(shù)據(jù)訪問計數(shù)器。大致有下列三種情形:

1)純計數(shù):如登錄的密碼錯誤計數(shù),超過門限N次,則表示計數(shù)器滿,此時可進行下一步處理,如鎖定該賬戶。

2)時間滑動窗口:設窗口寬度為T,如果窗口中尾幀時間與首幀時間差大于T,則表示計數(shù)器滿。

  例如使用redis緩存時,使用key查詢redis中數(shù)據(jù),如果有此key數(shù)據(jù),則返回對象數(shù)據(jù);如無此key數(shù)據(jù),則查詢數(shù)據(jù)庫,但如果一直都無此key數(shù)據(jù),從而反復查詢數(shù)據(jù)庫,顯然有問題。此時,可使用時間滑動窗口,對于查詢的失敗的key,距離首幀T時間(如1分鐘)內(nèi),不再查詢數(shù)據(jù)庫,而是直接返回無此數(shù)據(jù),直到新查詢的時間超過T,更新滑窗首幀為新時間,并執(zhí)行一次查詢數(shù)據(jù)庫操作。

3)時間滑動窗口+計數(shù):這往往在需要進行限流處理的場景使用。如T時間(如1分鐘)內(nèi),相同key的訪問次數(shù)超過超過門限N,則表示計數(shù)器滿,此時進行限流處理。

2、代碼實現(xiàn)

2.1、方案說明

1)使用字典來管理不同的key,因為不同的key需要單獨計數(shù)。

2)上述三種情況,使用類型屬性區(qū)分,并在構(gòu)造函數(shù)中進行設置。

3)滑動窗口使用雙向隊列Deque來實現(xiàn)。

4)考慮到訪問并發(fā)性,讀取或更新時,加鎖保護。

2.2、代碼

package com.abc.example.service;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;


/**
 * @className	: DacService
 * @description	: 數(shù)據(jù)訪問計數(shù)服務類
 * @summary		:
 * @history		:
 * ------------------------------------------------------------------------------
 * date			version		modifier		remarks                   
 * ------------------------------------------------------------------------------
 * 2021/08/03	1.0.0		sheng.zheng		初版
 *
 */
public class DacService {
	
	// 計數(shù)器類型:1-數(shù)量;2-時間窗口;3-時間窗口+數(shù)量
	private int counterType; 
	
	// 計數(shù)器數(shù)量門限
	private int counterThreshold = 5;
	
	// 時間窗口長度,單位毫秒
	private int windowSize = 60000;
	
	// 對象key的訪問計數(shù)器
	private Map<String,Integer> itemMap;

	// 對象key的訪問滑動窗口
	private Map<String,Deque<Long>> itemSlideWindowMap;
	
	/**
	 * 構(gòu)造函數(shù)
	 * @param counterType		: 計數(shù)器類型,值為1,2,3之一
	 * @param counterThreshold	: 計數(shù)器數(shù)量門限,如果類型為1或3,需要此值
	 * @param windowSize		: 窗口時間長度,如果為類型為2,3,需要此值
	 */
	public DacService(int counterType, int counterThreshold, int windowSize) {
		this.counterType = counterType;
		this.counterThreshold = counterThreshold;
		this.windowSize = windowSize;
		
		if (counterType == 1) {
		    // 如果與計數(shù)器有關(guān)
		    itemMap = new HashMap<String,Integer>();
		}else if (counterType == 2 || counterType == 3) {
		    // 如果與滑動窗口有關(guān)
		    itemSlideWindowMap = new HashMap<String,Deque<Long>>();
		}
	}		
		
	/**
	 * 
	 * @methodName		: isItemKeyFull
	 * @description		: 對象key的計數(shù)是否將滿
	 * @param itemKey	: 對象key
	 * @param timeMillis    : 時間戳,毫秒數(shù),如為滑窗類計數(shù)器,使用此參數(shù)值
	 * @return		: 滿返回true,否則返回false
	 * @history		:
	 * ------------------------------------------------------------------------------
	 * date			version		modifier		remarks                   
	 * ------------------------------------------------------------------------------
	 * 2021/08/03	1.0.0		sheng.zheng		初版
	 * 2021/08/08	1.0.1		sheng.zheng		支持多種類型計數(shù)器
	 *
	 */
	public boolean isItemKeyFull(String itemKey,Long timeMillis) {
		boolean bRet = false;
		
		if (this.counterType == 1) {
		    // 如果為計數(shù)器類型			
		    if (itemMap.containsKey(itemKey)) {
			synchronized(itemMap) {
		  	    Integer value = itemMap.get(itemKey);
			    // 如果計數(shù)器將超越門限
			    if (value >= this.counterThreshold - 1) {
			        bRet = true;
			    }					
			}
		    }else {
		        // 新的對象key,視業(yè)務需要,取值true或false
			bRet = true;
		    }
		}else if(this.counterType == 2){
		    // 如果為滑窗類型			
		    if (itemSlideWindowMap.containsKey(itemKey)) {
			  Deque<Long> itemQueue = itemSlideWindowMap.get(itemKey);
			  synchronized(itemQueue) {
			      if (itemQueue.size() > 0) {
				  Long head = itemQueue.getFirst();
				  if (timeMillis - head >= this.windowSize) {
				      // 如果窗口將滿
				      bRet = true;
				  }
			      }									
			  }
		    }else {
		        // 新的對象key,視業(yè)務需要,取值true或false
			bRet = true;				
		    }			
		}else if(this.counterType == 3){
		    // 如果為滑窗+數(shù)量類型
		    if (itemSlideWindowMap.containsKey(itemKey)) {
		        Deque<Long> itemQueue = itemSlideWindowMap.get(itemKey);
			synchronized(itemQueue) {
			    Long head = 0L;
			    // 循環(huán)處理頭部數(shù)據(jù),確保新數(shù)據(jù)幀加入后,維持窗口寬度
			    while(true) {
			    	// 取得頭部數(shù)據(jù)
			    	head = itemQueue.peekFirst();
			    	if (head == null || timeMillis - head <= this.windowSize) {
			            break;
				}
				// 移除頭部
				itemQueue.remove();
			    }	
			    if (itemQueue.size() >= this.counterThreshold -1) {
			        // 如果窗口數(shù)量將滿
				bRet = true;
			    }											
			}
		    }else {
			// 新的對象key,視業(yè)務需要,取值true或false
			bRet = true;				
		    }			
		}
		
		return bRet;		
	}
		
	/**
	 * 
	 * @methodName		: resetItemKey
	 * @description		: 復位對象key的計數(shù) 
	 * @param itemKey	: 對象key
	 * @history		:
	 * ------------------------------------------------------------------------------
	 * date			version		modifier		remarks                   
	 * ------------------------------------------------------------------------------
	 * 2021/08/03	1.0.0		sheng.zheng		初版
	 * 2021/08/08	1.0.1		sheng.zheng		支持多種類型計數(shù)器
	 *
	 */
	public void resetItemKey(String itemKey) {
		if (this.counterType == 1) {
		    // 如果為計數(shù)器類型
		    if (itemMap.containsKey(itemKey)) {
		        // 更新值,加鎖保護
			synchronized(itemMap) {
			    itemMap.put(itemKey, 0);
			}			
		    }		
		}else if(this.counterType == 2){
		    // 如果為滑窗類型
		    // 清空
		    if (itemSlideWindowMap.containsKey(itemKey)) {
		        Deque<Long> itemQueue = itemSlideWindowMap.get(itemKey);
			if (itemQueue.size() > 0) {
			    // 加鎖保護
			    synchronized(itemQueue) {
			      // 清空
			      itemQueue.clear();
			    }								
			}
		    }						
		}else if(this.counterType == 3){
		    // 如果為滑窗+數(shù)量類型
		    if (itemSlideWindowMap.containsKey(itemKey)) {
		        Deque<Long> itemQueue = itemSlideWindowMap.get(itemKey);
			synchronized(itemQueue) {
			    // 清空
			    itemQueue.clear();
			}
		    }
		}
	}
	
	/**
	 * 
	 * @methodName		: putItemkey
	 * @description		: 更新對象key的計數(shù)
	 * @param itemKey	: 對象key
	 * @param timeMillis    : 時間戳,毫秒數(shù),如為滑窗類計數(shù)器,使用此參數(shù)值
	 * @history		:
	 * ------------------------------------------------------------------------------
	 * date			version		modifier		remarks                   
	 * ------------------------------------------------------------------------------
	 * 2021/08/03	1.0.0		sheng.zheng		初版
	 * 2021/08/08	1.0.1		sheng.zheng		支持多種類型計數(shù)器
	 *
	 */
	public void putItemkey(String itemKey,Long timeMillis) {
		if (this.counterType == 1) {
		    // 如果為計數(shù)器類型
		    if (itemMap.containsKey(itemKey)) {
		        // 更新值,加鎖保護
			synchronized(itemMap) {
			    Integer value = itemMap.get(itemKey);
			    // 計數(shù)器+1
			    value ++;
			    itemMap.put(itemKey, value);
			}
		    }else {
		        // 新key值,加鎖保護
			synchronized(itemMap) {
			    itemMap.put(itemKey, 1);
			}			
		    }
		}else if(this.counterType == 2){
		    // 如果為滑窗類型	
		    if (itemSlideWindowMap.containsKey(itemKey)) {
		        Deque<Long> itemQueue = itemSlideWindowMap.get(itemKey);				
			// 加鎖保護
			synchronized(itemQueue) {
			    // 加入
			    itemQueue.add(timeMillis);
			}								
		    }else {
			// 新key值,加鎖保護
			Deque<Long> itemQueue = new ArrayDeque<Long>();
			synchronized(itemSlideWindowMap) {
			    // 加入映射表
			    itemSlideWindowMap.put(itemKey, itemQueue);
			    itemQueue.add(timeMillis);
			}
		    }
		}else if(this.counterType == 3){
		    // 如果為滑窗+數(shù)量類型
		    if (itemSlideWindowMap.containsKey(itemKey)) {
		        Deque<Long> itemQueue = itemSlideWindowMap.get(itemKey);				
			// 加鎖保護
			synchronized(itemQueue) {
			    Long head = 0L;
			    // 循環(huán)處理頭部數(shù)據(jù)
			    while(true) {
			        // 取得頭部數(shù)據(jù)
				head = itemQueue.peekFirst();
				if (head == null || timeMillis - head <= this.windowSize) {
				    break;
				}
				// 移除頭部
				itemQueue.remove();
			    }
			    // 加入新數(shù)據(jù)
			    itemQueue.add(timeMillis);					
			}								
		    }else {
			// 新key值,加鎖保護
			Deque<Long> itemQueue = new ArrayDeque<Long>();
			synchronized(itemSlideWindowMap) {
			    // 加入映射表
			    itemSlideWindowMap.put(itemKey, itemQueue);
			    itemQueue.add(timeMillis);
			}
		    }			
		}				
	}
		
	/**
	 * 
	 * @methodName	: clear
	 * @description	: 清空字典
	 * @history		:
	 * ------------------------------------------------------------------------------
	 * date			version		modifier		remarks                   
	 * ------------------------------------------------------------------------------
	 * 2021/08/03	1.0.0		sheng.zheng		初版
	 * 2021/08/08	1.0.1		sheng.zheng		支持多種類型計數(shù)器
	 *
	 */
	public void clear() {
		if (this.counterType == 1) {
			// 如果為計數(shù)器類型
			synchronized(this) {
				itemMap.clear();
			}				
		}else if(this.counterType == 2){
			// 如果為滑窗類型	
			synchronized(this) {
				itemSlideWindowMap.clear();
			}				
		}else if(this.counterType == 3){
			// 如果為滑窗+數(shù)量類型
			synchronized(this) {
				itemSlideWindowMap.clear();
			}				
		}			
	}
}

2.3、調(diào)用

  要調(diào)用計數(shù)器,只需在應用類中添加DacService對象,如:

public class DataCommonService {
	// 數(shù)據(jù)訪問計數(shù)服務類,時間滑動窗口,窗口寬度60秒
	protected DacService dacService = new DacService(2,0,60000);

	/**
	 * 
	 * @methodName		: procNoClassData
	 * @description		: 對象組key對應的數(shù)據(jù)不存在時的處理
	 * @param classKey	: 對象組key
	 * @return		: 數(shù)據(jù)加載成功,返回true,否則為false
	 * @history		:
	 * ------------------------------------------------------------------------------
	 * date			version		modifier		remarks                   
	 * ------------------------------------------------------------------------------
	 * 2021/08/08	1.0.0		sheng.zheng		初版
	 *
	 */
	protected boolean procNoClassData(Object classKey) {
		boolean bRet = false;
		String key = getCombineKey(null,classKey);
		Long currentTime = System.currentTimeMillis();
		// 判斷計數(shù)器是否將滿
		if (dacService.isItemKeyFull(key,currentTime)) {
			// 如果計數(shù)將滿
			// 復位
			dacService.resetItemKey(key);
			// 從數(shù)據(jù)庫加載分組數(shù)據(jù)項
			bRet = loadGroupItems(classKey);
		}
		dacService.putItemkey(key,currentTime);
		return bRet;
	}
	
	/**
	 * 
	 * @methodName		: procNoItemData
	 * @description		: 對象key對應的數(shù)據(jù)不存在時的處理
	 * @param itemKey	: 對象key
	 * @param classKey	: 對象組key
	 * @return		: 數(shù)據(jù)加載成功,返回true,否則為false
	 * @history		:
	 * ------------------------------------------------------------------------------
	 * date			version		modifier		remarks                   
	 * ------------------------------------------------------------------------------
	 * 2021/08/08	1.0.0		sheng.zheng		初版
	 *
	 */
	protected boolean procNoItemData(Object itemKey, Object classKey) {
		// 如果itemKey不存在
		boolean bRet = false;
		String key = getCombineKey(itemKey,classKey);
		
		Long currentTime = System.currentTimeMillis();
		if (dacService.isItemKeyFull(key,currentTime)) {
			// 如果計數(shù)將滿
			// 復位
			dacService.resetItemKey(key);
			// 從數(shù)據(jù)庫加載數(shù)據(jù)項
			bRet = loadItem(itemKey, classKey);
		}
		dacService.putItemkey(key,currentTime);			
		return bRet;
	}

	/**
	 * 
	 * @methodName		: getCombineKey
	 * @description		: 獲取組合key值
	 * @param itemKey	: 對象key
	 * @param classKey	: 對象組key
	 * @return		: 組合key
	 * @history		:
	 * ------------------------------------------------------------------------------
	 * date			version		modifier		remarks                   
	 * ------------------------------------------------------------------------------
	 * 2021/08/08	1.0.0		sheng.zheng		初版
	 *
	 */
	protected String getCombineKey(Object itemKey, Object classKey) {
		String sItemKey = (itemKey == null ? "" : itemKey.toString());
		String sClassKey = (classKey == null ? "" : classKey.toString());
		String key = "";
		if (!sClassKey.isEmpty()) {
			key = sClassKey;
		}
		if (!sItemKey.isEmpty()) {
			if (!key.isEmpty()) {
				key += "-" + sItemKey;
			}else {
				key = sItemKey;
			}
		}
		return key;
	}
}

  procNoClassData方法:分組數(shù)據(jù)不存在時的處理。procNoItemData方法:單個數(shù)據(jù)項不存在時的處理。

  主從關(guān)系在數(shù)據(jù)庫中,較為常見,因此針對分組數(shù)據(jù)和單個對象key分別編寫了方法;如果key的個數(shù)超過2個,可以類似處理。

作者:阿拉伯1999 出處:http://www.cnblogs.com/alabo1999/ 本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權(quán)利. 養(yǎng)成良好習慣,好文章隨手頂一下。

到此這篇關(guān)于Spring Boot實現(xiàn)數(shù)據(jù)訪問計數(shù)器方案詳解的文章就介紹到這了,更多相關(guān)Spring Boot數(shù)據(jù)訪問計數(shù)器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 8中如何獲取參數(shù)名稱的方法示例

    Java 8中如何獲取參數(shù)名稱的方法示例

    這篇文章主要給大家介紹了在Java 8中如何獲取參數(shù)名稱的方法,文中給出了詳細的介紹和方法示例,相信對大家的理解和學習具有一定的參考借鑒價值,有需要的朋友可以參考學習,下面來一起看看吧。
    2017-01-01
  • 基于java中兩個對象屬性的比較

    基于java中兩個對象屬性的比較

    下面小編就為大家?guī)硪黄趈ava中兩個對象屬性的比較。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • IDEA項目如何取消git版本管控并添加svn版本控制

    IDEA項目如何取消git版本管控并添加svn版本控制

    在公司內(nèi)部服務器環(huán)境下,將代碼倉庫從Gitee的Git遷移到SVN可以避免外部版本控制的風險,遷移過程中,先刪除項目的.git文件夾,再通過Eclipse的設置界面刪除原Git配置并添加SVN配置,之后,將項目提交到SVN倉庫,確保使用ignore列表過濾不必要的文件
    2024-10-10
  • spring-retry簡單使用方法

    spring-retry簡單使用方法

    這篇文章主要介紹了spring-retry簡單使用方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • springboot集成測試容器重啟問題的處理

    springboot集成測試容器重啟問題的處理

    這篇文章主要介紹了springboot集成測試容器重啟問題的處理,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • SpringBoot開發(fā)中的組件和容器詳解

    SpringBoot開發(fā)中的組件和容器詳解

    這篇文章主要介紹了SpringBoot開發(fā)中的組件和容器詳解,SpringBoot 提供了一個內(nèi)嵌的 Tomcat 容器作為默認的 Web 容器,同時還支持其他 Web 容器和應用服務器,需要的朋友可以參考下
    2023-09-09
  • SpringMVC中的攔截器詳解及代碼示例

    SpringMVC中的攔截器詳解及代碼示例

    這篇文章主要介紹了SpringMVC中的攔截器詳解及代碼示例,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • 解決java.sql.SQLException:索引中丟失 IN或OUT 參數(shù)::x問題

    解決java.sql.SQLException:索引中丟失 IN或OUT 參數(shù)::x問題

    這篇文章主要介紹了解決java.sql.SQLException:索引中丟失 IN或OUT 參數(shù)::x問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • idea創(chuàng)建SpringBoot項目時Type選maven?project和maven?pom有何區(qū)別

    idea創(chuàng)建SpringBoot項目時Type選maven?project和maven?pom有何區(qū)別

    Maven是一個Java工程的管理工具,跟其相同功能的工具如Gradle,下面這篇文章主要給大家介紹了關(guān)于idea創(chuàng)建SpringBoot項目時Type選maven?project和maven?pom有何區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • Java實現(xiàn)多線程下載和斷點續(xù)傳

    Java實現(xiàn)多線程下載和斷點續(xù)傳

    這篇文章主要為大家詳細介紹了Java實現(xiàn)多線程下載和斷點續(xù)傳,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06

最新評論