在前面的springcloud实战项目中我们有介绍使用setinel进行限流。这篇文章我们介绍下使用谷歌的guava框架实现单个接口的令牌筒限流。
首先我们介绍下什么是令牌筒限流:
令牌桶算法的原理也比较简单,我们可以理解成医院的挂号看病,只有拿到号以后才可以进行诊病。 系统会维护一个令牌(token)桶,以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。 令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制。
整体的令牌桶算法流程如下:
下面我们实战演示下使用令牌筒算法实现单个接口的限流。
一、新建一个maven项目,并且poml依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1-jre</version> </dependency> <!-- log start --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.6</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> <!-- OKHttp3依赖 --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.8.1</version> </dependency>
二、创建一个令牌筒公共类
这里我们把令牌筒的RateLimiter放在公共类里面,方便进行调整,完整代码如下:
package com.demo.api.utils; import com.google.common.util.concurrent.RateLimiter; public class Constants { /** * 限流策略 :1秒钟2个请求 */ public static RateLimiter limiter = RateLimiter.create(2.0); }
三、编写一个controller,实现令牌筒的判断逻辑
实现单个接口的令牌筒限流,我们在单个接口里面进行令牌筒的判断即可,完整代码如下:
package com.demo.api.controller; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.TimeUnit; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.demo.api.utils.Constants; import com.google.common.util.concurrent.RateLimiter; import lombok.extern.slf4j.Slf4j; @Slf4j @RestController @RequestMapping("/limit") public class Controller { private DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); @GetMapping("/testLimiter") public String testLimiter() { // 500毫秒内,没拿到令牌,就直接进入服务降级 boolean tryAcquire = Constants.limiter.tryAcquire(500, TimeUnit.MILLISECONDS); //如果没有拿到令牌,则提示稍候重试 if (!tryAcquire) { return "当前排队人数较多,请稍后再试!(没有获取到令牌,时间:"+LocalDateTime.now().format(dtf)+ ")"; } return "请求成功,获取令牌成功,时间:"+LocalDateTime.now().format(dtf); } }
到此为止,我们的服务端代码就完成了。
四、使用okhttp编写多线程请求
这里由于需要模拟高并发,因此我们在这里编写一个多线程来请求服务端,使用的框架是okhttp,代码如下:
package com.okhttp; import java.io.IOException; import java.util.Random; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @Slf4j public class TokenTask implements Runnable { private OkHttpClient client = new OkHttpClient(); private static final Random Random = new Random(); @Override public void run() { for (int i = 0; i < 3; i++) { try { TimeUnit.MILLISECONDS.sleep(Random.nextInt(200)); httpGetTest(); } catch (Exception e) { log.error(e.getMessage(),e); } } } private void httpGetTest() { try { Request request = new Request.Builder().url("http://localhost:9999/limit/testLimiter").build(); Response response = client.newCall(request).execute(); log.info(response.body().string()); } catch (Exception e) { log.error(e.getMessage(), e); } } }
准备测试:
package com.okhttp; public class OkHttpTest { public static void main(String[] args) throws Exception { for (int i = 0; i < 20; i++) { new Thread(new TokenTask()).start(); } } }
五、开始测试
这里我们把服务端先启动起来,然后启动下客户端,就可以看到客户端打印出来的日志了。
从日志可以看出,有的线程是没有抢到令牌的,因此直接返回了错误信息,抢到令牌的,则请求成功了。
以上就是一个完整的使用guava框架,在单个接口上实现令牌筒算法的限流案例。
最后按照惯例,附上本案例的源码,登录后即可下载。
还没有评论,来说两句吧...