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

Python Django/WSGI应用程序中的持久数据库连接,python,django,database-connection,Python,Django,Database Connection,我希望在django支持的web应用程序中保持与第三方遗留数据库的持久连接 我想保持web应用程序和旧数据库之间的a连接处于打开状态,因为对于这种特殊的数据库,创建新连接的速度非常慢 它不像通常的连接池,因为我需要为每个web用户存储连接。用户“Foo”需要在web服务器和遗留数据库之间建立自己的连接 到目前为止,我使用Apache和wsgi,但如果其他解决方案更适合,我可以更改 到目前为止,我使用django。在这里我也可以改变。但是痛苦会更大,因为已经有很多代码需要再次集成 到目前为止,我

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

我想保持web应用程序和旧数据库之间的a连接处于打开状态,因为对于这种特殊的数据库,创建新连接的速度非常慢

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

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

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

到目前为止,我使用Python。我想Node.js应该更适合这里,但是改变的痛苦太大了

当然,需要某种超时。如果N分钟内没有来自用户“Foo”的http请求,则需要关闭持久连接

如何解决这个问题

更新

我称之为
DB
,但它不是通过settings.DATABASES配置的DB。这是一个奇怪的遗留系统,不是我需要集成的广泛分布的DB类系统

如果我知道目前有50人在线使用web应用程序,那么我需要50个持久连接。每个用户一个

连接到数据库的代码

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

strangedb_connection = strangedb.connect(request.user.username)
但是这个操作很慢。使用连接速度很快


当然,
StrangedDB\u连接
无法序列化,也无法存储在会话中:-)

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

  • 在字典中存储连接。。。需要N个工作人员,但无法保证哪个请求发送给哪个工作人员
  • 在缓存中存储数据。。。太多数据
  • 在缓存中存储连接信息。。。连接不可序列化
据我所知,这实际上只有一个“元”解决方案,使用@Gahbu的字典建议,并保证给定
用户的请求转到同一个工作者。也就是说,找出一种方法,每次都以相同的方式将
用户
对象映射到给定的工作者(可能通过工作者数量散列他们的姓名和MOD?)

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

我能想到的两种可能的方法是:

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

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

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

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

  • 运行实现此“将用户绑定到索引”逻辑的代理
  • 然后让代理将请求转发到Apache/wsgi Web服务器的N个副本中的一个,每个副本都有一个工作服务器
注:我在这里遇到的第二个想法是:

摘要

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

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

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

您可以使用指令使多个工作线程在一个进程中运行,而不是有多个工作进程。这样,所有线程都可以共享相同的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>
…在第一次击中时,产生

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'
显然,当不再需要DB连接时,你需要添加一些东西来拆除它们,但是如果没有更多关于你的应用程序应该如何工作的信息,就很难知道最好的方法

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

在我的生活中,我两次使用线程,每次都是 噩梦。在调试不可复制文件上浪费了大量时间 问题。我认为这是一种事件驱动的非阻塞I/O体系结构 可能更坚固

使用I/O多路复用的解决方案可能更好,但会更复杂,并且还需要您的“奇异数据库”库来支持它,即它必须能够处理
EAGAIN
/
EWOULDBLOCK
,并且有能力在必要时重试系统调用

与大多数其他语言相比,Python中的多线程处理的危险性要小得多,因为Python的多线程处理本质上使所有Python字节码都是线程安全的

实际上,线程仅在底层C代码使用宏时并发运行,宏与其对应的
Py\u END\u ALLOW\u线程
,通常围绕系统调用和CPU密集型操作运行

这上面
Thread ID = 140597145999104
We reused an existing connection for 'bob'
Query results for 'SELECT * FROM my_table'
user  ----------->  webserver  <--------[1]-->  3rd party DB

connection [1] is expensive.
user ---->  webserver  <--->  task queue[1]  <--->  worker daemon  <--[2]-> 3rd party DB

[1] task queue can be redis, celery or rabbitmq.
[2] worker daemon keeps connection open.