【问题标题】:How to make requests_cache work with many concurrent requests?如何使 requests_cache 与许多并发请求一起工作?
【发布时间】:2017-01-03 01:36:19
【问题描述】:

我正在获取并缓存(为了提高性能)大量的 URL,例如:

import requests
import requests_cache
from multiprocessing.pool import ThreadPool

urls = ['http://www.google.com', ...]
with requests_cache.enabled():
    responses = ThreadPool(100).map(requests.get, urls)

但是,我遇到了很多错误:

sqlite3.OperationalError: database is locked

显然有太多线程同时访问缓存。

那么requests_cache 是否支持某种事务,以便仅在所有线程都完成时才发生写入?例如

with requests_cache.enabled():
    with requests_cache.transaction():
        responses = ThreadPool(100).map(requests.get, urls)

【问题讨论】:

    标签: python caching concurrency python-requests


    【解决方案1】:

    我有一个 Django-Rest-Framework 应用程序。它工作得很好,直到请求同时进来。发生这种情况时,应用程序有时会开始抛出 database is locked 错误。我的第一个猜测是,Django-db 已超载,需要用更强大的东西替换。

    通过使用来自 bash (see here) 的 curl 运行并行请求来重现问题,为我提供了新的日志和跟踪。我发现请求缓存在清理其数据库时遇到了问题。它被配置为缓存 600 秒,因此填充缓存后的第一次批处理总是会失败:

    ...
    File "/opt/app/lib/python3.5/site-packages/requests_cache/core.py" in remove_expired_responses
    159.         self.cache.remove_old_entries(datetime.utcnow() - self._cache_expire_after)
    
    File "/opt/app/lib/python3.5/site-packages/requests_cache/backends/base.py" in remove_old_entries
    117.             self.delete(key)
    
    File "/opt/app/lib/python3.5/site-packages/requests_cache/backends/base.py" in delete
    83.                 del self.responses[key]
    
    File "/opt/app/lib/python3.5/site-packages/requests_cache/backends/storage/dbdict.py" in __delitem__
    130.                               self.table_name, (key,))
    
    Exception Type: OperationalError at /app/v1/invitations/
    Exception Value: database is locked
    

    寻找可能的解决方案,我发现Redis 可以用作后端。我安装了 Redis 并仅为 localhost 运行它。只需将缓存配置的 backendsqlite 设置为 'redis' 即可解决问题。

    我感觉有点像在用锤子固定松动的螺栓,但我很高兴我能在没有损坏任何东西的情况下让它工作。我确信有人能够找到更好、更优雅的解决方案,例如通过requests-cache 或代码修复传递 sqlite-config-param。

    【讨论】:

      【解决方案2】:

      由于requests.cache.enabled() 及其related functions 使用猴子补丁,不幸的是它不是线程安全的。

      幸运的是,执行所有实际缓存 (CachedSession) 的底层类在 requests-cache 0.6+ 中是线程安全的(并且在 0.7+ 中进行了更多改进 strong>),所以这可能就是你想在这里使用的。这里有一个使用ThreadPoolExecutor 的完整示例:https://github.com/reclosedev/requests-cache/blob/master/examples/threads.py

      就像提到的另一个答案一样,Redis 将成为并发请求的更好选择,但这并不是完全必要的。 SQLite 可以很好地处理并发性;它支持无限并发读取,但并发写入在内部排队并以串行方式运行。在很多情况下,这仍然足够快,您甚至都不会注意到,但如果您正在执行大量并发写入,那么 Redis 或 one of the other backends 将对此进行更好的优化。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-21
        • 2015-02-15
        • 1970-01-01
        • 1970-01-01
        • 2010-10-30
        相关资源
        最近更新 更多