Real implementations almost never run a background timer that adds tokens. Instead they store two values per client: the current token count and the timestamp of the last update. When a request arrives, the code computes how much time has passed, multiplies by the refill rate, adds that many tokens up to the capacity ceiling, and only then checks whether a token can be spent. This lazy refill makes each check O(1) and avoids one timer per client.
Concretely the math is: tokens = min(capacity, tokens + (now - last_refill) * rate); last_refill = now. If tokens is at least the request cost, subtract the cost and allow it; otherwise reject. Storing just a counter and a timestamp means the whole limiter for one key fits in a few bytes, which is why it scales to millions of keys in Redis.
In a distributed system the state lives in a shared store so all servers see the same count. Redis is the common choice, with the read-modify-write done atomically inside a Lua script so two concurrent requests cannot both spend the last token.