Restful api 与 幂等性

Restful

REST全称是Representational State Transfer(表征性状态转移),适用于前后端分离
通常在接口URL中出现api关键字和资源名词,并可通过v版本号的方式标识版本,如:https://api.baidu.com/v1/usershttps://www.baidu.com/api/v2/books
资源的操作方式和返回结果,由请求方式决定:

  • GET /collection:返回资源对象的列表(数组)
  • GET /collection/id:返回单个资源对象
  • POST /collection:新增资源,并返回该资源对象
  • PUT /collection/id:整体修改资源,并返回该资源对象
  • PATCH /collection/id:局部修改资源,并返回该资源对象
  • DELETE /collection/id:删除资源,并返回空

幂等性

若一次和多次调用同一个HTTP请求,总是有相同的影响,则称其具有幂等性

分布式事务 vs 幂等设计

为什么需要幂等性呢?我们先从一个例子说起,假设有一个取钱的API

bool withdraw(account_id, amount)

从account_id对应的账户中扣除amount数额的钱;如果扣除成功则返回true,账户余额减少amount;如果扣除失败则返回false,账户余额不变。值得注意的是:和本地环境相比,我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认为上一次操作失败了,然后刷新页面,这就导致了withdraw被调用两次,账户也被多扣了一次钱。如图1所示:


这个问题的解决方案一是采用分布式事务,通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单,复杂性都交给了中间件来管理。缺点则是一方面架构太重量级,容易被绑在特定的中间件上,不利于异构系统的集成;另一方面分布式事务虽然能保证事务的ACID性质,而但却无法提供性能和可用性的保证。

另一种更轻量级的解决方案是幂等设计。我们可以通过一些技巧把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是幂等的,所以任何一步由于网络等原因失败或超时,客户端都可以重试。如图2所示:


  • GET
    用于获取资源,无副作用,所以是幂等的。
  • DELETE
    用于删除资源,有副作用。
    多次删除同一资源的副作用是相同的,因此满足幂等
  • POST
    不指向一个具体id(用于请求源服务器接受请求中的实体作为 Request-URI 所标志的资源的新下级),用于创建资源。
    多次执行会创建多个资源,显然非幂等
  • PUT
    指向一个具体id,如该id资源存在则完整更新(覆盖),否则进行创建。
    幂等的。
  • PATCH
    指向一个具体id,用于局部更新资源(后端只更新前端传入部分属性,不影响其他)。
    非幂等,因局部更新可能不是直接指定某个值,而是一次运算(如每次patch都+1),多次执行会导致不同的结果。

你可能感兴趣的:(Restful api 与 幂等性)