前言
一些用户请求在某些情况下是可能重复发送的,如果是查询类操作并无大碍,但其中有些涉及写入操作,一旦重复了,可能会导致很严重的后果。例如交易接口如果重复请求,可能会重复下单。
重复的场景有可能是:
-
黑客拦截了请求,重放;
-
前端/客户端因为某些原因请求重复发送了,或者用户在很短的时间内重复点击了;
-
网关重发;
-
……
本文讨论的是如何在服务端优雅地统一处理这种情况,如何禁止用户重复点击等客户端操作不在本文的讨论范畴。
利用唯一请求编号去重
你可能会想到,只要请求有唯一的请求编号,那么就能借用 Redis 做去重。
只要这个唯一请求编号在 Redis 存在,证明处理过,那么就认为是重复的。
业务参数去重
上面的方案能解决具备唯一请求编号的场景,例如每次写请求之前都是服务端返回一个唯一编号给客户端,客户端带着这个请求号做请求,服务端即可完成去重拦截。
但是,很多的场景下,请求并不会带这样的唯一编号!那么我们能否针对请求的参数作为一个请求的标识呢?
先考虑简单的场景,假设请求参数只有一个字段 reqParam,我们可以利用以下标识去判断这个请求是否重复。
用户ID:接口名:请求参数
String KEY = "dedup:U="+userId + "M=" + method + "P=" + reqParam;
那么当同一个用户访问同一个接口,带着同样的 reqParam 过来,我们就能定位到他是重复的了。
但是问题是,我们的接口通常不是这么简单,以目前的主流,我们的参数通常是一个 JSON。那么针对这种场景,我们怎么去重呢?