Django/WSGI应用程序中的持久数据库连接

2024-10-01 17:22:35 发布

您现在位置:Python中文网/ 问答频道 /正文

我希望在django支持的web应用程序中保持与第三方遗留数据库的持久连接。在

user--inet--webserver--persistent-db-connection

我想保持web应用程序和遗留数据库之间的a连接打开,因为对于这个特殊的数据库,创建一个新的连接是非常缓慢的。在

它不像通常的连接池,因为我需要为每个web用户存储连接。用户“Foo”需要在web服务器和遗留数据库之间建立自己的连接。在

到目前为止,我使用Apache和wsgi,但是如果其他解决方案更适合,我可以改变。在

到目前为止,我使用django。在这里我也可以改变。但是痛苦会更大,因为已经有很多代码需要重新集成。在

到目前为止,我使用Python。我想是吧节点.js在这里比较合适,但是改变的痛苦太大了。在

当然需要一些暂停。如果N分钟内没有来自用户“Foo”的http请求,则需要关闭持久连接。在

如何解决这个问题?在

更新

我称它为DB,但它不是通过配置的DBsettings.数据库. 这是一个奇怪的,遗留的不是广泛传播的数据库类系统,我需要集成。在

如果我需要50人在线连接的话,我需要50人在线。每个用户一个。在

连接数据库的代码

我可以在每个请求中执行这一行:

strangedb_connection = strangedb.connect(request.user.username)

但这次行动很慢。使用连接很快。在

当然,strangedb_connection不能序列化,也不能存储在会话中:-)


Tags: django代码用户服务器web数据库应用程序wsgi
3条回答

管理连接的工作守护程序

您的图片当前看起来像:

user  ----------->  webserver  <--------[1]-->  3rd party DB

connection [1] is expensive.

您可以通过以下方法解决此问题:

^{pr2}$

工作守护进程将连接到第三方数据库并保持连接打开。这意味着不需要支付每个连接的费用。任务队列将是进程间通信,将工作分派给守护进程,并在第三方数据库中执行查询。在处理方面,web服务器应该尽可能轻,并让工人完成昂贵的任务。在

使用apache+modwsgi预加载

实际上,您可以preload并在第一个请求之前完成昂贵的连接。这是用^{} configuration directive完成的。我不记得有一个预加载+分叉配置是否意味着每个请求都已经打开了连接并共享它;但是由于您有大部分代码,这可能是一个简单的实验。在

uwsgi预压

uwsgi也支持预加载。这是用the ^{} directive完成的。在

您可以使用^{}指令来拥有多个worker线程,而不是有多个worker进程,它们都在一个进程中运行。这样,所有线程都可以共享相同的DB连接映射。在

在你的apache配置中有这样的东西。。。在

# mydomain.com.conf

<VirtualHost *:80>

    ServerName mydomain.com
    ServerAdmin webmaster@mydomain.com

    <Directory />
        Require all granted
    </Directory>

    WSGIDaemonProcess myapp processes=1 threads=50 python-path=/path/to/django/root display-name=%{GROUP}
    WSGIProcessGroup myapp
    WSGIScriptAlias / /path/to/django/root/myapp/wsgi.py

</VirtualHost>

…然后你可以在你的Django应用程序中使用像这样简单的东西。。。在

^{pr2}$

…在第一次击中时,会产生。。。在

Thread ID = 140597557241600
We made a connection for 'bob'
Query results for 'SELECT * FROM my_table'

…第二天。。。在

Thread ID = 140597145999104
We reused an existing connection for 'bob'
Query results for 'SELECT * FROM my_table'

显然,当不再需要数据库连接时,你需要添加一些东西来断开它们,但是如果没有更多关于你的应用程序应该如何工作的信息,很难知道最好的方法。在

更新1:关于I/O多路复用与多线程

I worked with threads twice in my live and each time it was a nightmare. A lot of time was wasted on debugging non reproducible problems. I think an event-driven and a non-blocking I/O architecture might be more solid.

