我现在正在两个WebApi项目中工作,在这两个项目中我们都遇到了同样的问题:如果正在执行某个操作Actions
,我们会执行一些操作A
,反之亦然。因此,例如,如果其中任何Actions
正在运行且用户向操作A
发送请求,则操作A
必须等待其他操作完成才能运行。
来自有状态的服务背景,我们的第一个想法是使用锁,虽然有很大的阻力。我们创建了一个单例服务,它只存储映射到用户ID的object
,后者又可以用作锁。
现在,当然这会带来一些非常棘手的问题,例如:
(1)我们必须强制用户始终只与一个服务器实例通信,即使他们的请求来自世界不同地区的不同设备,因为锁不会自动跨越一个实例到另一个。
(2)然后我们必须构建自己的[次优]负载均衡器。我们的计划是使用Azure的内置版本。
所以我的问题是:您如何以不会损害水平可伸缩性的方式优雅地处理此问题,并且最好不需要自定义负载平衡策略?
答案 0 :(得分:3)
一种方法,但肯定不是唯一的方法,是对请求进行排队并以202(已接受)进行响应。通常,响应还将提供一个URL,客户端可以随后监视该URL以确定未完成请求的状态。同样,如果需要取消挂起的操作,客户端可以删除该URL。
编辑:以上面的解释另一种方式,假设每个修改共享资源的请求都按如下方式处理:
(现在,假设客户实际上需要知道请求的结果......)
这里的关键点是:
虽然与原始问题没有密切关系,但这种方法的另一个优点是“排队”请求有效地成为共享资源的事务历史记录。
答案 1 :(得分:1)
您可以路由到任何服务器,只要它们都使用一些并发控制机制,同时排除运行关键操作。该机制可以是分布式锁(zookeeper,etcd,redis,hazelcast等)或集中锁(例如,仅用于锁定的特定服务器,db,调整的memcached群集)。然后,您的服务器可以等待锁定时响应REST客户端,或者您可以实施seairth的答案中建议的策略。我不会说它根本不会影响可伸缩性,但如果关键部分很短并且冲突很少,那么接收请求的所有服务器在大多数情况下都会很忙。