本文采用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限流解决方案相关推荐

  1. Java限流解决方案

    前言 说到限流,想必大家都不陌生,一个很简单的例子就是,在12306上面买票的时候,遇到某时刻开始抢票的时候,经常页面会弹出一个类似请稍后重试的提示,从后端的技术层面来看,大概有2层解释,第一是服务器 ...

  2. SpringMvc 限流之 RateLimiter

    概念 限流 限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务.排队或等待.降级等处理 常用限流算法 常用的限流算法有两种:漏桶算法 ...

  3. 互联网高并发解决方案(2)--高并发服务限流特技

    RPC和本地JAVA调用的区别 RPC远程调用:一般是可以跨平台使用的,采用Socket技术,只要语言支持socket技术就可以进行互相通信.其实就是socket+反射实现的. 本地调用:只能支持Ja ...

  4. Java限流之 —— Sentinel初识

    前言 在之前的篇章中,我们聊到了限流的常用解决方案,基于限流衍生出了一些适合在并发场景下的解决方案,常用的像单机模式下的guawa限流,基于原生的限流算法如漏桶.令牌桶等封装出限流逻辑,redis+l ...

  5. redis+lua实现分布式限流

    前言 之前我们谈到的限流解决方案中提到了在分布式环境下,可以使用redis结合lua进行限流,与网关层限流不同的是,基于Redis+Lua的分布式限流属于服务端限流 使用redis做限流原因 在前面的 ...

  6. Java限流之 —— Guawa

    前言 在上一篇,我们聊了下限流相关的概念和常用的限流解决方案,本篇我们先从最简单的限流组件Guawa开始 关于Guawa的介绍,我们上一篇有过简单的介绍,这里不再过多说明,直接从撸码开始吧 1.pom ...

  7. 秒杀限制人群,如何设计秒杀服务的限流策略?

    对于秒杀业务,大家应该比较熟悉了.比如,"某商品原价 1299 元, 双十一整点秒杀价仅 500 元,限量 100 件,先到先得" 等等.通过这段文案我们能够发现,参与秒杀活动商品 ...

  8. 【高并发】亿级流量场景下如何实现分布式限流?看完我彻底懂了!!(文末有福利)

    写在前面 在互联网应用中,高并发系统会面临一个重大的挑战,那就是大量流高并发访问,比如:天猫的双十一.京东618.秒杀.抢购促销等,这些都是典型的大流量高并发场景.关于秒杀,小伙伴们可以参见我的另一篇 ...

  9. redis rua解决库存问题_库存秒杀问题-redis解决方案- 接口限流

    /** * Created by PhpStorm. * redis 销量超卖秒杀解决方案 * redis 文档:http://doc.redisfans.com/ * ab -n 10000 -c ...

最新文章

  1. [Vuex系列] - Vuex中的getter的用法
  2. hashmap扩容 面试_HashMap面试,看完这一篇就够了(上)
  3. python读书笔记2000_流畅的Python读书笔记
  4. 一个用于伪造IP地址进行爆破的BurpSuite插件:BurpFakeIP
  5. 深入理解K-Means聚类算法
  6. 5种你未必知道的JavaScript和CSS交互的方法
  7. 怎么让网页中的文字两边留出空白_横线、方格、点阵、空白本,谁才是笔记本中的C位?...
  8. 时间插件只能选择整点和半点_外贸人如何把控合适的客户开发时间及跟进频率...
  9. 基于eclipse的android项目实战—博学谷(一)欢迎界面
  10. TCP服务器端与多个客户端连接的C#代码实现
  11. 创新案例分享 | 升级改造干部档案管理系统,精确剖析干部执行力情况
  12. 国内云服务器提供商排名(仅供参考)
  13. 循环优化与多面体模型
  14. 2021-06-14 Socketio学习使用搭建一个聊天室
  15. 科普文——浅析拉卡拉支付安全通道建设
  16. python中 s是什么意思_这里面的s.name是什么意思啊?
  17. latex里设置居中左对齐
  18. Chrome 浏览器 Javascript 调试参考
  19. STL迭代器(iterator)用法详解
  20. Directory 与 DirectoryInfo 的区别

热门文章

  1. [html] HTML5的video怎样预加载(支持全量加载)?
  2. [html] 可以给内联元素设置宽和高吗?为什么?
  3. [vue] 你有使用做过vue与原生app交互吗?说说vue与ap交互的方法
  4. [css] scroll-snap-align属性的应用场景是什么?
  5. “约见”面试官系列之常见面试题之第五十五篇之清除浮动的方法(建议收藏)
  6. 前端学习(786):数组创建的两种方式
  7. 前端学习(759):预解析案例
  8. spring学习(23):基础组件
  9. 第五十八期:从0到1 手把手教你建一个区块链
  10. 第二十一期:拜托!面试不要再问我Spring Cloud底层原理