如何处理RESTful服务中的互斥操作?

时间:2016-07-30 19:09:48

标签: c# rest asp.net-web-api concurrency

我现在正在两个WebApi项目中工作,在这两个项目中我们都遇到了同样的问题:如果正在执行某个操作Actions,我们会执行一些操作A ,反之亦然。因此,例如,如果其中任何Actions正在运行且用户向操作A发送请求,则操作A必须等待其他操作完成才能运行。

来自有状态的服务背景,我们的第一个想法是使用锁,虽然有很大的阻力。我们创建了一个单例服务,它只存储映射到用户ID的object,后者又可以用作锁。

现在,当然这会带来一些非常棘手的问题,例如:

(1)我们必须强制用户始终只与一个服务器实例通信,即使他们的请求来自世界不同地区的不同设备,因为锁不会自动跨越一个实例到另一个。

(2)然后我们必须构建自己的[次优]负载均衡器。我们的计划是使用Azure的内置版本。

所以我的问题是:您如何以不会损害水平可伸缩性的方式优雅地处理此问题,并且最好不需要自定义负载平衡策略?

2 个答案:

答案 0 :(得分:3)

一种方法,但肯定不是唯一的方法,是对请求进行排队并以202(已接受)进行响应。通常,响应还将提供一个URL,客户端可以随后监视该URL以确定未完成请求的状态。同样,如果需要取消挂起的操作,客户端可以删除该URL。

编辑:以上面的解释另一种方式,假设每个修改共享资源的请求都按如下方式处理:

  1. 客户提出请求
  2. 前端服务器将请求转发到后端服务器,在后端服务器上排队等待处理(无需锁定)。除了明显的拒绝(格式错误的请求,未经授权的访问等),前端服务器始终接受请求。实际请求是否成功或者实际处理请求的时间对前端服务器来说并不重要。
  3. (现在,假设客户实际上需要知道请求的结果......)

    1. 前端服务器返回202(已接受)响应,其中包含URL。 URL有效地指向关联请求的状态(可能是结果)。
    2. 客户端定期轮询URL。 (前端服务器只是读取后端状态,因此它应该很快并且不需要锁定。)
    3. 最终,后端服务器将处理实际请求并根据需要更新请求状态。
    4. 客户端获取更新状态并采取相应措施。
    5. 这里的关键点是:

      • 大部分时间都不需要锁。当他们需要时,他们 应该只是为了非常快速的操作。
      • 请求的实际处理在后端序列化。这可以避免并发问题。
      • 前端的负载平衡和扩展问题最小化,因为它们仍在使用后端资源。前端服务器不需要与除了之外的任何东西协调 后端。

      虽然与原始问题没有密切关系,但这种方法的另一个优点是“排队”请求有效地成为共享资源的事务历史记录。

答案 1 :(得分:1)

您可以路由到任何服务器,只要它们都使用一些并发控制机制,同时排除运行关键操作。该机制可以是分布式锁(zookeeper,etcd,redis,hazelcast等)或集中锁(例如,仅用于锁定的特定服务器,db,调整的memcached群集)。然后,您的服务器可以等待锁定时响应REST客户端,或者您可以实施seairth的答案中建议的策略。我不会说它根本不会影响可伸缩性,但如果关键部分很短并且冲突很少,那么接收请求的所有服务器在大多数情况下都会很忙。