@@ -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
@@ -300,27 +354,146 @@ the :class:`Symfony\\Component\\RateLimiter\\Reservation` object returned by the
300
354
}
301
355
}
302
356
303
- Rate Limiter Storage and Locking
304
- --------------------------------
305
-
306
- Rate limiters use the default cache and locking mechanisms defined in your
307
- Symfony application. If you prefer to change that, use the ``lock_factory `` and
308
- ``storage_service `` options:
309
-
310
- .. code-block :: yaml
311
-
312
- # config/packages/rate_limiter.yaml
313
- framework :
314
- rate_limiter :
315
- anonymous_api_limiter :
316
- # ...
317
- # the value is the name of any cache pool defined in your application
318
- cache_pool : ' app.redis_cache'
319
- # or define a service implementing StorageInterface to use a different
320
- # mechanism to store the limiter information
321
- storage_service : ' App\RateLimiter\CustomRedisStorage'
322
- # the value is the name of any lock defined in your application
323
- lock_factory : ' app.rate_limiter_lock'
357
+ Storing Rate Limiter State: Caching
358
+ -----------------------------------
359
+
360
+ All rate limiter policies require to store the state of the rate limiter
361
+ (e.g. how many hits were already made in the current time window). This
362
+ state is stored by default using the :doc: `Cache component </cache >`.
363
+
364
+ The default cache pool used by all limiters is ``cache.rate_limiter ``. You
365
+ can modify this cache pool by :ref: `defining a "rate_limiter" pool <cache-create-pools >`.
366
+
367
+ You can also override the pool for a specific limiter using the ``cache_pool ``
368
+ option:
369
+
370
+ .. configuration-block ::
371
+
372
+ .. code-block :: yaml
373
+
374
+ # config/packages/rate_limiter.yaml
375
+ framework :
376
+ rate_limiter :
377
+ anonymous_api :
378
+ # ...
379
+
380
+ # use the "cache.anonymous_rate_limiter" cache pool
381
+ cache_pool : ' cache.anonymous_rate_limiter'
382
+
383
+ .. code-block :: xml
384
+
385
+ <!-- config/packages/rate_limiter.xml -->
386
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
387
+ <container xmlns =" http://symfony.com/schema/dic/services"
388
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
389
+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
390
+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
391
+ https://symfony.com/schema/dic/services/services-1.0.xsd
392
+ http://symfony.com/schema/dic/symfony
393
+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
394
+
395
+ <framework : config >
396
+ <framework : rate-limiter >
397
+ <!-- cache-pool: use the "cache.anonymous_rate_limiter" cache pool -->
398
+ <framework : limiter name =" anonymous_api"
399
+ policy =" fixed_window"
400
+ limit =" 100"
401
+ interval =" 60 minutes"
402
+ cache-pool =" cache.anonymous_rate_limiter"
403
+ />
404
+
405
+ <!-- ... ->
406
+ </framework:rate-limiter>
407
+ </framework:config>
408
+ </container>
409
+
410
+ .. code-block:: php
411
+
412
+ // config/packages/rate_limiter.php
413
+ $container->loadFromExtension('framework', [
414
+ rate_limiter' => [
415
+ 'anonymous_api' => [
416
+ // ...
417
+
418
+ // use the "cache.anonymous_rate_limiter" cache pool
419
+ 'cache_pool' => 'cache.anonymous_rate_limiter',
420
+ ],
421
+ ],
422
+ ]);
423
+
424
+ .. note::
425
+
426
+ Instead of using the Cache component, you can also implement a custom
427
+ storage. Create a PHP class that implements the
428
+ :class:`Symfony\\Component\\RateLimiter\\Storage\\StorageInterface` and
429
+ set the ``storage_service`` setting of each limiter to the service ID
430
+ of this class.
431
+
432
+ Using Locks to Prevent Race Conditions
433
+ --------------------------------------
434
+
435
+ Rate limiting can be affected by race conditions, if the same limiter is
436
+ applied to simultaneous requests (e.g. 3 servers of the same client call
437
+ the same API). To prevent these race conditions, the rate limiting
438
+ operations are protected using :doc:`locks </lock>`.
439
+
440
+ By default, the global lock (configured by ``framework.lock``) is used. You
441
+ can use a specific :ref:`named lock <lock-named-locks>` via the
442
+ ``lock_factory`` option:
443
+
444
+ .. configuration-block::
445
+
446
+ .. code-block:: yaml
447
+
448
+ # config/packages/rate_limiter.yaml
449
+ framework:
450
+ rate_limiter:
451
+ anonymous_api:
452
+ # ...
453
+
454
+ # use the "lock.rate_limiter.factory" for this limiter
455
+ lock_factory: 'lock.rate_limiter.factory'
456
+
457
+ .. code-block:: xml
458
+
459
+ <!-- config/packages/rate_limiter.xml -->
460
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
461
+ <container xmlns =" http://symfony.com/schema/dic/services"
462
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
463
+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
464
+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
465
+ https://symfony.com/schema/dic/services/services-1.0.xsd
466
+ http://symfony.com/schema/dic/symfony
467
+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
468
+
469
+ <framework : config >
470
+ <framework : rate-limiter >
471
+ <!-- limiter-factory: use the "lock.rate_limiter.factory" for this limiter -->
472
+ <framework : limiter name =" anonymous_api"
473
+ policy =" fixed_window"
474
+ limit =" 100"
475
+ interval =" 60 minutes"
476
+ lock-factory =" lock.rate_limiter.factory"
477
+ />
478
+
479
+ <!-- ... -->
480
+ </framework : rate-limiter >
481
+ </framework : config >
482
+ </container >
483
+
484
+ .. code-block :: php
485
+
486
+ // config/packages/rate_limiter.php
487
+ $container->loadFromExtension('framework', [
488
+ rate_limiter' => [
489
+ 'anonymous_api' => [
490
+ // ...
491
+
492
+ // use the "lock.rate_limiter.factory" for this limiter
493
+ 'lock_factory' => 'lock.rate_limiter.factory',
494
+ ],
495
+ ],
496
+ ]);
324
497
325
498
.. _`DoS attacks` : https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html
326
499
.. _`Apache mod_ratelimit` : https://httpd.apache.org/docs/current/mod/mod_ratelimit.html
0 commit comments