8

我正在运行一个 Django 应用程序。之前在Apache + mod_python下有过,一切OK。切换到 Lighttpd + FastCGI。现在我随机得到以下异常(无论是地点还是时间似乎都无法预测)。由于它是随机的,并且仅在切换到 FastCGI 后才会出现,因此我认为它与某些设置有关。

google 时发现了一些结果,但似乎与设置 maxrequests=1 有关。但是,我使用默认值,即 0。

任何想法在哪里寻找?

PS。我正在使用 PostgreSQL。也可能与此有关,因为在进行数据库查询时会出现异常。

 File "/usr/lib/python2.6/site-packages/django/core/handlers/base.py", line 86, in get_response
   response = callback(request, *callback_args, **callback_kwargs)

 File "/usr/lib/python2.6/site-packages/django/contrib/admin/sites.py", line 140, in root
   if not self.has_permission(request):

 File "/usr/lib/python2.6/site-packages/django/contrib/admin/sites.py", line 99, in has_permission
   return request.user.is_authenticated() and request.user.is_staff

 File "/usr/lib/python2.6/site-packages/django/contrib/auth/middleware.py", line 5, in __get__
   request._cached_user = get_user(request)

 File "/usr/lib/python2.6/site-packages/django/contrib/auth/__init__.py", line 83, in get_user
   user_id = request.session[SESSION_KEY]

 File "/usr/lib/python2.6/site-packages/django/contrib/sessions/backends/base.py", line 46, in __getitem__
   return self._session[key]

 File "/usr/lib/python2.6/site-packages/django/contrib/sessions/backends/base.py", line 172, in _get_session
   self._session_cache = self.load()

 File "/usr/lib/python2.6/site-packages/django/contrib/sessions/backends/db.py", line 16, in load
   expire_date__gt=datetime.datetime.now()

 File "/usr/lib/python2.6/site-packages/django/db/models/manager.py", line 93, in get
   return self.get_query_set().get(*args, **kwargs)

 File "/usr/lib/python2.6/site-packages/django/db/models/query.py", line 304, in get
   num = len(clone)

 File "/usr/lib/python2.6/site-packages/django/db/models/query.py", line 160, in __len__
   self._result_cache = list(self.iterator())

 File "/usr/lib/python2.6/site-packages/django/db/models/query.py", line 275, in iterator
   for row in self.query.results_iter():

 File "/usr/lib/python2.6/site-packages/django/db/models/sql/query.py", line 206, in results_iter
   for rows in self.execute_sql(MULTI):

 File "/usr/lib/python2.6/site-packages/django/db/models/sql/query.py", line 1734, in execute_sql
   cursor.execute(sql, params)

OperationalError: server closed the connection unexpectedly
       This probably means the server terminated abnormally
       before or while processing the request.
4

13 回答 13

5

可能的解决方案:http ://groups.google.com/group/django-users/browse_thread/thread/2c7421cdb9b99e48

