2010-06-13 29 views
9

Ứng dụng Giá treo của tôi sử dụng máy chủ MySQL cục bộ qua SQLAlchemy và python-MySQLdb. Khi máy chủ được khởi động lại, kết nối gộp mở được rõ ràng đóng cửa, nhưng các ứng dụng không biết về điều này và dường như khi nó cố gắng sử dụng kết nối như vậy mà nó nhận được "máy chủ MySQL đã biến mất":Xử lý khởi động lại mysql trong SQLAlchemy

File '/usr/lib/pymodules/python2.6/sqlalchemy/engine/default.py', line 277 in do_execute 
    cursor.execute(statement, parameters) 
File '/usr/lib/pymodules/python2.6/MySQLdb/cursors.py', line 166 in execute 
    self.errorhandler(self, exc, value) 
File '/usr/lib/pymodules/python2.6/MySQLdb/connections.py', line 35 in defaulterrorhandler 
    raise errorclass, errorvalue 
OperationalError: (OperationalError) (2006, 'MySQL server has gone away') 

ngoại lệ này không bị bắt ở bất kỳ nơi nào để nó phát tán tới người dùng. Nếu tôi nên xử lý ngoại lệ này ở đâu đó trong mã của tôi, hãy hiển thị vị trí cho mã như vậy trong một ứng dụng WSGI giá treo. Hoặc có thể có một giải pháp trong SA chính nó?

Trả lời

6

Xem EDIT ở dưới cùng cho giải pháp thử nghiệm

Tôi không thử nó, nhưng có lẽ sử dụng PoolListener là một con đường để đi?

Bạn có thể làm một cái gì đó như thế này:

class MyListener(sqlalchemy.interfaces.PoolListener): 
    def __init__(self): 
     self.retried = False 
    def checkout(self, dbapi_con, con_record, con_proxy): 
     try: 
      dbapi_con.info() # is there any better way to simply check if connection to mysql is alive? 
     except sqlalchemy.exc.OperationalError: 
      if self.retried: 
       self.retried = False 
       raise # we do nothing 
      self.retried = True 
      raise sqlalchemy.exc.DisconnectionError 

# next, code according to documentation linked above follows 

e = create_engine("url://", listeners=[MyListener()]) 

Bằng cách này mỗi kết nối thời gian sắp được kiểm tra ra từ hồ bơi chúng tôi kiểm tra nếu nó thực sự kết nối với máy chủ. Nếu không, chúng tôi cung cấp cho sqlalchemy một cơ hội để kết nối lại. Sau đó, nếu vấn đề vẫn còn đó, chúng ta hãy để nó đi.

PS: Tôi đã không kiểm tra xem công trình này có hoạt động hay không.

Edit: Đối với các giá treo, việc sửa đổi các khởi động trình bày ở trên sẽ cần phải được thực hiện trong your_app.model.init_model (giá treo 0.9.7) hoặc your_app.config.environment.load_environment (giá treo 1.0) Chức năng - đây là đây là địa điểm nơi nơi phiên bản động cơ được tạo.

EDIT

Ok. Tôi đã có thể tái tạo tình huống được mô tả. Đoạn mã trên cần một số thay đổi để hoạt động. Dưới đây là cách thực hiện. Ngoài ra nó không quan trọng cho dù đó là 0.9.7 hoặc 1.0.

Bạn cần chỉnh sửa your_app/config/environment.py. Đặt những xuất khẩu ở phía trên của tập tin:

import sqlalchemy 
import sqlalchemy.interfaces 
import _mysql_exceptions 

Và cuối chức năng load_environment nên xem xét như thế:

class MyListener(sqlalchemy.interfaces.PoolListener): 
    def __init__(self): 
     self.retried = False 
    def checkout(self, dbapi_con, con_record, con_proxy): 
     try: 
      dbapi_con.cursor().execute('select now()') 
     except _mysql_exceptions.OperationalError: 
      if self.retried: 
       self.retried = False 
       raise 
      self.retried = True 
      raise sqlalchemy.exc.DisconnectionError 

config['sqlalchemy.listeners'] = [MyListener()] 

engine = engine_from_config(config, 'sqlalchemy.') 
init_model(engine) 

thời gian này tôi đã có thể kiểm tra nó (trên giá treo 1.0 + SQLAlchemy 0.6. 1) và hoạt động. :)

+0

Cảm ơn, linh hồn tương tự là ở đây: http://www.mail-archive.com/[email protected]/msg15079.html và nó hoạt động cho tôi. – wRAR

+0

Không thấy chỉnh sửa của bạn :) – wRAR

+0

Lưu ý đối với SQLAlchemy 0.7 - 'PoolListener' không được dùng nữa, nhưng giải pháp tương tự có thể được triển khai bằng [hệ thống sự kiện] mới (http://docs.sqlalchemy.org/en/latest/ core/pooling.html # disconnect-handling-bi quan). –

3

Bạn có thể sử dụng SQLAlchemy proxy cho xử lý trên mỗi truy vấn sql ngoại lệ:

from sqlalchemy.interfaces import ConnectionProxy 
class MyProxy(ConnectionProxy): 
    def cursor_execute(self, execute, cursor, statement, parameters, context, executemany): 
     try: 
      return execute(cursor, statement, parameters, context) 
     except sqlalchemy.exc.OperationalError: 
      # Handle this exception 
      pass 

Để kết nối proxy này bạn phải làm điều đó trong config/môi trường.py

engine = engine_from_config(config, 'sqlalchemy.', proxy=MyProxy()) 

Hoặc viết trung để xử lý trên mỗi truy vấn http ngoại lệ:

class MyMiddleware(object): 
    def __init__(self, app): 
     self.app = app 

    def __call__(self, environ, start_response): 
     try: 
      return self.app(environ, start_response) 
     except sqlalchemy.exc.OperationalError: 
      start_response(
       '500 Internal Server Error', 
       [('content-type', 'text/html')]) 
      return ['error page\n'] 

Để kết nối trung gian này trong ngăn xếp thứ tự như bạn cần hoặc chỉ đơn giản là trong config/middleware.py:

# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) 
app = MyMiddleware(app) 
Các vấn đề liên quan