接口幂等性

 

所谓的幂等性,就是在同样的条件下,一次请求和重复多次请求,对资源的影响是一样的。比如get,不论是请求多少次,都是同样的结果。比如delete,你删除了一条数据比如id=10,当你重复请求的时候,这个id=10的数据已经删除了,资源里面已经没有这条数据了,所以即便请求多次,结果跟请求一次是一样的,所以,对资源的影响是一样的。

关于put请求,这里摘抄了restful api官网的官方解释:

Generally – not necessarily – PUT APIs are used to update the resource state. If you invoke a PUT API N times, the very first request will update the resource; then rest N-1 requests will just overwrite the same resource state again and again – effectively not changing anything. Hence, PUT is idempotent.

对于同一条数据的修改,不论修改多少次,也只是不断的覆盖前面的相同修改,所以对于资源的影响也是不变的。

get/delete请求方式,是天然幂等,put请求,如果重复修改同一条数据,数据库是没有变化的,所以,put也不需要做幂等性处理。需要做幂等性处理的就只有post请求方式。

 

接口幂等性的解决办法:实际生产应用中

1、提交表单时,表单内有唯一性的元素,它本身就是天然幂等,因为唯一性的元素不会被写入到数据库,数据库的字段属性本身就会拦截它,比如手机号,比如身份证。100%解决幂等性问题。

2、如果你提交表单是用页面刷新的方式而不是用Ajax,那么,这种方式会好一些,页面刷新之后,表单数据自然清空,用户提交的时候需要手动再次输入,

这样可以大概率避免用户自身不断输入重复的数据提交,能解决80%的幂等性问题。

3、最简单粗暴:

  用Ajax提交表单,表单填写完成之后,获取到表单所有数据后,传入到Ajax,同时,清除表单用户填入的信息。在post请求结束后在回调函数里面跳转到get页面。

4、上面的2、3、都是前端做操作,后端什么都没有做。这次,换成后端做拦截,每次数据插入,都去数据库查询一遍,如果有同样的数据,就拦截提交,这样就能100%解决幂等性。幂等性问题是解决了,但是会引发出来新的问题,就是执行效率的问题,频次过高,数据库会崩溃,此时,是另一个维度的问题。解决办法就是,常用的数据查询,放入到缓存里面,这样就减轻了数据库的压力。如果数据量过大,缓存会有压力,届时,再解决缓存的问题,比如集群,主从备份,分布式等等。

 

5、教科书式的,token方式。如果单纯为了做token验证幂等性,然后配置上redis等这种缓存机制,就过于浪费了,在实际生产中,不会这样做。

不过,如果你的redis有其他用途,然后顺带着用它做token验证幂等性,这倒是可以。

token的流程:前端调用后端接口发起请求,把表单数据传给后端,后端拿到数据,设置token随机字符串,存入到缓存机制中,放到响应参数中,返回给前端,前端拿到响应数据,提取出来token值,写入到cookie中,document.cookie即可获取cookie值,在前端页面全局皆可获取cookie值。下次请求的时候,请求头会携带cookie信息,后端拿到请求头中的token信息,跟上次设置的存入到缓存中的token值进行比对,如果是同一个token,请求重复提交,做拦截,不修改数据库。

 

不论采用上面何种方法,接下来,都必须要如下处理,从安全方面考虑,防止DDS攻击(写脚本在单位时间内疯狂点击页面接口调取按钮),做频次拦截。

  后端需要处理的部分,做频次拦截,防止同一个用户在单位时间内,不断对接口进行操作,需要记录每一次操作的时间,进行比对,如果频次过高,

给前端返回提示信息,防止服务挂掉。这个记录下来的操作时间,需要存入到mongodb,redis,rabitmq这样的缓存服务里面,不能存入到python的内存里面。

请求结束后,python内部是有垃圾回收机制的,这些python内部的list,dict等都会被清空,就无法把每次请求时间做记录进行比对。

 

posted @ 2021-05-13 11:30  dream-子皿  阅读(107)  评论(0编辑  收藏  举报