在前面的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框架,在单个接口上实现令牌筒算法的限流案例。
最后按照惯例,附上本案例的源码,登录后即可下载。











还没有评论,来说两句吧...