java基於map的一個簡單限流-代碼

項目請求接口的簡單限流實現

 

定義一個限流類:

 

/**
 * @author wangwei
 * @version v1.0.0
 * @description
 * @date
 */
public class CacheValidate {

    private long time;
    private int invokeNum;

    public long getTime() {
        return time;
    }
    public void setTime(long time) {
        this.time = time;
    }
    public int getInvokeNum() {
        return invokeNum;
    }
    public void setInvokeNum(int invokeNum) {
        this.invokeNum = invokeNum;
    }

    /*
     *
     * 校驗方法是否有效
     */
    public boolean isValidate(int limit){
        this.invokeNum = invokeNum + 1;
        if(System.currentTimeMillis() / 1000 <= time){
            System.err.println(System.currentTimeMillis() / 1000);
            if(invokeNum <= limit){
                return true;
            }
        }else{
            this.invokeNum = 1;
            this.time=System.currentTimeMillis() / 1000;
            return true;
        }
        return false;
    }
}

 

 

一個限流的實現

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author wangwei
 * @version v1.0.0
 * @description
 * @date
 */
@Component
public class FlowLimit {

    private static Map<String, CacheValidate> cache = new HashMap<String, CacheValidate>();

    public boolean invoke(String apiName, int sec, int limit) {
        if(apiName==null){
            return false;
        }
        CacheValidate cacheValidate = null;
        // 增加緩存中的值
        synchronized (cache) {
            cacheValidate = cache.get(apiName);
            if(cacheValidate==null){
                cacheValidate = new CacheValidate();
                cacheValidate.setTime(System.currentTimeMillis() / 1000 + sec);
                cacheValidate.setInvokeNum(1);
                cache.put(apiName, cacheValidate);
                return true;
            }
            return cacheValidate.isValidate(limit);
        }
    }

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(700);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            service.submit(getTask());
        }
        service.shutdown();
    }

    public static Runnable getTask(){
        return new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    FlowLimit fLimit = new FlowLimit();
                    System.err.println(fLimit.invoke("aaa", 1, 1));
                }
            }
        };
    }

}

 

錯誤碼定義類

public enum ErrorCode {

    SYSTEM_ERROR(500, "系統錯誤"),
    PARAMETER_CHECK_ERROR(400, "參數校驗錯誤"),
    AUTH_VALID_ERROR(701, "用戶權限不足"),
    UNLOGIN_ERROR(401, "用戶未登錄或登錄狀態超時失效"),

    CODE_430(430, "數據被篡改"),
    CODE_431(431, "祕鑰不正確"),
    CODE_432(432, "請求太頻繁,限流,請稍後再試"),

    CODE_450(450, "賬戶或者密碼不正確"),
    CODE_451(451, "身份證號碼驗證失敗"),

    CODE_6000(6000, "數據繁忙,請再試一次吧"),
    CODE_6001(6001, "手機號碼已經註冊,如果您忘記密碼,請找回密碼"),

    CODE_6010(6010, "銀行卡已被綁定過,不可以再次綁定"),

    CODE_6800(6800, "數據處理失敗"),

    CODE_9999(9999, "未知區域"),
    ;

    private final Integer value;
    private final String message;

    ErrorCode(int value, String message) {
        this.value = value;
        this.message = message;
    }

    public int getValue() {
        return value;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return value.toString();
    }

    public String getCode() {
        return value.toString();
    }

    public static ErrorCode getByCode(Integer value) {
        for (ErrorCode _enum : values()) {
            if (_enum.getValue() == value) {
                return _enum;
            }
        }
        return null;
    }

}

 

 

自定義一個限流的註解

import java.lang.annotation.*;

/**
 * @author wangwei
 * @version v1.0.0
 * @description 請求限流類
 * @date 2019-01-19
 */
@Inherited
@Documented
@Target({ElementType.FIELD, ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
    //標識 指定sec時間段內的訪問次數限制
    int limit() default 1;

    //標識 時間段
    int sec() default 5;
}

 

 

 攔截器類

import com.alibaba.fastjson.JSON;
import com.test.product_service.controller.base.BaseController;
import com.test.product_service.limiting.AccessLimit;
import com.test.product_service.utils.ErrorCode;
import com.test.product_service.utils.Resp;
import com.test.product_service.utils.redis.FlowLimit;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author wangwei
 * @version v1.0.0
 * @description 請求限流攔截器
 * @date 2019-01-19
 */
public class AccessLimitInterceptor implements HandlerInterceptor {

    //使用RedisTemplate操作redis
//    @Resource
//    public RedisTemplate<String, Integer> redisTemplate;

//    @Autowired
//    public RedisTemplate<String, Integer> redisTemplate;

//    @Autowired
//    public StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(handler.getClass());
        Class clazz = handler.getClass();
        System.out.println(clazz.getName());
        if (handler instanceof org.springframework.web.method.HandlerMethod) {
            org.springframework.web.method.HandlerMethod handlerMethod = (org.springframework.web.method.HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            if (!method.isAnnotationPresent(AccessLimit.class)) {
                return true;
            }
            AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
            if (accessLimit == null) {
                return true;
            }

            int limit = accessLimit.limit();
            int sec = accessLimit.sec();
            String key = getIpAddress(request) + request.getRequestURI();

            boolean flag = new FlowLimit().invoke(key, sec, limit);
            if(!flag) {
                JSON.toJSONString(Resp.fail(ErrorCode.CODE_432));
                output(response, JSON.toJSONString(Resp.fail(ErrorCode.CODE_432)));
                return false;
            } else {
                return true;
            }

            /*Object data = redisTemplate.opsForValue().get(key);
            System.out.println(String.format("data : " + data.toString()));
            Integer maxLimit = redisTemplate.opsForValue().get(key);
            if (maxLimit == null) {
                //set時一定要加過期時間
                redisTemplate.opsForValue().set(key, 1, sec, TimeUnit.SECONDS);
            } else if (maxLimit < limit) {
                redisTemplate.opsForValue().set(key, maxLimit + 1, sec, TimeUnit.SECONDS);
            } else {
                output(response, "請求太頻繁!");
                return false;
            }*/
        }
        return true;
    }

    public void output(HttpServletResponse response, String msg) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            outputStream.write(msg.getBytes("UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            outputStream.flush();
            outputStream.close();
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }

    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

}

多攔截器配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author wangwei
 * @version v1.0.0
 * @description
 * @date
 */
@EnableWebMvc
@Configuration
public class MywebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AccessLimitInterceptor()).addPathPatterns("/**");
//        registry.addInterceptor(new AccessLimitInterceptor()).excludePathPatterns("/**");
//        registry.addInterceptor(new MyInterceptor())
//                .addPathPatterns("/**");
    }

    @Bean
    AccessLimitInterceptor localInterceptor() {
        return new AccessLimitInterceptor();
    }
}

 

控制器配置自定義攔截註解

@AccessLimit(limit = 1,sec = 1)
    @GetMapping(value = "list", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public Resp list(String time,
                     @RequestParam(name = "apptype", required = true)String apptype,
                     @RequestParam(name = "ios_version", required = true)String ios_version,
                     HttpServletRequest request){
        return Resp.success();
    }

 

 

當前定義爲1s請求 一次, 1s內大於一次的請求,返回提示。

超過1s後,可正常訪問。

按接口詳細攔截

 完畢。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章