直到最近我还想在 Django 1.1.1 上测试它。会不会再次抛出这个异常......惊喜,它又出现了。我花了一些时间来调试这个,有用的提示是它只在(预)分叉时显示。因此,对于那些随机获得这些异常的人,我可以说...修复您的代码 :) 好吧.. 说真的,这样做的方法总是很少,所以让我先解释一下问题出在哪里。如果您在任何模块将导入时访问数据库,例如从数据库读取配置,那么您将收到此错误。当你的 fastcgi-prefork 应用程序启动时,首先它会导入所有模块,并且只有在这之后才会派生子模块。如果您在导入期间建立了数据库连接,则所有子进程都将拥有该对象的精确副本。此连接在请求阶段结束时关闭(request_finished 信号)。因此,第一个将被调用来处理您的请求的孩子将关闭此连接。但是其余的子进程会发生什么?他们会相信他们已经打开了与数据库的连接,并且可能正在工作,因此任何数据库操作都会导致异常。为什么这没有在线程执行模型中显示?我想是因为线程正在使用相同的对象并且知道任何其他线程何时关闭连接。如何解决这个问题?最好的方法是修复你的代码......但这有时会很困难。另一个选项,在我看来很干净,是在你的应用程序的某处写一小段代码:因此,第一个将被调用来处理您的请求的孩子将关闭此连接。但是其余的子进程会发生什么?他们会相信他们已经打开了与数据库的连接,并且可能正在工作,因此任何数据库操作都会导致异常。为什么这没有在线程执行模型中显示?我想是因为线程正在使用相同的对象并且知道任何其他线程何时关闭连接。如何解决这个问题?最好的方法是修复你的代码......但这有时会很困难。另一个选项,在我看来很干净,是在你的应用程序的某处写一小段代码:因此,第一个将被调用来处理您的请求的孩子将关闭此连接。但是其余的子进程会发生什么?他们会相信他们已经打开了与数据库的连接,并且可能正在工作,因此任何数据库操作都会导致异常。为什么这没有在线程执行模型中显示?我想是因为线程正在使用相同的对象并且知道任何其他线程何时关闭连接。如何解决这个问题?最好的方法是修复你的代码......但这有时会很困难。另一个选项,在我看来很干净,是在你的应用程序的某处写一小段代码:但是其余的子进程会发生什么?他们会相信他们已经打开了与数据库的连接,并且可能正在工作,因此任何数据库操作都会导致异常。为什么这没有在线程执行模型中显示?我想是因为线程正在使用相同的对象并且知道任何其他线程何时关闭连接。如何解决这个问题?最好的方法是修复你的代码......但这有时会很困难。另一个选项,在我看来很干净,是在你的应用程序的某处写一小段代码:但是其余的子进程会发生什么?他们会相信他们已经打开了与数据库的连接,并且可能正在工作,因此任何数据库操作都会导致异常。为什么这没有在线程执行模型中显示?我想是因为线程正在使用相同的对象并且知道任何其他线程何时关闭连接。如何解决这个问题?最好的方法是修复你的代码......但这有时会很困难。另一个选项,在我看来很干净,是在你的应用程序的某处写一小段代码:但这有时可能很困难。另一个选项,在我看来很干净,是在你的应用程序的某处写一小段代码:但这有时可能很困难。另一个选项,在我看来很干净,是在你的应用程序的某处写一小段代码:

from django.db import connection 
from django.core import signals 
def close_connection(**kwargs): 
    connection.close() 
signals.request_started.connect(close_connection) 

不理想的想法,两次连接到数据库充其量是一种解决方法。


可能的解决方案:使用连接池(pgpool,pgbouncer),这样您就可以将数据库连接池化且稳定,并快速传递给您的 FCGI 守护进程。

问题是这会触发另一个错误,psycopg2 引发InterfaceError因为它试图断开两次连接(pgbouncer 已经处理了这个)。

现在罪魁祸首是 Django 信号request_finished触发connection.close(),即使它已经断开连接,也会大声失败。我不认为这种行为是可取的,好像请求已经完成,我们不再关心数据库连接。纠正这个问题的补丁应该很简单。

相关回溯:

 /usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/core/handlers/wsgi.py in __call__(self=<django.core.handlers.wsgi.WSGIHandler object at 0x24fb210>, environ={'AUTH_TYPE': 'Basic', 'DOCUMENT_ROOT': '/storage/test', 'GATEWAY_INTERFACE': 'CGI/1.1', 'HTTPS': 'off', 'HTTP_ACCEPT': 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_AUTHORIZATION': 'Basic dGVzdGU6c3VjZXNzbw==', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_COOKIE': '__utma=175602209.1371964931.1269354495.126938948...none); sessionid=a1990f0d8d32c78a285489586c510e8c', 'HTTP_HOST': 'www.rede-colibri.com', ...}, start_response=<function start_response at 0x24f87d0>)
  246                 response = self.apply_response_fixes(request, response)
  247         finally:
  248             signals.request_finished.send(sender=self.__class__)
  249 
  250         try:
