如何防止接口重复请求

本文介绍了解决接口重复请求导致的数据异常问题。通过两种方案:一是使用数据库中间表结合唯一键约束,二是使用Redis的setnx命令实现分布式锁,确保同一业务只被单一请求处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何防止接口重复请求

问题

最近遇到一个接口重复请求导致数据异常的bug,场景是这样的: 在一个通知模块,老师发送通知给家长,家长点开通知后客户端需要调用一个ack接口告诉服务器该家长已经读取该通知,并在notify_read表中插入该家长的信息,内容大概是(notify_id, member_id),并把通知表notify的read_num字段+1。当遇到网络问题时,客户端会多次请求该接口。

int count = queryRead(notifyId, memberId);
if( count == 0){
    insertNotify(notifyId, memberId);
    incrementNotifyReadNum(notify, memberId);
}
复制代码

当出现并发请求时,可能一个请求执行到insertNotify时,另外一个请求也刚好执行到insertNotify。导致incrementNotifyReadNum方法被执行两次,数据就出错了。

解决方案

由于我们的服务是分布式的,所以在解决问题的时候需要考虑两个请求落在不同的两台服务器的情况。 当时想到存在两种方案解决这个问题。

使用数据库

新建一张中间表

CREATE TABLE request(rkey varchar(32)PRIMARY KEY (`rkey`))
复制代码

每次请求在进行业务处理前都往该表插入一条记录,请求完成后删除该记录。

try{
    insertRequest(String.format("NOTIFY_%s_%s", notify, memberId))

    int count = queryRead(notifyId, memberId);
    if( count == 0){
        insertNotify(notifyId, memberId);
        incrementNotifyReadNum(notify, memberId);
    }
}catch(Exception e){
    //doSomething();
}finally{
 deleteRequest(String.format("NOTIFY_%s_%s", notify, memberId));
}

复制代码

通过使用数据库中的唯一键保证在同一时刻只有一个请求可以正常插入request表,插入失败的请求将不做业务处理。方案很简单,但如果请求量比较大的话会损耗DB的性能且不容易扩展。

使用Redis的counter

使用Redis跟使用数据库的实现流程其实是差不多。每次请求时,对key进行setnx操作,setnx操作返回1时,表明只有当前请求在处理这项业务;当setnx操作返回等于0时,表明还有其他请求在处理这项业务。

**SETNX key value **

将 key 的值设为 value ,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作。 SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。 时间复杂度: O(1) 返回值: 设置成功,返回 1 。 设置失败,返回 0 。

String key = String.format("NOTIFY_%s_%s", notify, memberId);
try{
    int requestCount =  redisServer.setnx(key);
    
    if(requestCount == 1){
        int count = queryRead(notifyId, memberId);
        if( count == 0){
            insertNotify(notifyId, memberId);
            incrementNotifyReadNum(notify, memberId);
        }
    }
    
}catch(Exception e){
    //doSomething();
}finally{
    //记得释放
    redisServer.delete(key);
}
复制代码

Redis的setnx命令常用来实现分布式锁,这种做法就是利用了这一特性来实现防止接口重复请求的。相对于数据库的实现,Redis的实现性能更好且更容易扩展。

转载于:https://juejin.im/post/5b1e336d51882513bb1c637a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值