springboot系列
文章目录
3.1 application.yml配置redis本地服务信息
前言
在开发过程中会遇到这样的需求,需要对某个方法(功能)做限流,单位时间内只允许被访问多少次,或者只能登录才能访问。
一、限流是什么?
限流是单位时间内只允许某个功能被访问最大次数,实现限流的方式有很多,本篇采用springboot工程+redis配合注解方式来实现。
二、实现步骤
1.pom引入相关库
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
</dependencies>
2.定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
//单位时间
int seconds();
// 最大次数
int maxCount();
//是否需要登录
boolean needLogin() default true;
}
该注解会限定在方法上,seconds:表示限流的单位时间,maxCount:表示限流的最大次数,needLogin:表示是否需要登录,默认值是需要登录才能访问。
三、redis服务操作
3.1 application.yml配置redis本地服务信息
spring:
redis:
host: 127.0.0.1
port: 6379
timeout: 20000
jedis:
pool:
max-active: 8
min-idle: 0
max-idle: 8
max-wait: -1
3.2 实现redis的增删查操作
/**
* @author zhangl
* @version 1.0
* @description: 实现redis操作
* @date 2020-09-24 21:57
*/
@Service
public class RedisServiceImpl<T> implements RedisService {
@Resource //不能用Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public T get(AccessKey ak, String key) {
return (T) redisTemplate.opsForValue().get(key);
}
@Override
public void set(AccessKey ak, String key, int i) {
redisTemplate.opsForValue().set(key, i, ak.getSeconds(), TimeUnit.SECONDS);
}
@Override
public void incr(AccessKey ak, String key) {
if (redisTemplate.hasKey(key)) {
redisTemplate.opsForValue().set(key, (Integer) redisTemplate.opsForValue().get(key) + 1, ak.getSeconds(), TimeUnit.SECONDS);
}
}
}
该服务类实现对reids的设置key、获取key、key自增操作。
四、拦截器处理限流操作
/**
* @author zhangl
* @version 1.0
* @description: 拦截器限流
* @date 2020-09-24 21:34
*/
@Component
public class AccessLimitInterceptor extends HandlerInterceptorAdapter {
@Autowired
private RedisService<Integer> redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
//获取方法中的注解,看是否有该注解
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if (accessLimit == null) {
return true;
}
int seconds = accessLimit.seconds();
int maxCount = accessLimit.maxCount();
boolean needLogin = accessLimit.needLogin();
String key = request.getRequestURI();
if (needLogin) {
User currentuser = (User) request.getSession().getAttribute(Contant.CURRENT_USER);
if (null == currentuser){
render(response, CodeMsg.ACCESS_LIMIT_UNLOGIN);
return false;
}
key += "_" + currentuser.getUserName();
}
AccessKey ak = AccessKey.withExpire(seconds);
Integer count = redisService.get(ak, key);
if (count == null) {
//第一次访问
redisService.set(ak, key, 1);
} else if (count < maxCount) {
//加1
redisService.incr(ak, key);
} else {
//超出访问次数
render(response, CodeMsg.ACCESS_LIMIT_REACHED); //这里的CodeMsg是一个返回参数
return false;
}
}
return true;
}
private void render(HttpServletResponse response, CodeMsg cm) throws Exception {
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
String str = JSON.toJSONString(Result.error(cm));
out.write(str.getBytes("UTF-8"));
out.flush();
out.close();
}
}
获取方法上的AccessLimit注解。判断是否需要登录,若要登录,检查session是否有当前登录用户,没有则返回无登录用户信息。用访问路径和用户名作为key,保存seconds时间到redis,同时判断是否存在key,第一次直接存入,当小于限流maxCount最大次数,则累加次数,当大于等于限流最大次数,则提示限流访问。
五、测试限流
@RestController
public class UserController {
@AccessLimit(seconds=5, maxCount=5, needLogin=true)
@RequestMapping("/getUser")
public Result getUser(){
return Result.success("请求成功");
}
@AccessLimit(seconds=5, maxCount=5, needLogin=false)
@RequestMapping("/login")
public Result login(HttpServletRequest request){
User user = new User();
user.setUserName("admin");
user.setPassword("adminn");
request.getSession().setAttribute(Contant.CURRENT_USER,user);
return Result.success("登录成功");
}
}
login登录方法将用户登录信息存入到session,该方法也添加限流,但不需要登录。getUser获取用户请求,添加需登录的限流。
六、总结
本篇所需要掌握技能:自定义注解、redis基础知识、springboot拦截器。
七、作者介绍
一线码农,定期更新知识,承接软件开发,欢迎码友讨论技术、关注公众号,输入“springboot-parent"获取源码

