Pyramid REST API:如何安全地处理并发数据访问?

Jen*_*ens 17 synchronization pyramid cornice

我正在使用PyramidCornice为Web服务开发REST API ; 服务器端的数据使用SQLAlchemyMySQL处理.Web服务器是使用uwsgi的nginx,它被配置为运行多个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
Run Code Online (Sandbox Code Playgroud)

问题

假设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
Run Code Online (Sandbox Code Playgroud)

本质上这是一个读者 - 写作者问题,但由于涉及多个进程,使用锁/互斥锁/信号量的传统线程同步在这里不起作用.

我想了解为这种基于Pyramid的Web API实现锁定和同步的最佳方法,以便安全有效地处理上述示例中的并发调用(即没有不必要的序列化).

解决方案(?)

  • 我不认为标记/标记客户{id}是有意义的,locked因为SQLAlchemy缓存了这样的修改,并且flush()在这种情况下看起来不够原子?
  • 本文介绍如何使用HTTP ETag管理共享资源.
  • 还可以使用Redis作为自旋锁的分布式锁管理器来包装视图函数吗?
  • 那么Pyramid的交易经理呢?

Mik*_*maa 12

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

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

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

乐观锁定是您可以轻松使用SQL数据库的方法.如果两个事务从同一资源竞争,则数据库有效地执行其中一个事务,并且应用程序框架(在本例中为Pyramid + pyramid_tm)可以在放弃之前重试该事务N次.

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

pyramid_tm 实现乐观锁定解决方案,我建议你使用它或其他一些乐观锁定解决方案,除非你知道一个你不想要的非常具体的原因.

  • pyramid_tm 将事务生命周期与HTTP请求联系起来,从Web开发人员的角度来看非常自然

  • pyramid_tm可以将其他事件与成功的事务联系起来,例如pyramid_mailer,只有在事务提交时才向用户发送电子邮件

  • pyramid_tm经过充分测试并基于ZODB transaction事务管理器,该管理器自2000年初开始投入生产

  • 确保您的SQLAlchemy会话设置为SERIALIZABLE SQL隔离级别 - 您从最高一致性模型开始.如果你知道API调用可以容忍它,你可以降低性能要求 - 例如调用统计只读分析.

  • 乐观锁定通常在"正常"大量读取中表现更好 - 很少写入工作负载,冲突很少发生(两次API调用一次更新同一用户).只有在发生冲突时才会发生事务重试惩罚.

  • 如果事务在N次重试后最终失败,例如在异常高负载情况下,则应在API使用者端解决,告知服务器端数据已更改且用户必须再次验证或重新填充表单

进一步阅读


Erw*_*sen 3

通常,您首先要确定哪种一致性模型是可接受的。您的一致性要求越弱,这个问题在服务器端就越容易出现。

例如:

是否有可能摆脱乐观并发?即假设您拥有锁,执行您的操作,但检测何时存在并发情况,以便您可以正确恢复?如果您预计不会发生很多碰撞,这可能是一个不错的选择。例如,Sqlalchemy 应该能够检测到它正在更新已被修改的行。

如果这是不可接受的,那么您可以在 Redis 中使用分布式锁定。您可能可以使用它来提出某种形式的同步。