@@ -7,19 +7,43 @@ Rate Limiter
7
7
:doc: `experimental feature </contributing/code/experimental >`.
8
8
9
9
A "rate limiter" controls how frequently some event (e.g. an HTTP request or a
10
- login attempt) is allowed to happen. It implements a `token bucket algorithm `_,
11
- which roughly works like this:
10
+ login attempt) is allowed to happen. Rate limiting is commonly used as a
11
+ defensive measure to protect services from excessive use (intended or not) and
12
+ maintain their availability.
13
+
14
+ Symfony uses these rate limiters in built-in features like "login throttling",
15
+ which limits how many failed login attempts a user can make in a given period of
16
+ time, but you can use them for your own features too.
17
+
18
+ Rate Limiting Strategies
19
+ ------------------------
20
+
21
+ Symfony's rate limiter implements two of the most common strategies to enforce
22
+ rate limits: **fixed window ** and **token bucket **.
23
+
24
+ Fixed Window Rate Limiter
25
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
26
+
27
+ This is the simplest technique and it's based on setting a limit for a given
28
+ interval of time. For example: 5,000 requests per hour or 3 login attempts
29
+ every 15 minutes.
30
+
31
+ Its main drawback is that resource usage is not evenly distributed in time. In
32
+ the previous example, a user could make the 5,000 requests in the first minute
33
+ (possibly overloading the server) and do nothing for the rest of the hour.
34
+
35
+ Token Bucket Rate Limiter
36
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
37
+
38
+ This technique implements the `token bucket algorithm `_, which defines a
39
+ continuously updating budget of resource usage. It roughly works like this:
12
40
13
41
* A bucket is initially created with zero or more tokens;
14
42
* A new token is added to the bucket with a predefined frequency (e.g. every second);
15
43
* Allowing an event consumes one or more tokens;
16
44
* If the bucket still contains tokens, the event is allowed; otherwise, it's denied;
17
45
* If the bucket is at full capacity, new tokens are discarded.
18
46
19
- Symfony uses these rate limiters in built-in features like "login throttling",
20
- which limits how many failed login attempts a user can make in a given period of
21
- time, but you can use them for your own features too.
22
-
23
47
Installation
24
48
------------
25
49
@@ -33,39 +57,32 @@ install the associated Symfony Component in your application:
33
57
Configuration
34
58
-------------
35
59
36
- Use the following options to configure the behavior of the rate limiter:
60
+ The following example creates two different rate limiters for an API service, to
61
+ enforce different levels of service (free or paid):
37
62
38
63
.. code-block :: yaml
39
64
40
65
# config/packages/rate_limiter.yaml
41
66
framework :
42
67
rate_limiter :
43
- limiters :
44
- api_requests :
45
- # maximum number of tokens allowed in the bucket
46
- limit : 100
47
- # how many tokens are created and how often
48
- rate :
49
- amount : 100
50
- interval : ' 1 hour'
51
-
52
- This example creates a rate limiter to restrict the number of HTTP requests of
53
- some API to 100 per hour.
54
-
55
- There are other options that you can optionally define if needed:
56
-
57
- .. code-block :: yaml
58
-
59
- # config/packages/rate_limiter.yaml
60
- framework :
61
- rate_limiter :
62
- limiters :
63
- api_requests :
64
- # ...
65
- lock :
66
- storage :
67
- strategy :
68
- interval :
68
+ anonymous_api_limiter :
69
+ strategy : fixed_window
70
+ limit : 100
71
+ interval : 60m
72
+ authenticated_api_limiter :
73
+ strategy : token_bucket
74
+ limit : 5000
75
+ rate : { interval: 1h, amount: 5000 }
76
+
77
+ In the ``anonymous_api_limiter ``, when you make the first HTTP request, you can
78
+ make up to 100 requests in the next 60 minutes. After that time, the counter
79
+ resets and you have another 100 requests for the following 60 minutes.
80
+
81
+ In the ``authenticated_api_limiter ``, when you make the first HTTP request you
82
+ are allowed to make up to 5,000 HTTP requests in total, and this number grows
83
+ at a rate of another 5,000 requests per hour. If you don't make that number of
84
+ requests, the unused ones don't accumulate (the ``limit `` option prevents that
85
+ number from being higher than 5,000).
69
86
70
87
Rate Limiting in Action
71
88
-----------------------
@@ -84,11 +101,12 @@ the number of requests to the API::
84
101
85
102
class ApiController extends AbstractController
86
103
{
87
- public function index(LimiterInterface $limiter)
104
+ // the variable name must be the exact same as the rate limiter name
105
+ public function index(LimiterInterface $anonymous_api_limiter)
88
106
{
89
107
// the argument of consume() is the number of tokens to consume
90
108
// and returns FALSE if they cannot be consumed
91
- if (false === $limiter ->consume(1)) {
109
+ if (false === $anonymous_api_limiter ->consume(1)) {
92
110
throw new TooManyRequestsHttpException();
93
111
}
94
112
@@ -113,53 +131,34 @@ token is available. In those cases, use the ``reserve()`` and ``wait()`` methods
113
131
114
132
class ApiController extends AbstractController
115
133
{
116
- public function registerUser(LimiterInterface $limiter )
134
+ public function registerUser(LimiterInterface $authenticated_api_limiter )
117
135
{
118
136
// this blocks the application until the given number of tokens can be consumed
119
- $limiter ->reserve(1)->wait();
137
+ $authenticated_api_limiter ->reserve(1)->wait();
120
138
121
139
// ...
122
140
}
123
141
124
142
// ...
125
143
}
126
144
127
- Creating Multiple Rate Limiters
128
- -------------------------------
145
+ Rate Limiter Storage and Locking
146
+ --------------------------------
129
147
130
- You can define any number of rate limiters in the application. For example, you
131
- could set different API request limits for anonymous and authenticated users:
148
+ Rate limiters use the default cache and locking mechanisms defined in your
149
+ Symfony application. If you prefer to change that, use the ``lock `` and
150
+ ``storage `` options:
132
151
133
152
.. code-block :: yaml
134
153
135
154
# config/packages/rate_limiter.yaml
136
155
framework :
137
156
rate_limiter :
138
- limiters :
139
- # 1 request per minute for anonymous users
140
- anonymous_api_requests :
141
- limit : 1
142
- rate :
143
- amount : 1
144
- interval : ' 1 minute'
145
- # 85 requests per minute for authenticated users
146
- authenticated_api_requests :
147
- limit : 85
148
- rate :
149
- amount : 85
150
- interval : ' 1 minute'
151
-
152
- Now you can inject each rate limiter using the same name as in the configuration::
153
-
154
- // src/Controller/LuckyController.php
155
- // ...
156
-
157
- class ApiController extends AbstractController
158
- {
159
- public function index(LimiterInterface $authenticated_api_requests)
160
- {
161
- // ...
162
- }
163
- }
157
+ anonymous_api_limiter :
158
+ # ...
159
+ # the value is the name of any cache pool defined in your application
160
+ storage : ' app.redis_cache'
161
+ # the value is the name of any lock defined in your application
162
+ lock : ' app.rate_limiter_lock'
164
163
165
164
.. _`token bucket algorithm` : https://en.wikipedia.org/wiki/Token_bucket
0 commit comments