Spring Boot实现数据访问计数器

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 :

  • date version modifier remarks

  • 2021/08/03 1.0.0 sheng.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		:
 * ------------------------------------------------------------------------------
 * date			version		modifier		remarks                   
 * ------------------------------------------------------------------------------
 * 2021/08/03	1.0.0		sheng.zheng		初版
 * 2021/08/08	1.0.1		sheng.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或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,视业务需要,取值true或false
		bRet = 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或false
		bRet = true;				
	    }			
	}
	
	return bRet;		
}
	
/**
 * 
 * @methodName		: resetItemKey
 * @description		: 复位对象key的计数 
 * @param itemKey	: 对象key
 * @history		:
 * ------------------------------------------------------------------------------
 * date			version		modifier		remarks                   
 * ------------------------------------------------------------------------------
 * 2021/08/03	1.0.0		sheng.zheng		初版
 * 2021/08/08	1.0.1		sheng.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		:
 * ------------------------------------------------------------------------------
 * date			version		modifier		remarks                   
 * ------------------------------------------------------------------------------
 * 2021/08/03	1.0.0		sheng.zheng		初版
 * 2021/08/08	1.0.1		sheng.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		:
 * ------------------------------------------------------------------------------
 * date			version		modifier		remarks                   
 * ------------------------------------------------------------------------------
 * 2021/08/03	1.0.0		sheng.zheng		初版
 * 2021/08/08	1.0.1		sheng.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		:
 * ------------------------------------------------------------------------------
 * 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();
	// 判断计数器是否将满
	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		:
 * ------------------------------------------------------------------------------
 * 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)) {
		// 如果计数将满
		// 复位
		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		:
 * ------------------------------------------------------------------------------
 * 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;
}

}
mosfet驱动芯片https://www.zg886.cn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中,我们可以创建一个简单的计数器应用,主要利用Spring MVC和一些基本的数据持久化技术(如JPA或MongoDB)。以下是一个基础的步骤: 1. **设置项目**: 创建一个新的Spring Boot项目,选择Web和MVC依赖。 2. **创建数据模型**: 定义一个`Counter`实体,通常包含一个整数`value`和一个可能的日期戳表示上次更新时间。 ```java public class Counter { private Long id; private int value; private LocalDateTime lastUpdated; // getters and setters } ``` 3. **数据访问层**: 使用JPA(如果使用Java Persistence API)创建一个`CounterRepository`接口,继承自`JpaRepository`,用于CRUD操作。 ```java public interface CounterRepository extends JpaRepository<Counter, Long> {} ``` 或者,如果你选择使用MongoDB,可以创建`MongoTemplate`或`MongoRepository`。 4. **业务逻辑**: 创建一个`CounterService`,它负责处理计数器的操作,如增加、获取当前值等,并利用`CounterRepository`进行数据访问。 5. **控制器**: 在`Controller`类中,定义RESTful endpoints,比如`/counter`来显示当前值,`/counter/inc`来增加计数值。 ```java @RestController public class CounterController { @Autowired private CounterService counterService; @GetMapping("/counter") public Counter getCount() { return counterService.getCurrentCount(); } @PostMapping("/counter/inc") public ResponseEntity<?> incrementCount() { counterService.incrementCount(); return ResponseEntity.ok().build(); } } ``` 6. **初始化计数器**: 在`ApplicationRunner`或`CommandLineRunner`中,可以设置一个初始值。 7. **测试**: 使用Spring Boot Actuator或Postman测试API端点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值