流量攔截控制

本文分享一下如何實現流量攔截

首先,流量攔截是要在某一段時間內控制訪問次數,如果訪問次數超過閾值,則拒絕訪問。

所以,要有兩個配置化信息,一個是流量監控緩存失效時間內可以調用的次數,一個是流量監控緩存失效時間

實現方式是使用Redis緩存記錄調用次數和攔截器用來攔截http請求調用。

@Service
public class RateLimitInterceptor extends HandlerInterceptorAdapter {//繼承攔截器的適配器類HandlerInterceptorAdapter
	private static final Logger LOGGER = LoggerFactory.getLogger(RateLimitInterceptor.class);
	private String rateControlFlag = "0";//是否開啓流量控制
	private Long lastLoadCacheTime = System.currentTimeMillis();
	
	@Autowired
	private RateLimitService rateLimitService;

	/**
	 * 流量控制檢查入口
	 */
	@Override
	public boolean preHandle(HttpServletRequest request,
							 HttpServletResponse response, Object handler) throws Exception {
		//根據服務器狀態或請求用戶的限制策略來進行控制

		// this is where magic happens
		super.preHandle(request, response, handler);
		
		//暫定userKey用請求方地址,可能對方是局域網。。
		String userKey = request.getRemoteAddr();
		
		Long thisTime = System.currentTimeMillis();
		//配置的刷新時間
		long configTime = 300000;
		if(thisTime - lastLoadCacheTime >configTime){
			lastLoadCacheTime = thisTime;//刷新上次加載緩存的時間
			//是否開啓流量控制
			rateControlFlag = SystemConfigUtils.getValueByCodeAndBranchChannel(SystemConfigEnum.RATE_CONTROL_SWITCH,"","");
		}
		
		if("1".equals(rateControlFlag)){//開啓流量控制
			//獲取請求方在一定時間內 剩餘可以調用的次數
			Long remainingCount = rateLimitService.decrease(userKey);
			
			if(LOGGER.isDebugEnabled()){
			  LOGGER.debug(userKey+" remainingCount:"+ remainingCount);
			}
				
			// 小於0時表示該userKey的已經超過當前時間段能訪問的次數
			if (remainingCount < 0) {//429表示你需要限制客戶端請求某個服務數量時,該狀態碼就很有用,也就是請求速度限制。
			    response.sendError(429, "Rate limit exceeded, wait for the next quarter minute");
			    return false;
			}
			response.addHeader("Remaining request count", remainingCount == null? null :remainingCount.toString());
		}
		
		return true;
	}

}

下面看一下獲取剩餘可以調用的次數

	/*
	 * 減少當前userKey的剩餘訪問次數,並返回增加後的次數
	 */
	@Override
	public long decrease(String userKey) {

		// 查詢緩存,如果沒有則初始化,並設置緩存失效時間
		Long value = MyRedisCacheUtil.getCache(CacheEnum.RATE_LIMIT.getCode(), userKey);
		if (value == null) {
			this.doRefreshConfig();//刷新緩存配置

			value = RATE_LIMIT;//初始化剩餘可以調用的次數爲配置化的閾值
			MyRedisCacheUtil.putCache(CacheEnum.RATE_LIMIT.getCode(), userKey, value);
			// 設置key的失效時間
			MyRedisCacheUtil.expire(CacheEnum.RATE_LIMIT.getCode(), userKey, this.RATE_EXPIRES, TimeUnit.SECONDS);
		} else {
			value -= 1;//剩餘次數減一
			MyRedisCacheUtil.putCache(CacheEnum.RATE_LIMIT.getCode(), userKey, value);//更新緩存
		}
		return value;
	}
	/**
	 * 刷新流量限額的配置信息
	 */
	public void doRefreshConfig() {

		String rateExpires = SystemConfigUtils.getValueByCodeAndBranchChannel(SystemConfigEnum.RATE_EXPIRES,"","");
		String rateLimitStr = SystemConfigUtils.getValueByCodeAndBranchChannel(SystemConfigEnum.RATE_LIMIT,"","");
		if (StringUtils.hasText(rateLimitStr)) {
			RATE_LIMIT = Long.parseLong(rateLimitStr);
		}
		if (StringUtils.hasText(rateExpires)) {
			RATE_EXPIRES = Long.parseLong(rateExpires);
		}
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章