global signals = <module 'django.core.signals' from '/usr/local/l.../Django-1.1.1-py2.6.egg/django/core/signals.pyc'>, signals.request_finished = <django.dispatch.dispatcher.Signal object at 0x1975710>, signals.request_finished.send = <bound method Signal.send of <django.dispatch.dispatcher.Signal object at 0x1975710>>, sender undefined, self = <django.core.handlers.wsgi.WSGIHandler object at 0x24fb210>, self.__class__ = <class 'django.core.handlers.wsgi.WSGIHandler'>
 /usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/dispatch/dispatcher.py in send(self=<django.dispatch.dispatcher.Signal object at 0x1975710>, sender=<class 'django.core.handlers.wsgi.WSGIHandler'>, **named={})
  164 
  165         for receiver in self._live_receivers(_make_id(sender)):
  166             response = receiver(signal=self, sender=sender, **named)
  167             responses.append((receiver, response))
  168         return responses
response undefined, receiver = <function close_connection at 0x197b050>, signal undefined, self = <django.dispatch.dispatcher.Signal object at 0x1975710>, sender = <class 'django.core.handlers.wsgi.WSGIHandler'>, named = {}
 /usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/db/__init__.py in close_connection(**kwargs={'sender': <class 'django.core.handlers.wsgi.WSGIHandler'>, 'signal': <django.dispatch.dispatcher.Signal object at 0x1975710>})
   63 # when a Django request is finished.
   64 def close_connection(**kwargs):
   65     connection.close()
   66 signals.request_finished.connect(close_connection)
   67 
global connection = <django.db.backends.postgresql_psycopg2.base.DatabaseWrapper object at 0x17b14c8>, connection.close = <bound method DatabaseWrapper.close of <django.d...ycopg2.base.DatabaseWrapper object at 0x17b14c8>>
 /usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/db/backends/__init__.py in close(self=<django.db.backends.postgresql_psycopg2.base.DatabaseWrapper object at 0x17b14c8>)
   74     def close(self):
   75         if self.connection is not None:
   76             self.connection.close()
   77             self.connection = None
   78 
self = <django.db.backends.postgresql_psycopg2.base.DatabaseWrapper object at 0x17b14c8>, self.connection = <connection object at 0x1f80870; dsn: 'dbname=co...st=127.0.0.1 port=6432 user=postgres', closed: 2>, self.connection.close = <built-in method close of psycopg2._psycopg.connection object at 0x1f80870>

这里的异常处理可以增加更多的宽大处理:

/usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/db/__init__.py

   63 # when a Django request is finished.
   64 def close_connection(**kwargs):
   65     connection.close()
   66 signals.request_finished.connect(close_connection)

或者它可以在 psycopg2 上得到更好的处理,所以如果我们想要做的只是断开连接并且它已经是,那么就不会抛出致命错误:

/usr/local/lib/python2.6/dist-packages/Django-1.1.1-py2.6.egg/django/db/backends/__init__.py

   74     def close(self):
   75         if self.connection is not None:
   76             self.connection.close()
   77             self.connection = None

除此之外,我缺乏想法。

于 2010-03-23T21:57:07.560 回答
0

在交换机中,您是否更改了 PostgreSQL 客户端/服务器版本?

我在 php+mysql 上看到过类似的问题,罪魁祸首是客户端/服务器版本之间的不兼容(即使它们具有相同的主要版本!)

于 2008-12-26T09:43:27.020 回答
0

闻起来像是可能的线程问题。尽管文件内文档似乎表明 Django/FCGI 可以以这种方式运行,但Django 并不能保证线程安全。尝试使用 prefork 运行,然后将垃圾从服务器中剔除。如果问题消失...

于 2008-12-26T17:29:32.383 回答
0

