See what makes Kong the fastest, most-adopted API gateway
Check out the latest Kong feature releases and updates
Single platform for SaaS end-to-end connectivity
Enterprise service mesh based on Kuma and Envoy
Collaborative API design platform
How to Scale High-Performance APIs and Microservices
Call for speakers & sponsors, Kong API Summit 2023!
5 MIN READ
One of the most common use cases our customers are using Kong for is rate limiting. There are a few common reasons for doing this:
For all those and more, we’re able to easily add this functionality with Kong’s rate limit advanced plugin.
The plugin can be applied at different levels such as service, route, specific consumer, or even in a global scope. By having this flexibility, one can set a generic global limit but then still overrule the global limit to allow for a more specific rate limit at a lower level.
However, after working with many of Kong customers, one use case couldn’t be met with the specificity highlighted above. This use case is how can Kong help when customers want to have different rate limits based on an organization, partner, or tenant? The answer is Kong’s feature of “Consumer Groups,” which we’ll expand on below (and is documented here).
Released on 2.7, the Kong API gateway allows you to define limits per consumer groups. This means that one can still use the general RL functionality as mentioned above, but also add specific limits to certain groups. Let’s see how we can make it work.
Add a service
➜ demo-environment git:(main) ✗ http post localhost:8001/services name=prod-backend url=http://httpbin.org/anything HTTP/1.1 201 Created { "ca_certificates": null, "client_certificate": null, "connect_timeout": 60000, "created_at": 1655216213, "enabled": true, "host": "httpbin.org", "id": "9c77f727-42d2-4f75-b194-9b6dd9fcfe5b", "name": "prod-backend", "path": "/anything", "port": 80, "protocol": "http", "read_timeout": 60000, "retries": 5, "tags": null, "tls_verify": null, "tls_verify_depth": null, "updated_at": 1655216213, "write_timeout": 60000 }
Add a route
➜ demo-environment git:(main) ✗ http post localhost:8001/services/prod-backend/routes name=prod-backend-route paths:='["/requests"]' HTTP/1.1 201 Created { "created_at": 1655216505, "destinations": null, "headers": null, "hosts": null, "https_redirect_status_code": 426, "id": "8d1c42ed-0fe6-4774-a4df-36a790568796", "methods": null, "name": "prod-backend-route", "path_handling": "v0", "paths": [ "/requests" ], "preserve_host": false, "protocols": [ "http", "https" ], "regex_priority": 0, "request_buffering": true, "response_buffering": true, "service": { "id": "9c77f727-42d2-4f75-b194-9b6dd9fcfe5b" }, "snis": null, "sources": null, "strip_path": true, "tags": null, "updated_at": 1655216505 }
Add Advanced Rate Limiting plugin on the service level, and enforce it to work with 2 consumer groups.
➜ demo-environment git:(main) ✗ http post localhost:8001/services/prod-backend/plugins name=rate-limiting-advanced config:='{"sync_rate": 0, "window_size": [60], "limit": [10], "enforce_consumer_groups":true, "consumer_groups": ["hr","marketing"]}' HTTP/1.1 201 Created { "config": { "consumer_groups": [ "hr", "marketing" ], "dictionary_name": "kong_rate_limiting_counters", "enforce_consumer_groups": true, "header_name": null, "hide_client_headers": false, "identifier": "consumer", "limit": [ 10 ], "namespace": "vCcg7OpfB1rGxRrCaJ3vEytelRyqUfyZ", "path": null, "redis": { "cluster_addresses": null, "connect_timeout": null, "database": 0, "host": null, "keepalive_backlog": null, "keepalive_pool_size": 30, "password": null, "port": null, "read_timeout": null, "send_timeout": null, "sentinel_addresses": null, "sentinel_master": null, "sentinel_password": null, "sentinel_role": null, "sentinel_username": null, "server_name": null, "ssl": false, "ssl_verify": false, "timeout": 2000, "username": null }, "retry_after_jitter_max": 0, "strategy": "cluster", "sync_rate": 0, "window_size": [ 60 ], "window_type": "sliding" }, "consumer": null, "created_at": 1655216600, "enabled": true, "id": "cf9ade32-7be9-49c4-9414-1e2e25a4c74e", "name": "rate-limiting-advanced", "protocols": [ "grpc", "grpcs", "http", "https" ], "route": null, "service": { "id": "9c77f727-42d2-4f75-b194-9b6dd9fcfe5b" }, "tags": null }
Add key authentication plugin for our consumers
➜ demo-environment git:(main) ✗ http post localhost:8001/routes/prod-backend-route/plugins name=key-auth config.key_names=apikey config.key_in_body=false config.key_in_header=true config.key_in_query=true config.hide_credentials=false config.run_on_preflight=true -f HTTP/1.1 201 Created { "config": { "anonymous": null, "hide_credentials": false, "key_in_body": false, "key_in_header": true, "key_in_query": true, "key_names": [ "apikey" ], "run_on_preflight": true }, "consumer": null, "created_at": 1655216690, "enabled": true, "id": "6faf92ba-3d1e-4333-b603-b1dcd09a7767", "name": "key-auth", "protocols": [ "grpc", "grpcs", "http", "https" ], "route": { "id": "8d1c42ed-0fe6-4774-a4df-36a790568796" }, "service": null, "tags": null }
Add 3 consumers
➜ demo-environment git:(main) ✗ http post localhost:8001/consumers username=ann custom_id=ann HTTP/1.1 201 Created { "created_at": 1655216804, "custom_id": "ann", "id": "62a744e3-eee6-48c9-942c-6660fe5c3414", "tags": null, "type": 0, "username": "ann", "username_lower": "ann" } ➜ demo-environment git:(main) ✗ http post localhost:8001/consumers username=james custom_id=james HTTP/1.1 201 Created { "created_at": 1655216859, "custom_id": "james", "id": "1cb68a87-841d-4412-b902-41f74539544d", "tags": null, "type": 0, "username": "james", "username_lower": "james" } ➜ demo-environment git:(main) ✗ http post localhost:8001/consumers username=sarah custom_id=sarah HTTP/1.1 201 Created { "created_at": 1655216941, "custom_id": "sarah", "id": "ca40abfd-de21-412b-94a0-14ccced9640d", "tags": null, "type": 0, "username": "sarah", "username_lower": "sarah" }
Create Keys for the users for authentication
➜ demo-environment git:(main) ✗ http post localhost:8001/consumers/ann/key-auth HTTP/1.1 201 Created { "consumer": { "id": "62a744e3-eee6-48c9-942c-6660fe5c3414" }, "created_at": 1655216979, "id": "e1935b1e-6c9f-4f2d-af5e-25494f26854c", "key": "1kGHqaKRlFPuTh5T1GOyyNo4iG3Fsvfj", "tags": null, "ttl": null } ➜ demo-environment git:(main) ✗ http post localhost:8001/consumers/james/key-auth HTTP/1.1 201 Created { "consumer": { "id": "1cb68a87-841d-4412-b902-41f74539544d" }, "created_at": 1655217028, "id": "cf7ccc1d-d851-4327-adf0-bac04c724b4a", "key": "OFeajtvle099Dn1chzGLbeyDW8BlFHA0", "tags": null, "ttl": null } ➜ demo-environment git:(main) ✗ http post localhost:8001/consumers/sarah/key-auth HTTP/1.1 201 Created { "consumer": { "id": "ca40abfd-de21-412b-94a0-14ccced9640d" }, "created_at": 1655217068, "id": "40118577-c706-4ec9-85da-2f5f7bb84ea5", "key": "skIZ9Imx0fpJsAH36tP9PDpAELotbuuM", "tags": null, "ttl": null }
Add 2 consumer groups — we will assign different users to different groups later on to test our functionality
➜ demo-environment git:(main) ✗ http post localhost:8001/consumer_groups name=hr HTTP/1.1 201 Created { "created_at": 1655217132, "id": "e61cc03c-faf3-4898-a4b5-863790c4eea0", "name": "hr" } ➜ demo-environment git:(main) ✗ http post localhost:8001/consumer_groups name=marketing HTTP/1.1 201 Created { "created_at": 1655217158, "id": "b78ae896-7afa-4fd4-9b5e-f1486345e803", "name": "marketing" }
Let’s see that we are able to access our service and actually get limited requests regardless of the user. We will call our service with Ann and Sarah:
➜ demo-environment git:(main) ✗ http :8000/requests apikey:1kGHqaKRlFPuTh5T1GOyyNo4iG3Fsvfj HTTP/1.1 200 OK …… RateLimit-Limit: 10 RateLimit-Remaining: 9 ……. ➜ demo-environment git:(main) ✗ http :8000/requests apikey:skIZ9Imx0fpJsAH36tP9PDpAELotbuuM HTTP/1.1 200 OK ……. RateLimit-Limit: 10 RateLimit-Remaining: 9 ……
As we can see, both users are limited to 10 calls per minute.
Next phase is to Link between consumer groups and consumers:
➜ demo-environment git:(main) ✗ http POST :8001/consumers/ann/consumer_groups group=hr HTTP/1.1 201 Created { "consumer": { "created_at": 1655216804, "custom_id": "ann", "id": "62a744e3-eee6-48c9-942c-6660fe5c3414", "tags": null, "type": 0, "username": "ann", "username_lower": "ann" }, "consumer_groups": [ { "created_at": 1655217132, "id": "e61cc03c-faf3-4898-a4b5-863790c4eea0", "name": "hr" } ] } ➜ demo-environment git:(main) ✗ http POST :8001/consumers/james/consumer_groups group=marketing HTTP/1.1 201 Created { "consumer": { "created_at": 1655216859, "custom_id": "james", "id": "1cb68a87-841d-4412-b902-41f74539544d", "tags": null, "type": 0, "username": "james", "username_lower": "james" }, "consumer_groups": [ { "created_at": 1655217158, "id": "b78ae896-7afa-4fd4-9b5e-f1486345e803", "name": "marketing" } ] } ➜ demo-environment git:(main) ✗ http POST :8001/consumers/sarah/consumer_groups group=marketing HTTP/1.1 201 Created { "consumer": { "created_at": 1655216941, "custom_id": "sarah", "id": "ca40abfd-de21-412b-94a0-14ccced9640d", "tags": null, "type": 0, "username": "sarah", "username_lower": "sarah" }, "consumer_groups": [ { "created_at": 1655217158, "id": "b78ae896-7afa-4fd4-9b5e-f1486345e803", "name": "marketing" } ] }
Now, let’s change the consumer group functionality and assign different rates to different groups. We will set up the limit of 2000 requests per minute for “hr” group and 1000 requests per minute for “marketing”:
➜ demo-environment git:(main) ✗ http put :8001/consumer_groups/hr/overrides/plugins/rate-limiting-advanced config:='{"limit": [2000], "window_size": [60], "window_type": "sliding"}' HTTP/1.1 201 Created { "config": { "limit": [ 2000 ], "window_size": [ 60 ], "window_type": "sliding" }, "consumer_group": "hr", "plugin": "rate-limiting-advanced" } ➜ demo-environment git:(main) ✗ http put :8001/consumer_groups/marketing/overrides/plugins/rate-limiting-advanced config:='{"limit": [1000], "window_size": [60], "window_type": "sliding"}' HTTP/1.1 201 Created { "config": { "limit": [ 1000 ], "window_size": [ 60 ], "window_type": "sliding" }, "consumer_group": "marketing", "plugin": "rate-limiting-advanced" }
Now, lets see what we get when we test with Ann:
➜ demo-environment git:(main) ✗ http :8000/requests apikey:1kGHqaKRlFPuTh5T1GOyyNo4iG3Fsvfj HTTP/1.1 200 OK …. RateLimit-Limit: 2000 RateLimit-Remaining: 1999 ……
As you can see, Ann now has a limit of 2000 RPM, while the plugin general config is 10. One can also test this with John and Sarah and confirm that the grouping is indeed working:
➜ demo-environment git:(main) ✗ http :8000/requests apikey:OFeajtvle099Dn1chzGLbeyDW8BlFHA0 HTTP/1.1 200 OK …. RateLimit-Limit: 1000 RateLimit-Remaining: 999 …. ➜ demo-environment git:(main) ✗http :8000/requests apikey:skIZ9Imx0fpJsAH36tP9PDpAELotbuuM HTTP/1.1 200 OK ….. RateLimit-Limit: 1000 RateLimit-Remaining: 999 ……
As seen above, both John and Sarah have a limit of 1000 RPM as we wanted.
As we can see, it is very easy to configure Kong to rate limit your traffic with the relevant requirement for your use case — be it security, performance, or business use case.
Share Post
Learn how to make your API strategy a competitive advantage.