使用I/O多路复用的解决方案可能更好,但会更复杂,而且还需要您的“strangedb”库来支持它,也就是说,它必须能够处理EAGAIN/EWOULDBLOCK),并且在必要时能够重试系统调用。在

Python中的多线程处理远没有其他大多数语言中的危险,这是因为Python的GIL,它本质上使所有Python字节码都是线程安全的。在

实际上,线程只在底层C代码使用^{}宏时并发运行,该宏及其对应的宏Py_END_ALLOW_THREADS通常围绕系统调用和CPU密集型操作进行包装。在

这样做的好处是,在Python代码中几乎不可能发生线程冲突,但缺点是它不能总是在一台机器上最佳地使用多个CPU核。在

我建议使用上面的解决方案的原因是它相对简单,并且需要最少的代码更改,但是如果您可以详细介绍您的“strangedb”库,可能会有更好的选择。对于每个并发用户需要单独的网络连接的数据库来说,这似乎很奇怪。在

更新2:关于多处理与多线程

...the GIL limitations around threading seem to be a bit of an issue. Isn't this one of the reasons why the trend is to use separate processes instead?

这很可能是Python的^{}模块存在的主要原因,即提供跨多个CPU内核的Python字节码的并发执行,尽管该模块中有一个使用线程而不是进程的undocumentedThreadPool类。在

如果你在游戏中每秒钟都有60个CPU的限制,那你就需要每秒钟都有一个CPU。在

然而,大多数基于web的服务可能会花费大部分时间等待发生的事情,例如网络I/O或磁盘I/O,Python线程允许这些事情并发发生。在

归根结底,这是性能和可维护性之间的权衡,考虑到硬件通常比开发人员的时间便宜得多,支持可维护性而不是性能通常更具成本效益。在

坦白地说,当你决定使用一种虚拟机语言,比如Python,而不是一种可以编译成真实机器代码的语言,比如C,你已经在说你准备牺牲一些性能来换取方便。在

另请参阅The C10K problem,以了解缩放基于web的服务的技术的比较。在

据我所知,你已经排除了大部分(全部?)这类问题的常见解决方案:

  • 在字典中存储连接。。。需要N个工人,但不能保证哪个请求发送给哪个工人
  • 在缓存中存储数据。。。数据太多
  • 在缓存中存储连接信息。。。连接不可串行化

据我所知,实际上只有1个“meta”解决方案,使用@Gahbu的字典建议,并保证对给定的user的请求将发送给同一个worker。一、 e.找出一种方法,每次都以同样的方式从User对象映射到一个给定的worker(可能按worker的数量散列它们的名称和MOD?)。在

如果当前活动的用户都映射到同一个worker,这个解决方案将不能充分利用N个worker,但是如果所有用户在同一时间处于活动状态的可能性相等,则工作应该均匀分布。(如果它们的可能性不尽相同,那么映射可能能够解释这一点)。在

我能想到的两种方法是:

1。编写自定义请求分配器

我不太熟悉apache/wsgi接口,但是。。。可以用一些自定义逻辑替换Apache服务器中将HTTP请求分派给worker的组件,这样它就可以始终向同一进程发送。在

2。在N个单线程工作线程前面运行负载平衡器/代理

我不确定您是否可以在这里使用现成的软件包,但概念是:

  • 运行实现此“将用户绑定到索引”逻辑的代理
  • 然后让代理将请求转发到Apache/wsgiwebserver的N个副本中的一个,每个副本都有一个worker。在

NB:我在这里遇到的第二个想法是:https://github.com/benoitc/gunicorn/issues/183

摘要

对于这两个选项,现有应用程序中的实现非常简单。您的应用程序只需更改为使用字典来存储持久连接(如果还没有,则创建一个)。在dev中测试单个实例与在生产环境中测试相同。在生产中,实例本身并不明智,因为总是询问相同的用户。在

我喜欢这里的选项2,原因如下:

  • 也许有一个现有的服务器包允许您定义这种代理的技巧
  • 如果不是这样,创建一个自定义代理应用程序使其位于当前应用程序前面可能并不困难(特别是考虑到当请求到达strangedb服务时您(已经)受到的限制)

相关问题 更多 >

    热门问题