Java生鲜电商平台-接口限流的技术分析与源代码下载(小程序/APP)
说明:在实际的Java生鲜电商平台中,在对外暴露的接口中存在某些人为或者攻击者的恶意调用与攻击,这个时候为了系统的安全,就需要对某些接口进行限流操作,网上的大部分的接
口限流都是基于guava或者阿里巴巴的Sentinel,本文只是根据实际的业务出发,采用自定义注解来进行方法限流,满足我们的日常业务的要求。
1。阅读本文你需要掌握的知识为:
如何限流。如何基于注解限流,相关源代码等
2. 首先我们看下POM文件. 基于SpringBoot2.2.2 这个版本
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.2.2.RELEASE
com.ctg.test
springboot-limit-api
0.0.1-SNAPSHOT
springboot-limit-api
springboot-limit-api
1.8
org.springframework.boot
spring-boot-starter-web
org.aspectj
aspectjweaver
org.springframework.boot
spring-boot-starter-test
test
com.google.guava
guava
22.0
com.alibaba
fastjson
1.2.47
org.springframework.boot
spring-boot-maven-plugin
2. 限流的注解:可以根据自己的业务,灵活设置默认的值
import java.lang.annotation.*;
@Inherited
@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE,ElementType.PACKAGE})
@Retention(RetentionPolicy.RUNTIME)public @interfaceRateLimit {double limitNum() default 20; //默认每秒放入桶中的token
}
3. 基于sping的AOP进行业务限流
importcom.fasterxml.jackson.annotation.JsonInclude;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.google.common.util.concurrent.RateLimiter;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.Signature;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Pointcut;importorg.aspectj.lang.reflect.MethodSignature;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Scope;importorg.springframework.stereotype.Component;importjavax.servlet.ServletOutputStream;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.lang.reflect.Method;importjava.util.concurrent.ConcurrentHashMap;
@Component
@Scope
@Aspectpublic classRateLimitAspect {private Logger log = LoggerFactory.getLogger(this.getClass());//用来存放不同接口的RateLimiter(key为接口名称,value为RateLimiter)
private ConcurrentHashMap map = new ConcurrentHashMap<>();private static ObjectMapper objectMapper = newObjectMapper();privateRateLimiter rateLimiter;
@AutowiredprivateHttpServletResponse response;
@Pointcut("@annotation(com.ctg.test.limit.RateLimit)")public voidserviceLimit() {
}
@Around("serviceLimit()")public Object around(ProceedingJoinPoint joinPoint) throwsNoSuchMethodException {
Object obj= null;//获取拦截的方法名
Signature sig =joinPoint.getSignature();//获取拦截的方法名
MethodSignature msig =(MethodSignature) sig;//返回被织入增加处理目标对象
Object target =joinPoint.getTarget();//为了获取注解信息
Method currentMethod =target.getClass().getMethod(msig.getName(), msig.getParameterTypes());//获取注解信息
RateLimit annotation = currentMethod.getAnnotation(RateLimit.class);double limitNum = annotation.limitNum(); //获取注解每秒加入桶中的token
String functionName = msig.getName(); //注解所在方法名区分不同的限流策略//获取rateLimiter
if(map.containsKey(functionName)){
rateLimiter=map.get(functionName);
}else{
map.put(functionName, RateLimiter.create(limitNum));
rateLimiter=map.get(functionName);
}try{if(rateLimiter.tryAcquire()) {//执行方法
obj =joinPoint.proceed();
}else{//拒绝了请求(服务降级)
String result = objectMapper.writeValueAsString(ResultUtil.error(201, "你操作太过频繁,请稍后再试!"));
log.info("拒绝了请求:" +result);
outErrorResult(result);
}
}catch(Throwable throwable) {
throwable.printStackTrace();
}returnobj;
}//将结果返回
public voidoutErrorResult(String result) {
response.setContentType("application/json;charset=UTF-8");try (ServletOutputStream outputStream =response.getOutputStream()) {
outputStream.write(result.getBytes("utf-8"));
}catch(IOException e) {
e.printStackTrace();
}
}static{
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
}
4. 基础的业务类与返回实现. 包括基础的代码与工具类
/*** @Description: 统一接口返回值*/
public class Result{/**错误码.*/
privateInteger code;/**提示信息.*/
privateString msg;/**具体的内容.*/
privateT data;publicInteger getCode() {returncode;
}public voidsetCode(Integer code) {this.code =code;
}publicString getMsg() {returnmsg;
}public voidsetMsg(String msg) {this.msg =msg;
}publicT getData() {returndata;
}public voidsetData(T data) {this.data =data;
}
}
public classResultCode {public static final Integer SUCCESS = 200;public static final Integer ERROR = 201;public static final Integer UNKNOWERROR = 202;
}
public classResultUtil {public staticResult success() {return success(null);
}public staticResult success(Object object) {
Result result= newResult();
result.setCode(ResultCode.SUCCESS);
result.setMsg("成功");
result.setData(object);returnresult;
}public staticResult success(Integer code,Object object) {
Result result= newResult();
result.setCode(code);
result.setMsg("成功");
result.setData(object);returnresult;
}public staticResult error( String msg) {
Result result= newResult();
result.setCode(ResultCode.ERROR);
result.setMsg(msg);returnresult;
}public staticResult error(Integer code, String msg) {
Result result= newResult();
result.setCode(code);
result.setMsg(msg);
result.setData("");returnresult;
}
}
5. 进行业务代码测试
importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.ResponseBody;importjava.util.HashMap;importjava.util.Map;/*** @Description:
*http://localhost:8080/test1
*http://localhost:8080/test2*/@Controllerpublic classTestController {
@RateLimit(limitNum= 1.0)
@RequestMapping("/test1")
@ResponseBodypublicObject getResults() {return ResultUtil.success(200,"test1");
}
@RateLimit(limitNum= 10.0)
@RequestMapping("/test2")
@ResponseBodypublicObject getResultTwo(){
Mapmap =new HashMap<>();return ResultUtil.success(200,"test2");
}
}
最终的结果为:
结语
复盘与总结.
总结:
做Java生鲜电商平台的互联网应用,无论是生鲜小程序还是APP,在高可用的系统设计中限流设计思路是非常重要的,本文只是起一个抛砖引玉的作用,
希望用生鲜小程序的搭建限流的设计思路实战经验告诉大家一些实际的项目经验,希望对大家有用.
QQ:137071249
共同学习QQ群:793305035