项目中有些场景可能需要服务限流,服务限流有多种实现方式,本文将做一个简单示例,采用计数器的方式,通过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次,访问成功";
}
}
}
可以手写一个客户端(很简单,不再详述)模拟服务调用,下面是测试的结果:
通过测试结果,发现达到了预期。本文只是一个简单的举例,针对具体场景可以灵活的拓展,不足之处敬请斧正。