web-dev-qa-db-ja.com

InterfaceError:接続はすでに閉じています(Django + celery + Scrapyを使用)

Celeryタスク内でScrapy解析機能(10分ほどかかることもあります)を使用しているときに、これが発生します。

私が使用します:-Django == 1.6.5-Django-celery == 3.1.16-celery == 3.1.16-psycopg2 == 2.5.5(psycopg2 == 2.5.4も使用しました)

 [2015-07-19 11:27:49,488:CRITICAL/MainProcess]タスクmyapp.parse_items [63fc40eb-c0d6-46f4-a64e-acce8301d29a]内部エラー:InterfaceError( 'connection already closed'、)
トレースバック(最新の呼び出しは最後):
ファイル "/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/celery/app/trace.py "、284行目、trace_task 
 uuid、retval、SUCCESS、request = task_request、
ファイル" /home/mo/Work/python/pb-env/local/lib/python2.7/ site-packages/celery/backends/base.py "、行248、store_result 
 request = request、** kwargs)
ファイル"/home/mo/Work/python/pb-env /local/lib/python2.7/site-packages/djcelery/backends/database.py"、29行目の_store_result 
 traceback = traceback、children = self.current_task_children(request)、
ファイル「/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/djcelery/managers.py」の42行目の_inner 
に、fun(* args、 ** kwargs)
ファイル "/home/mo/Work/python/pb-env/local/lib/python2.7/ site-packages/djcelery/managers.py "、行181、store_result 
内の 'meta':{'children':children}})
ファイル"/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/djcelery/managers.py "、87行目のupdate_or_create 
でget get_queryset(self).update_or_create(** kwargs)
を返しますファイル "/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/djcelery/managers.py"、70行目、update_or_create 
 obj、created = self。 get_or_create(** kwargs)
ファイル "/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/Django/db/models/query.py"、line 376、get_or_create 
でself.get(** lookup)を返す、False 
ファイル "/home/mo/Work/python/pb-env/local/lib/python2.7/site- packages/Django/db/models/query.py "、304行目、get 
 num = len(clone)
ファイル"/home/mo/Work/python/pb-env/local /lib/python2.7/site-packages/Django/db/models/query.py"、77行、__ len __ 
 self._fetch_all()
ファイル "/ home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/Django/db/models/query.py "、857行目、_fetch_all 
内。self._result_cache= list(self.iterator())
ファイル「/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/Django/db/models/query.py」、行220、イテレータ
 compiler.results_iter():
ファイルの行の場合 "/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/Django/db/models/sql /compiler.py "、行713、self.execute_sql(MULTI)の行のresults_iter 
内:
ファイル"/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/Django/db/models/sql/compiler.py "、行785、execute_sql 
カーソル= self.connection.cursor()
ファイル"/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/Django/db/backends/__ init__.py "、line 160、in cursor 
 cursor = self.make_debug_cursor(self ._cursor())
ファイル "/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/Django/db/backends/__init__.py"、134行、 _cursor 
 return self.create_cursor()
ファイル "/home/mo/Work/python/pb-env/local/lib/python2.7/site-packages/Django/db/utils。 __exit __ 
のpy "、99行目。six.reraise(dj_exc_type、dj_exc_value、traceback)
ファイル" /home/mo/Work/python/pb-env/local/lib/python2.7 /site-packages/Django/db/backends/__init__.py"、134行目、_cursor 
でself.create_cursor()
ファイル "/ home/mo/Work/python/pb- env/local/lib/python2.7/site-packages/Django/db/backends/postgresql_psycopg2/base.py "、行137、create_cursor 
カーソル= self.connection.cursor()
 InterfaceError:接続はすでに閉じられています
16
mou55

残念ながら、これはDjango + psycopg2 +セロリコンボの問題です。これは、古い未解決の問題です。

このスレッドを見て理解してください: https://github.com/celery/Django-celery/issues/121

基本的に、セロリはワーカーを開始すると、Django.dbフレームワークからデータベース接続をフォークします。何らかの理由でこの接続が切断された場合、新しい接続は作成されません。 Django.dbライブラリを使用してデータベース接続が切断されたことを検出する方法がない場合、Celeryはこの問題とは何の関係もありません。 Djangoは、接続を開始するだけでwsgi呼び出しを受信するため(接続プールなし)、いつ発生しても通知しません。大量の実稼働環境で同じ問題が発生しましたマシンワーカー、場合によっては、これらのマシンはpostgresサーバーとの接続を失いました。

セロリの各マスタープロセスをLinuxのスーパーバイザハンドラとウォッチャに配置し、psycopg2.InterfaceErrorを処理するデコレータを実装して解決しました。この関数が発生すると、システムコールがディスパッチされ、スーパーバイザがSIGINTで正常に再起動します。

編集:

より良い解決策を見つけました。私はこのようにセロリタスクのベースクラスを実装しました:

from Django.db import connection
import celery

class FaultTolerantTask(celery.Task):
    """ Implements after return hook to close the invalid connection.
    This way, Django is forced to serve a new connection for the next
    task.
    """
    abstract = True

    def after_return(self, *args, **kwargs):
        connection.close()

@celery.task(base=FaultTolerantTask)
def my_task():
    # my database dependent code here

私はそれもあなたの問題を解決すると信じています。

13
mannysz

みんなと emanuelcds

私も同じ問題を抱えていましたが、コードを更新してセロリ用の新しいローダーを作成しました:

from djcelery.loaders import DjangoLoader
from Django import db

class CustomDjangoLoader(DjangoLoader):
    def on_task_init(self, task_id, task):
        """Called before every task."""
        for conn in db.connections.all():
            conn.close_if_unusable_or_obsolete()
        super(CustomDjangoLoader, self).on_task_init(task_id, task)

もちろん、djceleryを使用している場合は、設定で次のようなものが必要になります。

CELERY_LOADER = 'myproject.loaders.CustomDjangoLoader'
os.environ['CELERY_LOADER'] = CELERY_LOADER

私はまだそれをテストする必要があります、私は更新します。

6
PaoloC