You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何通过Retrofit2限制高频重复请求以保护后端

实现方案

核心通过OkHttp自定义拦截器实现限流逻辑即可,Retrofit2底层默认依赖OkHttp发起请求,该方案侵入性极低,不需要修改现有业务层的接口声明和调用代码,适合过渡阶段快速上线。

步骤1:定义相同请求的判定规则

首先需要生成请求的唯一标识,判断两个请求是否属于重复调用,一般从以下维度提取即可:

  • 请求的HTTP方法(当前场景仅需要限制@GET接口)
  • 请求的完整URL(含所有Query参数)
  • 若需要区分不同用户的调用,可额外拼接用户身份标识(比如uid、用户专属的请求header)

步骤2:实现限流拦截器

以下是Kotlin版本的简单固定窗口限流实现,每秒最多放行3次相同请求,阈值可按需调整:

import okhttp3.Interceptor
import okhttp3.Response
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit

class RequestRateLimitInterceptor : Interceptor {
    // 存储每个请求的访问记录:<请求唯一标识, 窗口内的访问时间戳列表>
    private val requestRecord = ConcurrentHashMap<String, MutableList<Long>>()
    // 限流窗口长度:1秒
    private val windowSizeMs = TimeUnit.SECONDS.toMillis(1)
    // 窗口内最大允许请求数,按需调整为1-5范围内即可
    private val maxRequestPerWindow = 3

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        // 非GET请求直接放行,不需要限流
        if (request.method != "GET") {
            return chain.proceed(request)
        }
        // 生成请求唯一标识
        val requestKey = "${request.method}:${request.url}"
        // 如需区分用户可额外拼接uid:val requestKey = "${request.method}:${request.url}:$currentUid"

        val currentTime = System.currentTimeMillis()
        val records = requestRecord.getOrPut(requestKey) { mutableListOf() }
        // 清理窗口外的过期记录
        records.removeIf { it < currentTime - windowSizeMs }

        return if (records.size >= maxRequestPerWindow) {
            // 超过限流阈值直接构造本地响应返回,不会把请求发到后端
            Response.Builder()
                .request(request)
                .protocol(okhttp3.Protocol.HTTP_1_1)
                .code(429) // 标准请求过多状态码
                .message("Too Many Requests")
                .body(okhttp3.ResponseBody.create(null, ""))
                .build()
        } else {
            records.add(currentTime)
            chain.proceed(request)
        }
    }
}

Java项目可按照相同逻辑做语法适配即可。

步骤3:配置到Retrofit实例

// 构造OkHttpClient时添加限流拦截器
val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(RequestRateLimitInterceptor())
    // 保留原有其他配置:超时、日志拦截器等
    .build()

// 给Retrofit设置带限流拦截器的OkHttpClient
val retrofit = Retrofit.Builder()
    .baseUrl(YOUR_BASE_URL)
    .client(okHttpClient)
    // 保留原有其他配置:转换器、CallAdapter等
    .build()
补充说明
  • 若存在不需要限流的GET接口,可以在拦截器中加路径白名单,匹配到对应URL直接放行即可。
  • 拦截器返回的429响应可以在上层全局异常处理中统一吞掉,避免用户感知到报错。
  • 若担心请求记录占内存,可以加定时任务清理长期没有访问的key,或者用LRU缓存限制最多存储的key数量。

内容的提问来源于stack exchange,提问作者MaaAn13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 02:09:05