Springboot整合 RateLimiter实现限流

依赖

 <!--RateLimiter的底层是基于令牌桶算法来实现的,来自谷歌的Guava包中-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.0-jre</version>
        </dependency>

  代码部分

package com.ip.config;


import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * @Description:
 * @Author: Yourheart
 * @Create: 2022/8/4 12:54
 */
@Component
public class LimitFilter implements WebMvcConfigurer {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //每秒只能接收10个请求
        registry.addInterceptor(new LimitInterceptor(10,LimitInterceptor.LimitType.DROP))
                .addPathPatterns("/**")
                //忽略拦截
                .excludePathPatterns("/login");

    }
}

  

package com.ip.config;


import com.google.common.util.concurrent.RateLimiter;
import com.ip.exception.CustomizeErrorException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
/**
 * @Description:
 * @Author: Yourheart
 * @Create: 2022/8/4 12:55
 */
@Component
@Slf4j
public class LimitInterceptor extends HandlerInterceptorAdapter {
    public enum LimitType {
        /**
         * 丢弃
         */
        DROP,
        /**
         * 等待
         */
        WAIT
    }

    /**
     * Guava 开源工具限流工具类
     * 限流器
     */
    private RateLimiter limiter;

    /**
     * 限流方式
     */
    private LimitType limitType = LimitType.DROP;

    public LimitInterceptor() {
        this.limiter = RateLimiter.create(1);
    }

    /**
     * @param tps	限流(每秒处理量)
     * @param limitType
     */
    public LimitInterceptor(int tps, LimitInterceptor.LimitType limitType) {
        this.limiter = RateLimiter.create(tps);
        this.limitType = limitType;
    }

    /**
     * @param permitsPerSecond	每秒新增的令牌数
     * @param limitType	限流类型
     */
    public LimitInterceptor(double permitsPerSecond, LimitInterceptor.LimitType limitType) {
        this.limiter = RateLimiter.create(permitsPerSecond, 10, TimeUnit.MILLISECONDS);
        this.limitType = limitType;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (limitType.equals(LimitType.DROP)) {
            //尝试获取一个令牌,立即返回
            if (limiter.tryAcquire()) {

                return super.preHandle(request, response, handler);
            }
        }

        /**
         * 达到限流后,往页面提示的错误信息
         */
        throw new CustomizeErrorException("亲爱的主人,您已经频繁使用我很多次了,请休息后稍后再试哟");

    }

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

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

    public RateLimiter getLimiter() {
        return limiter;
    }

    public void setLimiter(RateLimiter limiter) {
        this.limiter = limiter;
    }
} 

 

定制化异常

package com.ip.exception;


/**
 * @Description:
 * @Author: Yourheart
 * @Create: 2022/8/4 13:51
 */
public class CustomizeErrorException extends Exception {

    public CustomizeErrorException() {
    }

    public CustomizeErrorException(String message) {
        super(message);
    }
}

  

package com.ip.exception;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.javassist.NotFoundException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.NestedServletException;


import java.util.HashMap;
import java.util.Map;

/**
 * @Description:
 * @Author: Yourheart
 * @Create: 2021/9/1 09:47
 */
@ControllerAdvice
@Slf4j
public class MyExceptionHandler {


    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Map<String,Object> handleException(Exception e){
        log.error("全局异常打印:",e);
        Map<String,Object> map=new HashMap<>(16);
        map.put("code","-101");
        map.put("msg",e.getMessage());
        return map;

    }




}

  

效果图

 

以下是完整代码部分

<?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.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.ip</groupId>
    <artifactId>ip-service</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <skipTests>true</skipTests>
    </properties>

    <dependencyManagement>

        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>



        </dependencies>
    </dependencyManagement>


    <dependencies>

        <!--客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--tomcat容器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--添加fastjson依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.7</version>
        </dependency>
        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>

        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!--springboot整合mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>
        <!-- 热部署模块 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
        </dependency>
        <!--java爬虫需要的jar包-->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.12.2</version>
        </dependency>
        <!--判断空的用法  -->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.10.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.lionsoul</groupId>
            <artifactId>ip2region</artifactId>
            <version>1.7.2</version>
        </dependency>
         <!--RateLimiter的底层是基于令牌桶算法来实现的,来自谷歌的Guava包中-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.0-jre</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>


            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

        </plugins>
        <finalName>server0807</finalName>
    </build>


</project>

  

package com.ip.controller.front;

import com.alibaba.fastjson.JSONObject;
import com.ip.service.QueryIpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author yourheart
 * @Description
 * @create 2021-06-06 10:59
 */
@Controller
@RequestMapping("/queryIp")
@Slf4j
public class QueryIpController {

    @Autowired
    private QueryIpService queryIpService;

    @RequestMapping("/queryIpCity/{ip}")
    @ResponseBody
    public String queryIpCity(@PathVariable String ip){
        long a = System.currentTimeMillis();
        JSONObject ipCityByIp = queryIpService.getIpCityByIp(ip);
        String jsonString = ipCityByIp.toJSONString();
        long b = System.currentTimeMillis();
        log.info("接口调用耗时:{}",(b-a)+"ms");
        return jsonString;
    }
}

  

package com.ip.service;

import com.alibaba.fastjson.JSONObject;

/**
 * @author yourheart
 * @Description
 * @create 2021-06-06 10:44
 */
public interface QueryIpService {
    /**
     * 获取ip地址的信息
     * @return
     */
    JSONObject getIpCity();

    /**
     * 通过ip地址查询对应的ip信息
     * @param ip
     * @return
     */
    JSONObject getIpCityByIp(String ip);

