Springboot整合 RateLimiter实现限流

依赖

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

  代码部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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");
 
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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;
    }
} 

 

定制化异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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;
 
    }
 
 
 
 
}

  

效果图

 

以下是完整代码部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<?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>

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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;
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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);
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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;
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 @   不忘初心2021  阅读(779)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示