React Native+Go架构下API重复调用问题:寻求即时处理替代方案
解决方案:实现API2的即时/低延迟精准调用(避免重复执行)
针对你遇到的「Google定位重复触发API1调用,导致API2重复执行」问题,以下几个方案可以在保证去重的同时,实现API2的即时或低延迟执行:
一、移动端侧提前过滤重复触发
从源头减少重复请求是最有效的方式:
- 防抖+区域确认:使用防抖函数(比如
lodash.debounce)包裹定位回调,当用户离开目标区域后,延迟1-2秒再确认位置状态,如果用户确实持续在区域外,再调用API1。同时在本地记录「已触发离开事件」的状态,短时间内(比如5分钟)不再重复发起请求。
示例(React Native):import { debounce } from 'lodash'; let hasTriggeredLeave = false; const handleLocationChange = debounce(async (location) => { if (isOutsideTargetArea(location) && !hasTriggeredLeave) { hasTriggeredLeave = true; await fetch('/api1', { method: 'POST', body: JSON.stringify({ location }) }); // 5分钟后重置状态,允许再次触发 setTimeout(() => hasTriggeredLeave = false, 5 * 60 * 1000); } }, 2000); // 2秒防抖延迟
二、API1侧实现请求幂等性
在后端层面拦截重复请求,确保同一个事件只处理一次:
- 基于唯一请求ID的幂等校验:要求移动端在调用API1时,携带一个客户端生成的唯一标识(比如UUID或
设备ID+时间戳的哈希值),放在请求头(如X-Request-ID)或请求体中。API1接收到请求后,先检查这个ID是否已经被处理过(用Redis或内存缓存存储已处理的ID,设置过期时间覆盖重复触发窗口),如果已处理直接返回成功,否则执行后续逻辑并标记该ID为已处理。
示例(Go):import "github.com/go-redis/redis/v8" import "time" func API1Handler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() reqID := r.Header.Get("X-Request-ID") if reqID == "" { http.Error(w, "missing X-Request-ID", http.StatusBadRequest) return } // 检查请求是否已处理 processed, err := redisClient.Get(ctx, "processed:"+reqID).Result() if err == nil && processed == "true" { w.WriteHeader(http.StatusOK) return } // 调用API2 callAPI2() // 标记请求已处理,设置10分钟过期 redisClient.SetEx(ctx, "processed:"+reqID, "true", 10*time.Minute) w.WriteHeader(http.StatusOK) }
三、用消息队列替代定时轮询,实现异步即时处理
将「缓存到数据库+定时轮询」的模式替换为「消息队列+即时消费」,既保留异步解耦,又能低延迟处理:
- API1接收到请求后,先做幂等校验(同方案二),然后将任务发送到消息队列(如Redis List、RabbitMQ、NATS)。
- 单独启动一个Go消费进程,持续监听队列,一旦有新任务就取出,再次校验幂等(避免队列重复投递),然后调用API2。
- 这种方式可以做到秒级甚至毫秒级的延迟,同时消息队列的ACK机制可以保证任务不丢失。
四、分布式锁控制并发执行
如果同一设备的多个API1请求可能同时到达,用分布式锁确保同一时间只有一个请求调用API2:
- API1接收到请求后,以
设备ID为锁的key,尝试获取分布式锁(比如基于Redis的Redlock),锁的有效期设置为API2执行所需的最长时间(比如30秒)。 - 如果获取到锁,就调用API2;如果获取失败,说明已有请求在处理,直接返回成功即可。
示例(Go,使用redsync):import "github.com/go-redsync/redsync/v4" import "time" func API1Handler(w http.ResponseWriter, r *http.Request) { deviceID := r.FormValue("device_id") if deviceID == "" { http.Error(w, "missing device_id", http.StatusBadRequest) return } // 创建分布式锁 mutex := rsync.NewMutex("lock:"+deviceID, redsync.New(redsync.WithPool(redisClient))) // 尝试获取锁,最多等待1秒,锁有效期30秒 if err := mutex.LockContext(r.Context(), 1*time.Second, 30*time.Second); err != nil { w.WriteHeader(http.StatusOK) return } defer mutex.Unlock() // 调用API2 callAPI2() w.WriteHeader(http.StatusOK) }
方案选型建议
- 如果移动端的重复触发是主要原因,优先选方案一+方案二,从源头和后端双重拦截。
- 如果需要异步解耦且低延迟,选方案二+方案三,兼顾去重和即时处理。
- 如果存在并发请求导致的重复执行,选方案四配合幂等校验。
内容的提问来源于stack exchange,提问作者Amandeep kaur
相关产品推荐
相关产品推荐

