我应该如何在 Bottle 应用程序中使用 sqlalchemy 会话来避免 'Lost connection to MySQL server during query'

How should I use sqlalchemy session in bottle app to avoid 'Lost connection to MySQL server during query'

我的 bottle/sqlalchemy 应用托管在 pythonanywhere.com 上,不确定这是否重要,我希望不会。该应用程序在我离开一段时间后抛出 'Lost connection to MySQL server during query'。然后我刷新它,它工作正常。

请注意,我在没有使用 sqlachemy 的情况下仅使用 MySQLdb 也实现了相同的查询。该实现一直运行良好,它从不抛出异常,因为它每次都会建立一个新连接。

我假设,在像 pythonanywhere 这样的托管环境中,我不能 fiddle 使用可能影响此错误的 mysql 配置,例如 max_allowed_packet 或超时。

如何创建sqlalchemy引擎和会话来解决这个问题?

bottle_app.py:

db_host = 'localhost'
db_user = 'root'
db_password = 'gggggg'
db_dbname = 'test'

#web framework imports
from bottle import default_app, route, run, template, redirect

#sqlalchemy and mysql setup
mysql_connect_string = 'mysql+mysqldb://%s:%s@%s/%s?charset=utf8' % (db_user, db_password, db_host, db_dbname)
from sqlalchemy import create_engine
engine = create_engine(mysql_connect_string)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
Base.metadata.bind = engine
from sqlalchemy import Column, Integer, String, Float, SmallInteger
from sqlalchemy.orm import sessionmaker
DBSession = sessionmaker()
DBSession.bind = engine

#import mysql package for raw SQL
import MySQLdb

#sqlalchemy model class for table 'nevek'
class Person(Base):
    __tablename__ = 'nevek'
    # Here we define columns for the table nevek
    id = Column(Integer, primary_key=True)
    name = Column(String(250), nullable=False)
    lev = Column(SmallInteger)
    point = Column(Float)
    play = Column(Integer)
    kmp = Column(Float)

#get records from table nevek with sqlalchemy
def get_nevek_from_db():
    session = DBSession()
    result = session.query(Person).all()
    session.close()
    return result

#basic handler will redirect to nevek
@route('/')
def hello_world():
    redirect('/nevek')

@route('/nevek')
def nevek():
    return template('nevek-obj', nevek=get_nevek_from_db())

#get records from table nevek
def get_raw_sql(sql):
    conn = MySQLdb.connect(host=db_host, user=db_user,
        passwd=db_password, db=db_dbname, charset='utf8')
    cur = conn.cursor()
    cur.execute(sql)
    res = cur.fetchall()
    cur.close()
    conn.close()
    return res

@route('/nevek-raw')
def nevek_raw():
    return template('nevek-tuple', nevek=get_raw_sql("SELECT * FROM nevek"))


#this will be imported and run by the wsgi.py (in hosted env)
application = default_app()

#this will be used when running on your own machine
if __name__ == '__main__':
    run(application)

异常:

