我希望在django支持的Web应用程序中保持对第三方遗留数据库的持久连接。
我想保持Web应用程序和旧数据库之间的连接打开,因为对于这个特殊的数据库,创建新连接的速度非常慢。
它不像通常的连接池,因为我需要存储每个Web用户的连接。用户" Foo"需要在Web服务器和旧版DB之间建立自己的连接。
到目前为止,我使用Apache和wsgi,但如果其他解决方案更适合,我可以改变。
到目前为止,我使用django。在这里我也可以改变。但是痛苦会更大,因为已经有很多代码需要再次集成。
到目前为止,我使用的是Python。我想Node.js在这里会更合适,但改变的痛苦太高了。
当然需要某种超时。如果没有来自用户的http请求" Foo" N分钟,然后持久连接需要关闭。
怎么能解决这个问题?
更新
我称之为DB
,但它不是通过settings.DATABASES配置的数据库。这是一个我需要整合的奇怪的,传统的,不是广泛的类似DB的系统。
如果我此时有50人在线使用网络应用,那么我需要有50个持久连接。每个用户一个。
连接数据库的代码
我可以在每个请求中执行此行:
strangedb_connection = strangedb.connect(request.user.username)
但是这个操作很慢。使用连接很快。
当然strangedb_connection
无法序列化,也无法存储在会话中: - )
答案 0 :(得分:3)
据我所知,你排除了这类问题的大多数(所有?)常见解决方案:
据我所知,实际上只有1' meta'解决方案,使用@ Gahbu建议的字典并保证给定user
的请求转到同一个工作人员。即找出一种方法,每次都以相同的方式从User
对象映射到给定的工作者(可能用工作人员的数量哈希他们的名字和MOD?)。
如果当前活动的用户全部映射到同一个工作程序,但是如果所有用户同时可能同时处于活动状态,那么此解决方案将无法充分利用您的N个工作者,那么应该同等传播。 (如果它们不是同样可能的话,那么映射可以能够解释它。)
我能想到的两种可能的方法是:
<强> 1。编写自定义请求分配器
我并不熟悉apache / wsgi接口,但是......可能有可能替换Apache服务器中的组件,该组件使用一些自定义逻辑将HTTP请求分派给worker,这样就可以了总是派遣到同一个过程。
<强> 2。在N个单线程工作人员面前运行负载均衡器/代理
我不确定你是否可以在这里使用现成的包裹,但概念是:
注意:我在这里遇到的第二个想法是:https://github.com/benoitc/gunicorn/issues/183
<强>摘要强>
对于这两个选项,现有应用程序中的实现非常简单。您的应用程序只是更改为使用字典存储持久连接(如果还没有连接,则创建一个)。测试单个实例在开发中与在生产中相同。在生产中,实例本身并不明智,他们总是被问及相同的用户。
我喜欢选项2,原因如下:
strangedb
服务时你已经存在的限制)答案 1 :(得分:3)
您可以使用WSGIDaemonProcess
指令使多个worker 线程全部在一个进程中运行,而不是拥有多个worker 进程。这样,所有线程都可以共享相同的数据库连接映射。
在你的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应用程序中使用像这样简单的东西...
# views.py
import thread
from django.http import HttpResponse
# A global variable to hold the connection mappings
DB_CONNECTIONS = {}
# Fake up this "strangedb" module
class strangedb(object):
class connection(object):
def query(self, *args):
return 'Query results for %r' % args
@classmethod
def connect(cls, *args):
return cls.connection()
# View for homepage
def home(request, username='bob'):
# Remember thread ID
thread_info = 'Thread ID = %r' % thread.get_ident()
# Connect only if we're not already connected
if username in DB_CONNECTIONS:
strangedb_connection = DB_CONNECTIONS[username]
db_info = 'We reused an existing connection for %r' % username
else:
strangedb_connection = strangedb.connect(username)
DB_CONNECTIONS[username] = strangedb_connection
db_info = 'We made a connection for %r' % username
# Fake up some query
results = strangedb_connection.query('SELECT * FROM my_table')
# Fake up an HTTP response
text = '%s\n%s\n%s\n' % (thread_info, db_info, results)
return HttpResponse(text, content_type='text/plain')
......在第一次打击时产生......
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 / O架构 可能更坚固。
使用I / O多路复用的解决方案可能会更好,但会更复杂,并且还需要您的&#34; strangedb&#34;库支持它,即它必须能够处理EAGAIN
/ EWOULDBLOCK
并有能力在必要时重试系统调用。
Python中的多线程远比其他大多数语言都要危险,因为Python GIL实际上使得所有Python字节码都是线程安全的。
实际上,当底层C代码使用Py_BEGIN_ALLOW_THREADS
宏时,线程只会并发运行,multiprocessing
宏与其对应的Py_END_ALLOW_THREADS
通常包含在系统调用和CPU密集型操作中。 / p>
这样做的好处是,在Python代码中几乎不可能发生线程冲突,但缺点是它不会总是在一台机器上最佳地使用多个CPU内核。 / p>
我建议上述解决方案的原因是它相对简单,并且需要最少的代码更改,但如果您可以详细说明您的&#34; strangedb&#34;那么可能有更好的选择。图书馆。拥有一个每个并发用户需要单独网络连接的数据库似乎很奇怪。
更新#2:关于多处理与多线程
......围绕线程的GIL限制似乎是一个问题。 这不是趋势分开使用的原因之一 而不是过程?
这很可能是Python undocumented模块存在的主要原因,即提供跨多个CPU核心的Python字节码的并发执行,尽管存在The C10K problem {该模块中的{1}}类,它使用线程而不是进程。
&#34; GIL限制&#34;在你真正需要利用每个CPU核心上的每个CPU周期的情况下肯定会出现问题,例如:如果你正在编写一个必须以高清晰度每秒渲染60帧的计算机游戏。
然而,大多数基于网络的服务可能会花费大部分时间等待某些事情发生,例如:网络I / O或磁盘I / O,Python线程允许同时发生。
最终,它在性能和可维护性之间进行权衡,并且考虑到硬件通常比开发人员便宜得多,因此支持可维护性而不是性能通常更具成本效益。
坦率地说,当你决定使用虚拟机语言(如Python)而不是编译成真实机器代码的语言(例如C)时,你已经说过你已经准备好了牺牲一些表现来换取方便。
有关扩展基于Web的服务的技术比较,另请参阅{{3}}。
答案 2 :(得分:0)
执行此操作的一种简单方法是让另一个python进程管理持久连接池(每个用户一个,并在需要时可以超时)。然后另一个python进程和django可以像zeromq一样快速地进行通信。 interprocess communication in python