电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

Spring Boot實現數據訪問計數器方案詳解

瀏覽:2日期:2023-06-26 18:28:45
目錄1、數據訪問計數器2、代碼實現2.1、方案說明2.2、代碼2.3、調用1、數據訪問計數器

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

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

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

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

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

2、代碼實現2.1、方案說明

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

2)上述三種情況,使用類型屬性區分,并在構造函數中進行設置。

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

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

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

  要調用計數器,只需在應用類中添加DacService對象,如:

public class DataCommonService {// 數據訪問計數服務類,時間滑動窗口,窗口寬度60秒protected DacService dacService = new DacService(2,0,60000);/** * * @methodName: procNoClassData * @description: 對象組key對應的數據不存在時的處理 * @param classKey: 對象組key * @return: 數據加載成功,返回true,否則為false * @history: * ------------------------------------------------------------------------------ * dateversionmodifierremarks * ------------------------------------------------------------------------------ * 2021/08/081.0.0sheng.zheng初版 * */protected boolean procNoClassData(Object classKey) {boolean bRet = false;String key = getCombineKey(null,classKey);Long currentTime = System.currentTimeMillis();// 判斷計數器是否將滿if (dacService.isItemKeyFull(key,currentTime)) {// 如果計數將滿// 復位dacService.resetItemKey(key);// 從數據庫加載分組數據項bRet = loadGroupItems(classKey);}dacService.putItemkey(key,currentTime);return bRet;}/** * * @methodName: procNoItemData * @description: 對象key對應的數據不存在時的處理 * @param itemKey: 對象key * @param classKey: 對象組key * @return: 數據加載成功,返回true,否則為false * @history: * ------------------------------------------------------------------------------ * dateversionmodifierremarks * ------------------------------------------------------------------------------ * 2021/08/081.0.0sheng.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)) {// 如果計數將滿// 復位dacService.resetItemKey(key);// 從數據庫加載數據項bRet = loadItem(itemKey, classKey);}dacService.putItemkey(key,currentTime);return bRet;}/** * * @methodName: getCombineKey * @description: 獲取組合key值 * @param itemKey: 對象key * @param classKey: 對象組key * @return: 組合key * @history: * ------------------------------------------------------------------------------ * dateversionmodifierremarks * ------------------------------------------------------------------------------ * 2021/08/081.0.0sheng.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方法:分組數據不存在時的處理。procNoItemData方法:單個數據項不存在時的處理。

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

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

到此這篇關于Spring Boot實現數據訪問計數器方案詳解的文章就介紹到這了,更多相關Spring Boot數據訪問計數器內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
主站蜘蛛池模板: 交变/复合盐雾试验箱-高低温冲击试验箱_安奈设备产品供应杭州/江苏南京/安徽马鞍山合肥等全国各地 | 砍排机-锯骨机-冻肉切丁机-熟肉切片机-预制菜生产线一站式服务厂商 - 广州市祥九瑞盈机械设备有限公司 | 香港新时代国际美容美发化妆美甲培训学校-26年培训经验,值得信赖! | 聚丙烯酰胺_厂家_价格-河南唐达净水材料有限公司 | 交联度测试仪-湿漏电流测试仪-双85恒温恒湿试验箱-常州市科迈实验仪器有限公司 | 煤粉取样器-射油器-便携式等速飞灰取样器-连灵动 | 安徽控制器-合肥船用空调控制器-合肥家电控制器-合肥迅驰电子厂 安徽净化板_合肥岩棉板厂家_玻镁板厂家_安徽科艺美洁净科技有限公司 | 达利园物流科技集团- | 开云(中国)Kaiyun·官方网站 - 登录入口 | 仿真茅草_人造茅草瓦价格_仿真茅草厂家_仿真茅草供应-深圳市科佰工贸有限公司 | 工作服定制,工作服定做,工作服厂家-卡珀职业服装(苏州)有限公司 | 空气净化器租赁,空气净化器出租,全国直租_奥司汀净化器租赁 | 深圳活动策划公司|庆典策划|专业公关活动策划|深圳艺典文化传媒 重庆中专|职高|技校招生-重庆中专招生网 | 陕西安闸机-伸缩门-车牌识别-广告道闸——捷申达门业科技 | 理化生实验室设备,吊装实验室设备,顶装实验室设备,实验室成套设备厂家,校园功能室设备,智慧书法教室方案 - 东莞市惠森教学设备有限公司 | 净化车间装修_合肥厂房无尘室设计_合肥工厂洁净工程装修公司-安徽盛世和居装饰 | 缓蚀除垢剂_循环水阻垢剂_反渗透锅炉阻垢剂_有机硫化物-郑州威大水处理材料有限公司 | 济南玻璃安装_济南玻璃门_济南感应门_济南玻璃隔断_济南玻璃门维修_济南镜片安装_济南肯德基门_济南高隔间-济南凯轩鹏宇玻璃有限公司 | 干法制粒机_智能干法制粒机_张家港市开创机械制造有限公司 | 手持气象站_便携式气象站_农业气象站_负氧离子监测站-山东万象环境 | 东莞市踏板石餐饮管理有限公司_正宗桂林米粉_正宗桂林米粉加盟_桂林米粉加盟费-东莞市棒子桂林米粉 | 扬尘在线监测系统_工地噪声扬尘检测仪_扬尘监测系统_贝塔射线扬尘监测设备「风途物联网科技」 | 化工ERP软件_化工新材料ERP系统_化工新材料MES软件_MES系统-广东顺景软件科技有限公司 | PCB设计,PCB抄板,电路板打样,PCBA加工-深圳市宏力捷电子有限公司 | 无负压供水设备,消防稳压供水设备-淄博创辉供水设备有限公司 | 微信聊天记录恢复_手机短信删除怎么恢复_通讯录恢复软件下载-快易数据恢复 | 电气控制系统集成商-PLC控制柜变频控制柜-非标自动化定制-电气控制柜成套-NIDEC CT变频器-威肯自动化控制 | 河北凯普威医疗器材有限公司,高档轮椅系列,推车系列,座厕椅系列,协步椅系列,拐扙系列,卫浴系列 | 河北中仪伟创试验仪器有限公司是专业生产沥青,土工,水泥,混凝土等试验仪器的厂家,咨询电话:13373070969 | 新型锤式破碎机_新型圆锥式_新型颚式破碎机_反击式打沙机_锤式制砂机_青州建源机械 | 金属波纹补偿器厂家_不锈钢膨胀节价格_非金属伸缩节定制-庆达补偿器 | 免费个人pos机申请办理-移动pos机刷卡-聚合收款码办理 | 北京百度网站优化|北京网站建设公司-百谷网络科技 | 桥架-槽式电缆桥架-镀锌桥架-托盘式桥架 - 上海亮族电缆桥架制造有限公司 | 纯化水设备-纯水设备-超纯水设备-[大鹏水处理]纯水设备一站式服务商-东莞市大鹏水处理科技有限公司 | 高低温试验箱-模拟高低温试验箱订制-北京普桑达仪器科技有限公司【官网】 | 武汉天安盾电子设备有限公司 - 安盾安检,武汉安检门,武汉安检机,武汉金属探测器,武汉测温安检门,武汉X光行李安检机,武汉防爆罐,武汉车底安全检查,武汉液体探测仪,武汉安检防爆设备 | 卫浴散热器,卫浴暖气片,卫生间背篓暖气片,华圣格浴室暖气片 | 聚丙烯酰胺PAM-聚合氯化铝PAC-絮凝剂-河南博旭环保科技有限公司 巨野电机维修-水泵维修-巨野县飞宇机电维修有限公司 | 短信通106短信接口验证码接口群发平台_国际短信接口验证码接口群发平台-速度网络有限公司 | 合肥注册公司|合肥代办营业执照、2024注册公司流程 |