    /**
     * 通过ip查询对应的城市信息
     * @param ip
     * @return
     */
    String queryCityByIp(String ip);
}

  

package com.ip.service.impl;


import com.alibaba.fastjson.JSONObject;
import com.ip.bean.QueryIpBean;
import com.ip.constants.Constant;
import com.ip.mapper.QueryIpMapper;
import com.ip.service.QueryIpService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import java.io.IOException;

/**
 * @author yourheart
 * @Description
 * @create 2021-06-06 10:46
 */
@Service
@Slf4j
public class QueryIpServiceImpl implements QueryIpService {



    @Autowired
    private QueryIpMapper queryIpMapper;


    /**
     * 获取ip地址的信息
     * @return
     */
    @Override
    public JSONObject getIpCity() {
        return null;
    }


    public  String recordIp(String ip) {
        String url = "http://ip.taobao.com/outGetIpInfo?ip=" + ip + "&accessKey=alibaba-inc";
        String jsonStr = okGetArt(url);
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        JSONObject result = (JSONObject) jsonObject.get("data");
        return result.get("country") +""+ result.get("region")+""+ result.get("city")+""+result.get("isp");

    }

    public String okGetArt(String url){
        String html=null;
        OkHttpClient okHttpClient = new OkHttpClient();
        Request build = new Request.Builder().url(url).addHeader("Content-type", "charset=utf-8").build();
        try (Response responseObj = okHttpClient.newCall(build).execute()){
            html = responseObj.body().string();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return html;
    }


    public JSONObject judgeIp(String ip) {
        long a = System.currentTimeMillis();
        int queryIpNum = queryIpMapper.queryIpNum(ip);
        long b = System.currentTimeMillis();
        log.info("queryIpNum查询耗时:{}",(b-a)+"ms");
        log.info("queryIpNum:{}", queryIpNum);
        if (queryIpNum == 1) {
            long a1 = System.currentTimeMillis();
            QueryIpBean queryIpBean = queryIpMapper.queryIp(ip);
            long b1 = System.currentTimeMillis();
            log.info("queryIp查询耗时:{}",(b1-a1)+"ms");
            log.info("queryIpBean:{}", queryIpBean);
            JSONObject object1=new JSONObject();
            object1.put("ip", ip);
            object1.put("address",queryIpBean.getIpContent());
            return object1;
        } else if (queryIpNum == 0) {
            //调用接口获取ip地址的信息
            String addr = null;
            try {
                addr = recordIp(ip);
                log.info("调用接口返回参数:{}",addr);
            } catch (RestClientException e) {
                String city="城市信息未能获取";
                JSONObject object1=new JSONObject();
                object1.put("ip", ip);
                object1.put("address",city);
                return object1;
            }
            JSONObject object=new JSONObject();
            object.put("ip",ip);
            object.put("address",addr);
            QueryIpBean queryIpBean = new QueryIpBean();
            queryIpBean.setIp(ip);
            queryIpBean.setIpContent(addr);
            int addIp = queryIpMapper.addIp(queryIpBean);
            if (addIp == 1) {
                log.info("ip数据库中新增一条ip信息");
                return object;
            } else {
                log.info("新增ip信息添加失败");
                return object;
            }

        }
       return null;
    }

    /**
     * 通过ip地址查询对应的ip信息
     * @param ips
     * @return
     */
    @Override
    public JSONObject getIpCityByIp(String ips) {
        log.info("入参:"+ips);
        if (Constant.NULL.equals(ips)){
            JSONObject object=new JSONObject();
            object.put("ip","0.0.0.0");
            object.put("address","小坏蛋,你给我个null,就想骗我的人");
            return object;
        }
        JSONObject object1 = new JSONObject();
        String ipp=ips;
        if (StringUtils.isEmpty(ipp)){
            String ip="127.0.0.1";
            String city="ip地址为null";
            object1.put("ip", ip);
            object1.put("city",city);
            return object1;
        }
        return judgeIp(ips);

    }
    protected String getAddress(JSONObject jsonObject,String isp){
        String address=null;
        if (StringUtils.isNotEmpty(jsonObject.getString(Constant.COUNTRY))){
            address=jsonObject.getString("Country");
        }
        if (StringUtils.isNotEmpty(jsonObject.getString(Constant.PROVINCE))){
            address=address+jsonObject.getString("Province");
        }
        if (StringUtils.isNotEmpty(jsonObject.getString(Constant.CITY))){
            address=address+jsonObject.getString("City");
        }
        if (StringUtils.isNotEmpty(isp)){
            address=address+isp;
        }
        if (StringUtils.isEmpty(address)){
            address="地址飘到火星了";
        }
        return address;

    }

    /**
     * 通过ip查询对应的城市信息
     * @param ip
     * @return
     */
    @Override
    public String queryCityByIp(String ip) {
        return null;
    }
}

  

package com.ip.bean;


import lombok.Data;

/**
 * @author yourheart
 * @Description
 * @create 2021-11-23 22:03
 */
@Data
public class QueryIpBean {

    /**
     * 主键
     */
    private String id;
    /**
     * ip地址
     */
    private String ip;
    /**
     * ip地址的详细信息
     */
    private String ipContent;
    /**
     * ip信息存储时间
     */
    private String ipTime;
}

  建议使用压测,测试是否可以限流,压测工具使用教程地址

https://www.cnblogs.com/q202105271618/p/17214621.html

每秒只能接受10个请求,如下配置

 

 

 

 

 

posted @ 2022-08-19 17:58  不忘初心2021  阅读(744)  评论(0编辑  收藏  举报