两种设置(Apache+mod_python 和 lighttpd + FastCGI)的 PYTHONPATH 和 PATH 环境变量可能不同。

于 2009-02-13T17:29:29.383 回答
0

最后,我切换回 Apache + mod_python(除了这个,我还遇到了 fcgi 的其他随机错误),现在一切都很好而且很稳定。

这个问题仍然悬而未决。如果将来有人遇到此问题并解决它,他们可以在此处记录解决方案以供将来参考。:)

于 2009-03-02T11:23:49.783 回答
0

在使用未将默认 ORM 用于其功能之一的 geodjango 模型时,我修复了类似的问题。当我添加一行手动关闭连接时,错误消失了。

http://code.djangoproject.com/ticket/9437

但是,在使用用户登录/会话进行操作时,我仍然会随机看到错误(约 50% 的请求)。

于 2009-03-13T23:56:27.060 回答
0

我最近遇到了同样的问题(lighttpd、fastcgi 和 postgre)。找了几天的解决方案没有成功,万不得已改用mysql。问题消失了。

于 2009-06-27T19:17:03.837 回答
0

为什么不将会话存储在缓存中?放

SESSION_ENGINE = "django.contrib.sessions.backends.cache"

您也可以尝试将 postgres 与pgbouncer一起使用(postgres - prefork 服务器,每次不喜欢多次连接/断开连接),但首先检查您的 postgresql.log。

另一个版本 - 您在会话表中有许多记录,django-admin.py cleanup可以提供帮助。

于 2009-11-07T00:07:35.073 回答
0

问题可能主要与进口有关。至少这就是发生在我身上的事。在网上一无所获后,我编写了自己的解决方案。请在此处查看我的博文:Simple Python Utility to check all Imports in your project

当然,这只会帮助您很快找到原始问题的解决方案,而不是您问题的实际解决方案本身。

于 2009-11-16T18:46:07.967 回答
0

从 method=prefork 更改为 method=threaded 为我解决了这个问题。

于 2010-03-05T23:06:49.823 回答
0

即使我没有使用 django 而是使用金字塔作为框架,我也会尝试对此给出答案。很长一段时间以来我都遇到了这个问题。问题是,为测试产生这个错误真的很困难......无论如何。最后,我通过挖掘会话、范围会话、会话实例、引擎和连接等所有内容来解决它。我发现了这个:

http://docs.sqlalchemy.org/en/rel_0_7/core/pooling.html#disconnect-handling-pessimistic

这种方法只是简单地将监听器添加到引擎的连接池中。在侦听器中,向数据库查询静态选择。如果它失败,池尝试在它完全失败之前建立到数据库的新连接。重要提示:这发生在将任何其他内容扔到数据库之前。因此,可以预先检查连接以防止其余代码失败。

这不是一个干净的解决方案,因为它本身不能解决错误,但它就像一个魅力。希望这可以帮助某人。

于 2015-10-14T14:57:08.777 回答
0

适用报价:

"2019 anyone?" - half of YouTube comments, circa 2019

如果有人仍在处理这个问题,请确保您的应用程序“急切地分叉”,以便您的 Python DB 驱动程序(psycopg2对我而言)不会在进程之间共享资源。

我通过添加lazy-apps = true选项在 uWSGI 上解决了这个问题,这导致直接派生应用程序进程,而不是等待写时复制。我想其他 WSGI / FastCGI 主机也有类似的选项。

于 2019-03-08T21:19:27.213 回答
-1

您是否考虑过降级到 Python 2.5.x(特别是 2.5.4)?我认为 Django 在 Python 2.6 上不会被认为是成熟的,因为存在一些向后不兼容的更改。但是,我怀疑这会解决您的问题。

此外,Django 1.0.2 修复了一些邪恶的小错误,因此请确保您正在运行它。这很好地解决了你的问题。

于 2009-04-29T18:35:06.703 回答