springmvc限流解决方案
本文采用3中限流方案:
1,谷歌的guava框架
2,使用redis技术
3,使用lua + redis 技术
限流方案类型
1,令牌桶限流(guava) 2,计数器限流(redis)
各位看官可根据自己的项目情况选择方案!!!
package com.example.webtest.controller;import java.text.SimpleDateFormat;
import java.util.Date;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import com.example.webtest.service.Isacquire;
import com.google.common.util.concurrent.RateLimiter;import redis.clients.jedis.Jedis;/*** 令牌桶限流* @author zack* QQ群:167350653*/
@Controller
public class TokenBucketLimitController {final String SUCCESS = "success";final String FAIL = "fail";/*** 定义一个令牌桶,每秒钟放两个令牌*/final RateLimiter rateLimiter = RateLimiter.create(2);/*** 普通限流测试(令牌桶限流)(缺点:不适合分布式)* <dependency>* <groupId>com.google.guava</groupId>* <artifactId>guava</artifactId>* <version>23.0</version>* </dependency>* @param name* @return*/@RequestMapping("/limiterTest")@ResponseBodypublic String limiterTest(String name) {SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");boolean acquire = false;//vip用户,2秒返回一次,一次获取4个令牌if(name!=null && "vip".equals(name)) {//尝试获取令牌acquire = rateLimiter.tryAcquire(4);}else {//尝试获取令牌acquire = rateLimiter.tryAcquire();}//判断获取令牌的结果if(acquire) {System.out.println("success获取令牌成功,当前时间:"+format.format(new Date()));return SUCCESS;}else {//当前的一秒钟之内已经没有令牌,返回失败,当前请求被限流System.out.println("fail获取令牌失败(被限流)");return FAIL+"(被限流)";}}/*** 限流阻塞测试(令牌桶限流)(缺点:不适合分布式)* @param name* @return*/@RequestMapping("/limiterTest2")@ResponseBodypublic String limiterTest2(String name) {SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");速率是每秒5个许可RateLimiter rateLimiter = RateLimiter.create(5);for(int i = 0;i<100;i++){//开启限流阻塞rateLimiter.acquire();System.out.println("下标:"+i+" success获取令牌成功,当前时间:"+format.format(new Date()));}return SUCCESS;}/*** 计数器分布式限流,使用redis保持原子性(计数器限流)* pom文件引入jedis*<dependency>* <groupId>redis.clients</groupId>* <artifactId>jedis</artifactId>* <version>3.0.1</version>*</dependency>* @param name* @return*/@RequestMapping("/limiterTest3")@ResponseBodypublic String limiterTest3(String name) {String result = SUCCESS;SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//获取redis连接Jedis jedis = new Jedis("127.0.0.1", 6379);jedis.auth("123456"); jedis.connect();//连接//限流的keyString key = "limiterTest3";//对key的value进行+1操作,aferValue就是+1后的值long aferValue = jedis.incr(key);if(aferValue == 1) {System.out.println("第一次");//设置key 60秒后失效jedis.expire(key, 60);}else {//判断是否超过限制10次if(aferValue > 10) {result = FAIL;}}System.out.println("请求次数:"+aferValue+" 当前时间:"+format.format(new Date())+" 能否成功连接:"+result);return result;}@Autowiredprivate Isacquire isacquire;/*** 使用 redis + lua 进行分布式限流(计数器限流)* @return*/@RequestMapping("/limiterTest4")@ResponseBodypublic String limiterTest4() {SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");//得到限流判断结果 ,15秒最多10次请求if(isacquire.acquire("limiterTest4",15,10)) {System.out.println("success获取令牌成功,当前时间:"+format.format(new Date()));return SUCCESS;}else {//当前的一秒钟之内已经没有令牌,返回失败,当前请求被限流System.out.println("fail获取令牌失败(被限流)");return FAIL+"(被限流)";}}}
下面为limiterTest4方法所用到的service类
package com.example.webtest.service;import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;import redis.clients.jedis.Jedis;@Service
public class Isacquire {private DefaultRedisScript<Long> redisScript;/*** 是否被限流* @param limitKey 关键字* @param second 限制秒数* @param limitCount 限制次数* @return* * pom文件需导入* <dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId></dependency>* */public boolean acquire(String limitKey,Integer second,Integer limitCount) {//获取redis连接Jedis jedis = new Jedis("127.0.0.1",6379);jedis.auth("123456"); jedis.connect();//连接redisScript = new DefaultRedisScript<>();//设置lua的返回值为luaredisScript.setResultType(Long.class);//加载我们自己写的lua脚本redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua")));//执行lua脚本 参数说明:(lua脚本,key的数量,限制10次,60秒)Long result = (Long) jedis.eval(redisScript.getScriptAsString(),1,limitKey,String.valueOf(limitCount),String.valueOf(second));if(result == 0) {System.out.println("被限流");return false;}return true;}}
rateLimiter.lua文件
--获取传入的参数key
local key = KEYS[1];
--限制参数
local limitCount = ARGV[1];
--限制周期
local expire = ARGV[2];
--对指定的key进行+1的操作
local afterValue = redis.call('incr',key);
--第一次,设置失效时间, 备注:tonumber转为number
if afterValue == 1 thenredis.call("expire",key,tonumber(expire));return 1;
end
--判断次数是否超过限制,超过返回0
if afterValue > tonumber(limitCount) thenreturn 0;
endreturn 1;
pom.xml文件
<?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.1.6.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>webtest</artifactId><version>0.0.1-SNAPSHOT</version><name>webtest</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 引入Guava实现:令牌桶限流 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.0</version></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
备注:如需下载项目源码请在QQ群中167350653下载
另外更优雅的方式是将redis+lua限流方案改成一个注解方式,在需要的方法上加注解更灵活和方便,将在下一篇文章中展示。
springmvc限流解决方案相关推荐
- Java限流解决方案
前言 说到限流,想必大家都不陌生,一个很简单的例子就是,在12306上面买票的时候,遇到某时刻开始抢票的时候,经常页面会弹出一个类似请稍后重试的提示,从后端的技术层面来看,大概有2层解释,第一是服务器 ...
- SpringMvc 限流之 RateLimiter
概念 限流 限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务.排队或等待.降级等处理 常用限流算法 常用的限流算法有两种:漏桶算法 ...
- 互联网高并发解决方案(2)--高并发服务限流特技
RPC和本地JAVA调用的区别 RPC远程调用:一般是可以跨平台使用的,采用Socket技术,只要语言支持socket技术就可以进行互相通信.其实就是socket+反射实现的. 本地调用:只能支持Ja ...
- Java限流之 —— Sentinel初识
前言 在之前的篇章中,我们聊到了限流的常用解决方案,基于限流衍生出了一些适合在并发场景下的解决方案,常用的像单机模式下的guawa限流,基于原生的限流算法如漏桶.令牌桶等封装出限流逻辑,redis+l ...
- redis+lua实现分布式限流
前言 之前我们谈到的限流解决方案中提到了在分布式环境下,可以使用redis结合lua进行限流,与网关层限流不同的是,基于Redis+Lua的分布式限流属于服务端限流 使用redis做限流原因 在前面的 ...
- Java限流之 —— Guawa
前言 在上一篇,我们聊了下限流相关的概念和常用的限流解决方案,本篇我们先从最简单的限流组件Guawa开始 关于Guawa的介绍,我们上一篇有过简单的介绍,这里不再过多说明,直接从撸码开始吧 1.pom ...
- 秒杀限制人群,如何设计秒杀服务的限流策略?
对于秒杀业务,大家应该比较熟悉了.比如,"某商品原价 1299 元, 双十一整点秒杀价仅 500 元,限量 100 件,先到先得" 等等.通过这段文案我们能够发现,参与秒杀活动商品 ...
- 【高并发】亿级流量场景下如何实现分布式限流?看完我彻底懂了!!(文末有福利)
写在前面 在互联网应用中,高并发系统会面临一个重大的挑战,那就是大量流高并发访问,比如:天猫的双十一.京东618.秒杀.抢购促销等,这些都是典型的大流量高并发场景.关于秒杀,小伙伴们可以参见我的另一篇 ...
- redis rua解决库存问题_库存秒杀问题-redis解决方案- 接口限流
/** * Created by PhpStorm. * redis 销量超卖秒杀解决方案 * redis 文档:http://doc.redisfans.com/ * ab -n 10000 -c ...
最新文章
- [Vuex系列] - Vuex中的getter的用法
- hashmap扩容 面试_HashMap面试,看完这一篇就够了(上)
- python读书笔记2000_流畅的Python读书笔记
- 一个用于伪造IP地址进行爆破的BurpSuite插件:BurpFakeIP
- 深入理解K-Means聚类算法
- 5种你未必知道的JavaScript和CSS交互的方法
- 怎么让网页中的文字两边留出空白_横线、方格、点阵、空白本,谁才是笔记本中的C位?...
- 时间插件只能选择整点和半点_外贸人如何把控合适的客户开发时间及跟进频率...
- 基于eclipse的android项目实战—博学谷(一)欢迎界面
- TCP服务器端与多个客户端连接的C#代码实现
- 创新案例分享 | 升级改造干部档案管理系统,精确剖析干部执行力情况
- 国内云服务器提供商排名(仅供参考)
- 循环优化与多面体模型
- 2021-06-14 Socketio学习使用搭建一个聊天室
- 科普文——浅析拉卡拉支付安全通道建设
- python中 s是什么意思_这里面的s.name是什么意思啊?
- latex里设置居中左对齐
- Chrome 浏览器 Javascript 调试参考
- STL迭代器(iterator)用法详解
- Directory 与 DirectoryInfo 的区别
热门文章
- [html] HTML5的video怎样预加载(支持全量加载)?
- [html] 可以给内联元素设置宽和高吗?为什么?
- [vue] 你有使用做过vue与原生app交互吗?说说vue与ap交互的方法
- [css] scroll-snap-align属性的应用场景是什么?
- “约见”面试官系列之常见面试题之第五十五篇之清除浮动的方法(建议收藏)
- 前端学习(786):数组创建的两种方式
- 前端学习(759):预解析案例
- spring学习(23):基础组件
- 第五十八期:从0到1 手把手教你建一个区块链
- 第二十一期:拜托!面试不要再问我Spring Cloud底层原理