@@ -124,20 +124,74 @@ Configuration
124
124
The following example creates two different rate limiters for an API service, to
125
125
enforce different levels of service (free or paid):
126
126
127
- .. code-block :: yaml
128
-
129
- # config/packages/rate_limiter.yaml
130
- framework :
131
- rate_limiter :
132
- anonymous_api :
133
- # use 'sliding_window' if you prefer that policy
134
- policy : ' fixed_window'
135
- limit : 100
136
- interval : ' 60 minutes'
137
- authenticated_api :
138
- policy : ' token_bucket'
139
- limit : 5000
140
- rate : { interval: '15 minutes', amount: 500 }
127
+ .. configuration-block ::
128
+
129
+ .. code-block :: yaml
130
+
131
+ # config/packages/rate_limiter.yaml
132
+ framework :
133
+ rate_limiter :
134
+ anonymous_api :
135
+ # use 'sliding_window' if you prefer that policy
136
+ policy : ' fixed_window'
137
+ limit : 100
138
+ interval : ' 60 minutes'
139
+ authenticated_api :
140
+ policy : ' token_bucket'
141
+ limit : 5000
142
+ rate : { interval: '15 minutes', amount: 500 }
143
+
144
+ .. code-block :: xml
145
+
146
+ <!-- config/packages/rate_limiter.xml -->
147
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
148
+ <container xmlns =" http://symfony.com/schema/dic/services"
149
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
150
+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
151
+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
152
+ https://symfony.com/schema/dic/services/services-1.0.xsd
153
+ http://symfony.com/schema/dic/symfony
154
+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
155
+
156
+ <framework : config >
157
+ <framework : rate-limiter >
158
+ <!-- policy: use 'sliding_window' if you prefer that policy -->
159
+ <framework : limiter name =" anonymous_api"
160
+ policy =" fixed_window"
161
+ limit =" 100"
162
+ interval =" 60 minutes"
163
+ />
164
+
165
+ <framework : limiter name =" authenticated_api"
166
+ policy =" token_bucket"
167
+ limit =" 5000"
168
+ >
169
+ <framework : rate interval =" 15 minutes"
170
+ amount =" 500"
171
+ />
172
+ </framework : limiter >
173
+ </framework : rate-limiter >
174
+ </framework : config >
175
+ </container >
176
+
177
+ .. code-block :: php
178
+
179
+ // config/packages/rate_limiter.php
180
+ $container->loadFromExtension('framework', [
181
+ rate_limiter' => [
182
+ 'anonymous_api' => [
183
+ // use 'sliding_window' if you prefer that policy
184
+ 'policy' => 'fixed_window',
185
+ 'limit' => 100,
186
+ 'interval' => '60 minutes',
187
+ ],
188
+ 'authenticated_api' => [
189
+ 'policy' => 'token_bucket',
190
+ 'limit' => 5000,
191
+ 'rate' => [ 'interval' => '15 minutes', 'amount' => 500 ],
192
+ ],
193
+ ],
194
+ ]);
141
195
142
196
.. note ::
143
197
@@ -302,27 +356,146 @@ the :class:`Symfony\\Component\\RateLimiter\\Reservation` object returned by the
302
356
}
303
357
}
304
358
305
- Rate Limiter Storage and Locking
306
- --------------------------------
307
-
308
- Rate limiters use the default cache and locking mechanisms defined in your
309
- Symfony application. If you prefer to change that, use the ``lock_factory `` and
310
- ``storage_service `` options:
311
-
312
- .. code-block :: yaml
313
-
314
- # config/packages/rate_limiter.yaml
315
- framework :
316
- rate_limiter :
317
- anonymous_api_limiter :
318
- # ...
319
- # the value is the name of any cache pool defined in your application
320
- cache_pool : ' app.redis_cache'
321
- # or define a service implementing StorageInterface to use a different
322
- # mechanism to store the limiter information
323
- storage_service : ' App\RateLimiter\CustomRedisStorage'
324
- # the value is the name of any lock defined in your application
325
- lock_factory : ' app.rate_limiter_lock'
359
+ Storing Rate Limiter State: Caching
360
+ -----------------------------------
361
+
362
+ All rate limiter policies require to store the state of the rate limiter
363
+ (e.g. how many hits were already made in the current time window). This
364
+ state is stored by default using the :doc: `Cache component </cache >`.
365
+
366
+ The default cache pool used by all limiters is ``cache.rate_limiter ``. You
367
+ can modify this cache pool by :ref: `defining a "rate_limiter" pool <cache-create-pools >`.
368
+
369
+ You can also override the pool for a specific limiter using the ``cache_pool ``
370
+ option:
371
+
372
+ .. configuration-block ::
373
+
374
+ .. code-block :: yaml
375
+
376
+ # config/packages/rate_limiter.yaml
377
+ framework :
378
+ rate_limiter :
379
+ anonymous_api :
380
+ # ...
381
+
382
+ # use the "cache.anonymous_rate_limiter" cache pool
383
+ cache_pool : ' cache.anonymous_rate_limiter'
384
+
385
+ .. code-block :: xml
386
+
387
+ <!-- config/packages/rate_limiter.xml -->
388
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
389
+ <container xmlns =" http://symfony.com/schema/dic/services"
390
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
391
+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
392
+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
393
+ https://symfony.com/schema/dic/services/services-1.0.xsd
394
+ http://symfony.com/schema/dic/symfony
395
+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
396
+
397
+ <framework : config >
398
+ <framework : rate-limiter >
399
+ <!-- cache-pool: use the "cache.anonymous_rate_limiter" cache pool -->
400
+ <framework : limiter name =" anonymous_api"
401
+ policy =" fixed_window"
402
+ limit =" 100"
403
+ interval =" 60 minutes"
404
+ cache-pool =" cache.anonymous_rate_limiter"
405
+ />
406
+
407
+ <!-- ... ->
408
+ </framework:rate-limiter>
409
+ </framework:config>
410
+ </container>
411
+
412
+ .. code-block:: php
413
+
414
+ // config/packages/rate_limiter.php
415
+ $container->loadFromExtension('framework', [
416
+ rate_limiter' => [
417
+ 'anonymous_api' => [
418
+ // ...
419
+
420
+ // use the "cache.anonymous_rate_limiter" cache pool
421
+ 'cache_pool' => 'cache.anonymous_rate_limiter',
422
+ ],
423
+ ],
424
+ ]);
425
+
426
+ .. note::
427
+
428
+ Instead of using the Cache component, you can also implement a custom
429
+ storage. Create a PHP class that implements the
430
+ :class:`Symfony\\Component\\RateLimiter\\Storage\\StorageInterface` and
431
+ set the ``storage_service`` setting of each limiter to the service ID
432
+ of this class.
433
+
434
+ Using Locks to Prevent Race Conditions
435
+ --------------------------------------
436
+
437
+ Rate limiting can be affected by race conditions, if the same limiter is
438
+ applied to simultaneous requests (e.g. 3 servers of the same client call
439
+ the same API). To prevent these race conditions, the rate limiting
440
+ operations are protected using :doc:`locks </lock>`.
441
+
442
+ By default, the global lock (configured by ``framework.lock``) is used. You
443
+ can use a specific :ref:`named lock <lock-named-locks>` via the
444
+ ``lock_factory`` option:
445
+
446
+ .. configuration-block::
447
+
448
+ .. code-block:: yaml
449
+
450
+ # config/packages/rate_limiter.yaml
451
+ framework:
452
+ rate_limiter:
453
+ anonymous_api:
454
+ # ...
455
+
456
+ # use the "lock.rate_limiter.factory" for this limiter
457
+ lock_factory: 'lock.rate_limiter.factory'
458
+
459
+ .. code-block:: xml
460
+
461
+ <!-- config/packages/rate_limiter.xml -->
462
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
463
+ <container xmlns =" http://symfony.com/schema/dic/services"
464
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
465
+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
466
+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
467
+ https://symfony.com/schema/dic/services/services-1.0.xsd
468
+ http://symfony.com/schema/dic/symfony
469
+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
470
+
471
+ <framework : config >
472
+ <framework : rate-limiter >
473
+ <!-- limiter-factory: use the "lock.rate_limiter.factory" for this limiter -->
474
+ <framework : limiter name =" anonymous_api"
475
+ policy =" fixed_window"
476
+ limit =" 100"
477
+ interval =" 60 minutes"
478
+ lock-factory =" lock.rate_limiter.factory"
479
+ />
480
+
481
+ <!-- ... -->
482
+ </framework : rate-limiter >
483
+ </framework : config >
484
+ </container >
485
+
486
+ .. code-block :: php
487
+
488
+ // config/packages/rate_limiter.php
489
+ $container->loadFromExtension('framework', [
490
+ rate_limiter' => [
491
+ 'anonymous_api' => [
492
+ // ...
493
+
494
+ // use the "lock.rate_limiter.factory" for this limiter
495
+ 'lock_factory' => 'lock.rate_limiter.factory',
496
+ ],
497
+ ],
498
+ ]);
326
499
327
500
.. _`DoS attacks` : https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html
328
501
.. _`Apache mod_ratelimit` : https://httpd.apache.org/docs/current/mod/mod_ratelimit.html
0 commit comments