为Spring Boot应用程序实施速率限制器
在本教程中,我们将学习API的安全性和可用性。我们将了解Bucket4j库的效率,以及如何用它来限制Spring REST API的速率。
我们将实施一个简单的计算器项目来探索速率限制的内部运作,获得对Bucket4j库的理解,并将其用于对实际的Spring boot应用程序进行速率限制。
让我们开始吧。
前提条件
- 有过REST APIs的知识。
- 使用[Spring Boot]的知识。
- Java编程语言的基本知识。
什么是速率限制?
速率限制是一种软件工程策略,允许API基础设施的创建者和维护者控制对其API的访问。在一个特定的时间内,任何消费者可以进行的调用数量都会被检查。
通过这样做,可以防止API的滥用和不必要的使用。费率限制器也可以作为一种手段,通过控制消费者选择的计划可以进行多少次呼叫,从而将消费者置于支付计划中。
费率限制库 - Bucket4j
Bucket4j库是一个基于Java的库,使用令牌桶算法构建。这意味着它在线程上是安全的,既可以在集群环境中也可以在孤立的Java虚拟机(JVM)环境中采用。
令牌桶算法
这个简单而强大的算法背后的想法是简单明了的。想象一下,有一个桶,可以容纳x 数量的令牌。任何时候,客户希望访问一个资源或一个端点,他都必须从这个桶中获得一个令牌来实现。
我们只需拿出一个令牌,递给他,然后他就可以成功地进行请求。反之,如果没有令牌,我们就直接拒绝他的请求。
因此,请求和令牌的数量是成反比的。
在类似的情况下,考虑一个速率限制为每小时500个请求的应用程序。我们可以建立一个可以容纳500个代币的桶,并设置每小时500个的补给率。
如果我们在一小时内收到450个请求,少于500个可用代币的总数,我们将把剩余的50个代币结转到下一小时;提高桶的容量。
如果我们在45分钟内用完了所有的500个请求,我们将需要再等15分钟才能再次访问资源。
对一个应用程序进行速率限制
现在,我们将使用Bucket4j为一个小型应用实现速率限制。我们将通过建立一个 "周长计算器 "应用程序来学习。它可以简单地计算出一个形状的周长。
设置
首先,建立一个普通的spring boot项目,并在你的pom.xml 文件中添加这个依赖关系。
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>4.10.0</version>
</dependency>
然后,创建三个文件,如图所示。
- 一个名为 "请求 "的类
Dimension - 一个名为 "模型 "的类
Perimeter - 一个名为
Controller的类
@Getter
@Setter
public class Dimension{
private int length;
private int breadth;
}
上面的Dimension 类是作为与前端的数据传输对象使用的。
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Perimeter {
private String shape;
private Double perimeter;
}
Perimeter 类是实际的对象,我们在这里计算一个形状的周长。
@RestController
public class Controller {
@PostMapping(value = "/api/v1/perimeter/rectangle")
public ResponseEntity<Perimeter> rectangle(@RequestBody Dimension dimensions) {
return ResponseEntity.ok(new Perimeter("rectangle",
(double) 2 * (dimensions.getLength() + dimensions.getBreadth())));
}
}
实施
现在,让我们写出一个简单的速率限制代码。
有了这个,API应该在一分钟内只允许50个请求。因此,在一分钟内的第50次API调用后,API会拒绝该调用。
我们的控制器将被修改以反映这一变化--建立一个桶并引入带宽,如图所示。
@RestController
public class Controller {
private final Bucket bucket;
public Controller() {
Bandwidth limit = Bandwidth.classic(50, Refill.greedy(50, Duration.ofMinutes(1)));
this.bucket = Bucket4j.builder()
.addLimit(limit)
.build();
}
@PostMapping(value = "/api/v1/perimeter/rectangle")
public ResponseEntity<Perimeter> rectangle(@RequestBody Dimension dimensions) {
return ResponseEntity.ok(new Perimeter("rectangle",
(double) 2 * (dimensions.getLength() + dimensions.getBreadth())));
}
}
测试应用程序
我们可以通过向端点发送请求来测试工作情况,看看它是否利用了桶中的令牌,使用Bucket4j库的tryConsume 方法(Controller class modified)。
当我们达到极限时,调用会被拒绝,状态码和响应分别为49 – Too many requests 。
@PostMapping(value = "/api/v1/perimeter/rectangle")
public ResponseEntity<Perimeter> rectangle(@RequestBody Dimension dimensions) {
if (bucket.tryConsume(1)) {
return ResponseEntity.ok(new Perimeter("rectangle",
(double) 2 * (dimensions.getLength() + dimensions.getBreadth())));
}
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build();
}

从上面的图片中,我们看到我们的请求是成功的,有一个相应的正确响应。
现在,为了简洁起见,我将再送入50个,并显示在第51个时,该计数的API调用被拒绝,并有一个正确的拒绝响应,如图。

要再次使用资源,我们必须等到下一分钟的开始。
需要注意的几点
随着时间的推移,API调用正变得越来越流行。你一定使用过一种服务,要求你从一个计划升级到更高的计划--比如说,从免费层到基本计划。
有了速率限制,我们可以根据用户所在的计划来控制他们的API使用量。你只需写一个方法,与他们所处的计划的一些指标相匹配,并指出他们在一个时间框架内可以拨打的电话数量。
例如,一个免费计划的用户可以每小时打1000个电话,而一个基本用户可以有每小时100000个电话的带宽,以此类推。
每个客户都需要有一个独特的API Key(或某种标识符),他们必须与他们的请求一起发送,以帮助我们识别他们所属的计划。
你可以写一些方法,可以为客户生成新的API密钥,并相应地增加他们的API调用带宽。
主要收获
在本教程中,读者对限制其应用程序的速率有了更好的理解。通过教程,他们学会了如何将其添加到他们的项目中。
结论
利用本教程中的知识,开发者可以更好地保护他们的API,并通过控制对它们的点击量使它们更可用。由于它允许一些很好的控制措施,它是一个非常有用的赚钱途径。