項目中有些場景可能需要服務限流,服務限流有多種實現方式,本文將做一個簡單示例,採用計數器的方式,通過redis實現。
需求舉例:針對某個商戶,限制一分鐘內訪問平臺服務不超過10次。
主要思路: 基於redis做一個計數器,可以以商戶編號爲key,初始值爲0,設置失效時間爲一分鐘。該商戶每訪問服務一次,計數加1,如果不超過10,允許繼續訪問,超過10,則直接返回拒絕訪問提示。當超過一分鐘時,原有的設置的鍵值失效,相當於計數器重置,又開始新的一分鐘監控。
實現過程:
Springboot整合redis詳細教程網上有很多很好的文章,本文就不做詳細介紹,下面主要貼出一些關鍵代碼。
準備一個redis工具類,名爲爲RedisUtils
package com.read.redis.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtils {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 普通緩存放入
* @param key 鍵
* @param value 值
* @return true成功 false失敗
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 判斷key是否存在
* @param key 鍵
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 指定緩存失效時間
* @param key 鍵
* @param time 時間(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 遞增
* @param key 鍵
* @param delta 要增加幾(大於0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("遞增因子必須大於0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
}
寫一個示例接口:
controller層:
import com.read.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping(path = "/redis")
public class RedisController {
@Autowired
RedisService redisService;
@RequestMapping("/requestTest")
public String requestTest(HttpServletRequest request) {
return redisService.requestTest(request);
}
}
service層:
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public interface RedisService {
public String requestTest(HttpServletRequest request);
}
實現類:
import com.read.redis.service.RedisService;
import com.read.redis.util.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
RedisUtils redisUtils;
public String requestTest(HttpServletRequest request) {
String mchId = request.getParameter("mchid"); //商戶編號
if (redisUtils.hasKey(mchId)) {
long ops = redisUtils.incr(mchId, 1);
if (ops > 10) {
return "一分鐘內訪問了" + ops + "次,超過了10次,訪問被拒絕";
} else {
return "一分鐘內訪問了" + ops + "次,未超過10次,訪問成功";
}
} else {
redisUtils.set(mchId, 0);
redisUtils.expire(mchId, 60);
redisUtils.incr(mchId, 1);
return "一分鐘內訪問了1次,未超過10次,訪問成功";
}
}
}
可以手寫一個客戶端(很簡單,不再詳述)模擬服務調用,下面是測試的結果:
通過測試結果,發現達到了預期。本文只是一個簡單的舉例,針對具體場景可以靈活的拓展,不足之處敬請斧正。