nginx和gateway网关都是对整个系统进行访问限流

通过guava可以精确的对一个方法进行限流,基于令牌桶算法,我们通过AOP创建一个注解

 

首先引入坐标

  <dependency>
      <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
       <version>28.0-jre</version>
  </dependency>

然后新建包结构

aspect下一个接口一个注解AccessLimit,一个AOP切面AccessLimitAop

AccessLimit内容为

package com.changgou.seckill.web.aspect;

import java.lang.annotation.*;

//自定义注解
//添加元注解,前三个通常是必备的
@Inherited //A类使用了此注解,B继承A,B继承这个注解
@Documented  //生成javadoc时会标记注解,没有实质性的作用,不用关心
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE}) //这个注解可以被注解到什么地方(field 字段、枚举的常量 method 方法 type 类、枚举、注解、接口)
@Retention(RetentionPolicy.RUNTIME) //当前的注解不仅会保存在class文件中,并且jvm加载class后,该注解仍然存在
public @interface AccessLimit {


}

AccessLimitAop为

package com.changgou.seckill.web.aspect;

import com.alibaba.fastjson.JSON;
import com.changgou.entity.Result;
import com.changgou.entity.StatusCode;
import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
@Scope//默认为单例模式 常用的可选项为 singleton单例 prototype原型模式
@Aspect //声明为一个aop切面
public class AccessLimitAop {
    @Autowired
    private HttpServletResponse response;  //这错不用管

    //设置令牌的生成速率
    //每秒生成两个,并存在桶中
    private RateLimiter rateLimiter = RateLimiter.create(2.0);
    //配置一个切入点,切入点方法必须为public void 无参 无内容的方法,方法名就是id
    @Pointcut("@annotation(com.changgou.seckill.web.aspect.AccessLimit)")
    public void limit(){

    }

    @Around("limit()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        boolean flag = rateLimiter.tryAcquire();
        Object obj = null;

        if(flag){
            //允许访问
            try {
                obj = proceedingJoinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }else {
            //拒绝访问
            String errorMessage = JSON.toJSONString(new Result<>(false, StatusCode.ACCESSERROR,"fail"));
            //将信息返回到客户端
            this.outMessage(response,errorMessage);
        }
        return obj;
    }

    private void outMessage(HttpServletResponse response,String errorMessage){
        ServletOutputStream outputStream = null;
        try {
            response.setContentType("application/json;charset=utf-8");
            outputStream = response.getOutputStream();
            outputStream.write(errorMessage.getBytes("utf-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

使用时,只需将AccessLimit注解加载要限流的方法上即可

    @RequestMapping("/add")
    @AccessLimit //使用谷歌的guava自定义实现的微服务限流注解,目前限制为每秒最多访问两次
    public Result add(@RequestParam("time")String time,@RequestParam("id")Long id,@RequestParam("random")String random){ .... }

1

1

posted on 2021-01-29 20:15  wuzi2333  阅读(308)  评论(0)    收藏  举报