2015-02-25 12:43:57,107 :Traceback (most recent call last):
2015-02-25 12:43:57,108 :  File "/usr/local/lib/python2.7/dist-packages/bottle.py", line 764, in _handle
2015-02-25 12:43:57,108 :    return route.call(**args)
2015-02-25 12:43:57,108 :  File "/usr/local/lib/python2.7/dist-packages/bottle.py", line 1575, in wrapper
2015-02-25 12:43:57,108 :    rv = callback(*a, **ka)
2015-02-25 12:43:57,108 :  File "/home/bpgergo/bridge/bottle_app.py", line 49, in nevek
2015-02-25 12:43:57,108 :    return template('nevek-obj', nevek=get_nevek_from_db())
2015-02-25 12:43:57,108 :  File "/home/bpgergo/bridge/bottle_app.py", line 38, in get_nevek_from_db
2015-02-25 12:43:57,109 :    result = session.query(Person).all()
2015-02-25 12:43:57,109 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/query.py", line 2241, in all
2015-02-25 12:43:57,109 :    return list(self)
2015-02-25 12:43:57,109 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/query.py", line 2353, in __iter__
2015-02-25 12:43:57,109 :    return self._execute_and_instances(context)
2015-02-25 12:43:57,109 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/query.py", line 2368, in _execute_and_instances
2015-02-25 12:43:57,109 :    result = conn.execute(querycontext.statement, self._params)
2015-02-25 12:43:57,109 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 662, in execute
2015-02-25 12:43:57,109 :    params)
2015-02-25 12:43:57,109 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 761, in _execute_clauseelement
2015-02-25 12:43:57,109 :    compiled_sql, distilled_params
2015-02-25 12:43:57,109 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 874, in _execute_context
2015-02-25 12:43:57,109 :    context)
2015-02-25 12:43:57,109 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1024, in _handle_dbapi_exception
2015-02-25 12:43:57,109 :    exc_info
2015-02-25 12:43:57,109 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/compat.py", line 196, in raise_from_cause
2015-02-25 12:43:57,109 :    reraise(type(exception), exception, tb=exc_tb)
2015-02-25 12:43:57,109 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 867, in _execute_context
2015-02-25 12:43:57,109 :    context)
2015-02-25 12:43:57,110 :  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py", line 324, in do_execute
2015-02-25 12:43:57,110 :    cursor.execute(statement, parameters)
2015-02-25 12:43:57,110 :  File "/usr/local/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 205, in execute
2015-02-25 12:43:57,110 :    self.errorhandler(self, exc, value)
2015-02-25 12:43:57,110 :  File "/usr/local/lib/python2.7/dist-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
2015-02-25 12:43:57,110 :    raise errorclass, errorvalue
2015-02-25 12:43:57,110 :OperationalError: (OperationalError) (2013, 'Lost connection to MySQL server during query') 'SELECT nevek.id AS nevek_id, nevek.name AS nevek_name, nevek.lev AS nevek_lev, nevek.point AS nevek_point, nevek.play AS nevek_play, nevek.kmp AS nevek_kmp \nFROM nevek' ()

PythonAnywhere 的 MySQL 连接在大约 5 分钟后超时,因此您需要配置 SQLAlchemy 以在发生这种情况时重新连接。 This forum thread on the PythonAnywhere site has some examples for Flask, which you may be able to adapt to Bottle, and here are the relevant docs on the SQLAlchemy site.

你能试试吗,然后告诉我

通过使用pool_recycle我想你可以克服它。

http://docs.sqlalchemy.org/en/rel_0_9/dialects/mysql.html#connection-timeouts

替换

engine = create_engine(mysql_connect_string)

来自

engine = create_engine(mysql_connect_string, pool_size=100, pool_recycle=280)

连接超时

MySQL features an automatic connection close behavior, for connections that have been idle for eight hours or more. To circumvent having this issue, use the pool_recycle option which controls the maximum age of any connection:

engine = create_engine('mysql+mysqldb://...', pool_recycle=3600)

解足够小 pool_recycle 参数。不需要使用session.

#web framework imports
from bottle import default_app, route, run, template, redirect

#sqlalchemy engine setup with mysql
mysql_connect_string = 'mysql+mysqldb://%s:%s@%s/%s?charset=utf8' % (db_user, db_password, db_host, db_dbname)
from sqlalchemy import create_engine
#pool recicle set to 1 min
#because the pythonanywhere mysql server will close conecctions in a few mins
#see http://docs.sqlalchemy.org/en/rel_0_7/core/engines.html?highlight=pool_recycle
engine = create_engine(mysql_connect_string, pool_recycle=60)

from sqlalchemy import MetaData, Table, Column, Integer, String, Float, SmallInteger
#sqlalchemy model class for table 'nevek'
meta = MetaData()
nevek_table = Table('nevek', meta,
    Column('id', Integer, primary_key=True),
    Column('name', String(250), nullable=False),
    Column('lev', SmallInteger),
    Column('point', Float),
    Column('play', Integer),
    Column('kmp', Float)
)

#get records from table nevek with sqlalchemy
def get_nevek_from_db():
    result = engine.execute(nevek_table.select())
    return result

#basic handler will redirect to nevek
@route('/')
def hello_world():
    redirect('/nevek')

@route('/nevek')
def nevek():
    return template('nevek-obj', nevek=get_nevek_from_db())