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
相关产品推荐
相关产品推荐

