本次样例从单机层面上,采用拦截器的方式对请求限流。
资源:https://github.com/xiaozhuanfeng/rateLimiterProj
工程结构:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 热启动:项目修改,即时生效 完整的打包环境下运行的时候会被禁用 java -jar启动应用或者用一个特定的classloader启动 认为生产环境 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.jetbrains</groupId> <artifactId>annotations</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> <!-- 引入guava 包--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </dependency> <!-- 引入fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.56</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.properties
#端口号设置
server.port=8007
#日志的输出路径
logging.path=/logs/rateLimiterProj
logging.config=classpath:logback-spring.xml
#自定义限流方式,默认当请求超过指定QPS,放弃请求
rateLimit.qps = 1
rateLimit.type=acquire
rateLimit.tryAcquire.permits=1
rateLimit.tryAcquire.timeOut=100
1、新建抽象拦截器
package com.example.demo.interceptor; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.util.IOUtils; import com.example.demo.constant.ResponseEnum; import com.example.demo.dto.ResponseDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public abstract class AbstractInterceptor extends HandlerInterceptorAdapter { public final Logger log = LoggerFactory.getLogger(this.getClass()); /** * 具体拦截方法 * * @param req * @return */ protected abstract ResponseEnum handleRequest(HttpServletRequest req) throws Exception; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ResponseEnum result = ResponseEnum.SERVER_ERROR; try { result = handleRequest(request); } catch (Exception e) { log.error("handleRequest catch an Exception>>>", e); } if (ResponseEnum.OK == result) { //成功 return true; } handleFailResponse(response, result); return false; } public void handleFailResponse(HttpServletResponse resp, ResponseEnum result) { resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); ResponseDTO dto = new ResponseDTO(); dto.setCode(result.getCode()); dto.setMesg(result.getMesg()); PrintWriter pw = null; try { pw = resp.getWriter(); pw.write(JSON.toJSONString(dto)); } catch (IOException e) { log.error("handleFailResponse catch an IOException>>>", e); } finally { IOUtils.close(pw); } } }
2、新建RateLimiter Bean
package com.example.demo.configuration; import com.google.common.util.concurrent.RateLimiter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RateLimitConfig { @Value("${rateLimit.qps}") private double qps; @Bean public RateLimiter getRateLimiter() { if(0 == this.qps){ this.qps = 1.0D; } return RateLimiter.create(qps); } }
3、新建返回枚举类和响应实体类
package com.example.demo.constant; /** * 自定义响应码 */ public enum ResponseEnum { OK(200, "成功"), RATE_LIMIT(403, "访问次数受限"), AUTHENTICATION_FAIL(401, "未授权"), SERVER_ERROR(500, "服务器错误"); private int code; private String mesg; ResponseEnum(int code, String mesg) { this.code = code; this.mesg = mesg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMesg() { return mesg; } public void setMesg(String mesg) { this.mesg = mesg; } @Override public String toString() { return "ResponseEnum{" + "code=" + code + ", mesg='" + mesg + '\'' + '}'; } }