1.定义:
HTTP 方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。说白了就是,同一个请求,发送一次和发送 N 次效果是一样的!幂等性是分布式系统设计中十分重要的概念,而 HTTP 的分布式本质也决定了它在 HTTP 中具有重要地位。
我们不能轻易假设分布式环境的可靠性。
【问题】
withdraw 的语义是从 account_id 对应的账户中扣除 amount 数额的钱;如果扣除成功则返回 true,账户余额减少 amount;如果扣除失败则返回 false,账户余额不变。
一种典型的情况是 withdraw 请求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认为上一次操作失败了,然后刷新页面,这就导致了 withdraw 被调用两次,账户也被多扣了一次钱。
【解决】
我们可以通过一些技巧把 withdraw 变成幂等的,比如:
int create_ticket()
bool idempotent_withdraw(ticket_id, account_id, amount)
create_ticket 的语义是获取一个服务器端生成的唯一的处理号 ticket_id,它将用于标识后续的操作。idempotent_withdraw 和 withdraw 的区别在于关联了一个 ticket_id,一个 ticket_id 表示的操作至多只会被处理一次,每次调用都将返回第一次调用时的处理结果。这样,idempotent_withdraw 就符合幂等性了,客户端就可以放心地多次调用。
基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤:1. 调用 create_ticket () 获取 ticket_id;2. 调用 idempotent_withdraw (ticket_id, account_id, amount)。虽然 create_ticket 不是幂等的,但在这种设计下,它对系统状态的影响可以忽略,加上 idempotent_withdraw 是幂等的,所以任何一步由于网络等原因失败或超时,客户端都可以重试,直到获得结果。
和分布式事务相比,幂等设计的优势在于它的轻量级,容易适应异构环境,以及性能和可用性方面。在某些性能要求比较高的应用,幂等设计往往是唯一的选择
1.GET 方法用于获取资源,不应有副作用,所以是幂等的。
2. DELETE 方法用于删除资源,有副作用,但它应该满足幂等性。比如:DELETE http://www.forum.com/article/4231,调用一次和 N 次对系统产生的副作用是相同的,即删掉 id 为 4231 的帖子;因此,调用者可以多次调用或刷新页面而不必担心引起错误。
3.PUT 方法用于创建或更新操作,有副作用,与 DELETE 相同,对同一资源无论调用一次还是多次,其副作用是相同的,因此也满足幂等性。比如:PUT http://www.forum/articles/4231 的语义是创建或更新 ID 为 4231 的帖子。对同一 URI 进行多次 PUT 的副作用和一次 PUT 是相同的;因此,PUT 方法具有幂等性。
4.POST 方法与 PUT 方法的区别主要在于幂等性,POST 不具备幂等性,因为 POST 请求每次都会创建一个文件,而 PUT 方法会在服务器验证是否有 ENTITY,若有则更新该 ENTITY 而不是重新创建。比如:POST http://www.forum.com/articles 的语义是在 http://www.forum.com/articles 下创建一篇帖子,HTTP 响应中应包含帖子的创建状态以及帖子的 URI。两次相同的 POST 请求会在服务器端创建两份资源,它们具有不同的 URI;所以,POST 方法不具备幂等性。
(二)如何防范 POST 重复提交
HTTP POST 操作既不是安全的,也不是幂等的(至少在 HTTP 规范里没有保证)。当我们因为反复刷新浏览器导致多次提交表单,多次发出同样的 POST 请求,导致远端服务器重复创建出了资源。
所以,对于电商应用来说,
第一、对应的后端 WebService 一定要做到幂等性,
第二、服务器端收到 POST 请求,在操作成功后必须 302 跳转到另外一个页面,这样即使用户刷新页面,也不会重复提交表单。
GET 和 POST 方法都是基于 TCP/IP 协议,也就是说,两者的数据传输都是建立在 TCP 的连接,所以,如果从两者的本质来讲并没有多大的区别,你非要给 GET 方法加上 request body,给 POST 方法加上 URL 参数都是行得通的,HTTP 协议对于 GET 和 POST 其实并没有长度限制。
因而,两者的区别更多地体现在使用规范上,从使用规范上来说:
二者还有一个显著区别:GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。
对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据);
而对于 POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。
【Java 面试那点事】
这里致力于分享 Java 面试路上的各种知识,无论是技术还是经验,你需要的这里都有!
这里可以让你【快速了解 Java 相关知识】,并且【短时间在面试方面有跨越式提升】
面试路上,你不孤单!