我正在使用PyramidCornice为Web服务开发REST API;使用SQLAlchemyMySQL处理服务器端的数据。 Web服务器是使用nginxuwsgi,并且已配置为运行多个Python进程:

[uwsgi]
socket = localhost:6542
plugins = python34
...
processes = 2 # spawn the specified number of workers/processes
threads = 2 # run each worker in prethreaded mode with the specified number of threads


问题

假设服务器端有一个表customers。使用该API可以读取,修改或删除客户数据。除此之外,还有其他API函数可以读取客户数据。

我可以同时发出多个API调用,然后竞争相同的客户资源:

# Write/modify the customer {id} data
curl --request POST ... https://some.host/api/customer/{id}
# Delete customer {id} and all of its associated data
curl --request DELETE https://some.host/api/customer/{id}
# Perform some function which reads customer {id}
curl --request GET ... https://some.host/api/do-work


本质上,这是一个Readers-Writers Problem,但是由于涉及多个进程,因此使用locks/mutexes/semaphores的传统线程同步在这里将无法工作。



我想了解为此类基于Pyramid的Web API实现锁定和同步的最佳方法,从而可以安全有效地处理并发调用(如上述示例)(即,无需不必要的序列化)。

解决方案(?)


我认为将客户{id}标记/标记为locked并不有意义,因为SQLAlchemy会缓存此类修改,并且flush()在这种情况下似乎还不够原子化?
This article描述使用HTTP ETag来管理共享资源。
还可以将Redis用作distributed lock manager的自旋锁来包装视图函数吗?
金字塔的transaction manager呢?

最佳答案

我假设您正在处理一个MySQL数据库,并且您的锁不需要覆盖其他资源(Redis,第三方API等)。我还假设您的客户端功能本身并不需要处理事务数据(通过多个API调用维护会话),而您只是想防止并发的API访问破坏数据库。

锁定有两种,悲观锁定和乐观锁定。

悲观锁是大多数人通常通过锁知道的-您可以通过编程预先在代码中创建和获取锁。这就是分布式锁管理器。

乐观锁定是您可以很轻松地摆脱SQL数据库的束缚。如果两个事务从同一个资源竞争,则数据库实际上将使事务之一失败,并且应用程序框架(在这种情况下为Pyramid + pyramid_tm)可以在放弃之前重试N次。

从开发的角度来看,乐观锁定是更理想的解决方案,因为它不会给应用程序开发人员带来任何认知负担,让他们记住正确锁定资源或创建内部锁定机制。相反,开发人员依靠框架和数据库来重试和管理并发情况。但是,乐观锁定在Web开发人员中并不是众所周知的,因为由于编程语言缺乏灵活性,因此很难在广泛的PHP风格的环境中进行乐观锁定。

pyramid_tm实现了乐观锁定解决方案,我建议您使用它或其他乐观锁定解决方案,除非您知道不想使用的特定原因。


从Web开发人员的角度来看,pyramid_tm将事务生命周期与HTTP请求联系起来非常自然
pyramid_tm可以将其他事件与成功的交易相关联,例如pyramid_mailer仅在事务提交时才向用户发送电子邮件
pyramid_tm经过充分测试,基于ZODB transaction事务管理器,该管理器自2000年初开始投入生产
确保将SQLAlchemy session设置为SERIALIZABLE SQL isolation级别-从最高一致性模型开始。如果您知道API调用可以容忍此要求,则可以降低此要求,例如调用统计信息的只读分析。
乐观锁定通常在“正常”的大量读取中表现更好-很少有写入工作负载,而很少发生冲突(两个API调用一次更新同一用户)。仅当存在冲突时,交易重试罚款才会达到。
如果交易在N次重试后最终失败,例如在异常的高负载情况下,应在API使用方方面解决此问题,告知服务器端数据已更改,用户必须再次验证或重新填写表格


进一步阅读


Optimistic concurrency control in Wikipedia
SQLAlchemy + pyramid_tm example。注意:Try to avoid global DBSession object and use request.dbsession instead
Race condition incidence examples
ConflictResolver, Alternative, more low level, optimistic locking solution for SQLAlchemy, based on using Python functions as retryable context instead of full HTTP request

关于synchronization - Pyramid REST API:如何安全处理并发数据访问?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33934771/

10-17 01:21