Backend Development 5 min read

Implementing Dynamic Rate Limiting with Caffeine Cache in Java

This article explains how to build a high‑performance, dynamically configurable rate‑limiting utility for Java applications using Caffeine cache with a 1‑second refresh interval, covering data structures, limit‑checking logic, dynamic configuration, and sample code with test results.

FunTester
FunTester
FunTester
Implementing Dynamic Rate Limiting with Caffeine Cache in Java

Previously a simple rate‑limiting solution was built using JDK's Map, but it lacked a clean asynchronous reset mechanism. This article introduces a high‑performance local cache Caffeine to handle dynamic rate limiting based on TPS, using a 1‑second refresh interval.

The design uses two data structures: a Map storing each request's TPS configuration and a Caffeine cache holding an AtomicInteger counter for each request identifier.

Data Structure Selection : Map for configuration and Caffeine cache for counters.

Rate‑limit Decision Logic : Retrieve the counter from the cache, compare it with the configured TPS, return true if the limit is exceeded, otherwise increment the counter and return false.

Dynamic Configuration : Add or update request identifiers and their TPS values at runtime.

Using Caffeine Cache : Configure the cache to expire entries after 1 second, ensuring counters are refreshed automatically.

Implementation code:

import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.LoadingCache

import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger

/** Rate‑limit tool based on Caffeine, supports dynamic configuration by TPS */
class TpsLimit {
    Map
qpsConfig = [:]

    LoadingCache
build = Caffeine.newBuilder()
        .refreshAfterWrite(1, TimeUnit.SECONDS)
        .build(key -> new AtomicInteger())

    /** Whether to limit */
    boolean isLimit(String key) {
        AtomicInteger atomicInteger = build.get(key)
        if (atomicInteger.get() >= qpsConfig.get(key, 1)) {
            return true
        }
        atomicInteger.incrementAndGet()
        return false
    }

    /** Add rate‑limit configuration */
    def addConfig(String key, Integer qps) {
        qpsConfig.put(key, qps)
    }
}

Test script demonstrates a 1 TPS configuration, repeatedly sending requests and printing “未限流” (not limited) when the limit is not reached.

import com.funtester.httpclient.FunHttp
import com.funtester.utils.TpsLimit

class Routine extends FunHttp {
    static void main(String[] args) {
        def limit = new TpsLimit()
        limit.addConfig("test", 1)
        1000.times {
            sleep(0.1)
            fun {
                def limit1 = limit.isLimit("t4est")
                if (!limit1) {
                    output("未限流")
                }
            }
        }
    }
}

Console output shows several “未限流” lines, confirming the default 1 TPS setting works as expected.

backendJavaCachecaffeineRate LimitingTPS
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.