来自使用Nginx和uWsgi的Cherrypy项目中的SQLAlchemy的UnboundExecutionError

时间:2013-06-01 19:54:07

标签: python nginx sqlalchemy cherrypy uwsgi

我目前在Cherrypy项目中使用SQLAlchemy作为ORM。当我在我的网络嵌入式网络服务器上时,我所遇到的错误不会发生,但仅限于我的Nginx部署。

在Debian Wheezy上使用Nginx 1.2.1,但这并不重要,因为我已经在Sqeeze上使用Nginx 1.2.8和1.4进行了打击 - 结果相同!

因此,我使用Nginx& amp;部署我的项目。 uWsgi。我的wsgi_handler.py与我的devserver启动脚本没有区别,但无论如何我在开发模式下都没有错误。

项目结构:

testproj:
        - models.py
        - root.py
        - tools.py //this is a cherrypy tool that I found to bind sqlalchemy session to cherrypy requests
        - devserver.py

        //the production mode files
        - wsgi_handler.py
        - uwsgi.ini

以下是满足我的应用程序要求的Web项目的最小配置:

models.py:

# -*- coding: utf-8 -*-
import logging
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column
from sqlalchemy.types import String, Integer

logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.CRITICAL)

Base = declarative_base()


class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    firstname = Column(String)
    lastname = Column(String)

    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname

tools.py:

# -*- coding: utf-8 -*-
import os
import cherrypy

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker

from models import *


class SAEnginePlugin(cherrypy.process.plugins.SimplePlugin):
    def __init__(self, bus, dburi):
        """
        The plugin is registered to the CherryPy engine and therefore
        is part of the bus (the engine *is* a bus) registery.

        We use this plugin to create the SA engine. At the same time,
        when the plugin starts we create the tables into the database
        using the mapped class of the global metadata.

        Finally we create a new 'bind' channel that the SA tool
        will use to map a session to the SA engine at request time.
        """
        cherrypy.process.plugins.SimplePlugin.__init__(self, bus)
        self.sa_engine = None
        self.bus.subscribe("bind", self.bind)
        self.dburi = dburi

    def start(self):
        db_path = os.path.abspath(os.path.join(os.curdir, self.dburi))
        self.sa_engine = create_engine('sqlite:///%s' % db_path, echo=True)
        Base.metadata.create_all(self.sa_engine)

    def stop(self):
        if self.sa_engine:
            self.sa_engine.dispose()
            self.sa_engine = None

    def bind(self, session):
        session.configure(bind=self.sa_engine)


class SATool(cherrypy.Tool):
    def __init__(self):
        """
        The SA tool is responsible for associating a SA session
        to the SA engine and attaching it to the current request.
        Since we are running in a multithreaded application,
        we use the scoped_session that will create a session
        on a per thread basis so that you don't worry about
        concurrency on the session object itself.

        This tools binds a session to the engine each time
        a requests starts and commits/rollbacks whenever
        the request terminates.
        """
        cherrypy.Tool.__init__(self, 'on_start_resource', self.bind_session, priority=20)

        self.session = scoped_session(sessionmaker(autoflush=True, autocommit=False))

    def _setup(self):
        cherrypy.Tool._setup(self)
        cherrypy.request.hooks.attach('on_end_resource', self.commit_transaction, priority=80)

    def bind_session(self):
        cherrypy.engine.publish('bind', self.session)
        cherrypy.request.db = self.session

    def commit_transaction(self):
        cherrypy.request.db = None
        try:
            self.session.commit()
        except:
            self.session.rollback()  
            raise
        finally:
            self.session.remove()

root.py:

# -*- coding: utf-8 -*-
import cherrypy
from models import User

class Root(object):
    def index(self):
        """
        List users from the DB & add new if no users
        """
        user = cherrypy.request.db.query(User).filter_by(firstname='John').first()
        if user is None:
            user = User('John', 'Doe')
            cherrypy.request.db.add(user)
            return 'Created user: %s %s' % (user.firstname, user.lastname)
        return 'Found user: %s %s' % (user.firstname, user.lastname)
    index.exposed = True

这是有趣的部分,其中我的devserver.py与我的wsgi_handler.py没有太大差别

devserver.py:

# -*- coding: utf-8 -*-
import os, cherrypy
from tools import SAEnginePlugin, SATool
from root import Root

basedir = os.path.abspath(os.path.dirname(__file__))

config = {
            'DEFAULT':  {
                            basedir : basedir,
            },

            'global':   {
                            'server.socket_port' : 8000,
                            'server.socket_host' : "127.0.0.1",

                            'tools.encode.on' : True,
                            'tools.encode.encoding' : 'utf-8',

                            'request.show_tracebacks' : True,
            },

            '/':        {
                            'tools.db.on' : True,
            },
}


if __name__ == "__main__":
    SAEnginePlugin(cherrypy.engine, '/tmp/data.db3').subscribe()

    cherrypy.tools.db = SATool()

    cherrypy.quickstart(Root(), config=config)

wsgi_handler.py:

# -*- coding: utf-8 -*-
import os, cherrypy
from tools import SAEnginePlugin, SATool
from root import Root

basedir = os.path.abspath(os.path.dirname(__file__))

config = {
            'DEFAULT':  {
                            basedir : basedir,
            },

            'global':   {
                            'server.socket_port' : 80,
                            'server.socket_host' : "127.0.0.1",

                            'tools.encode.on' : True,
                            'tools.encode.encoding' : 'utf-8',

                            'request.show_tracebacks' : True,
            },

            '/':        {
                            'tools.db.on' : True,
            },
}


def application(environ, start_response):
    SAEnginePlugin(cherrypy.engine, '/tmp/data.db3').subscribe()

    cherrypy.tools.db = SATool()

    cherrypy.tree.mount(Root(), script_name='/', config=config)
    return cherrypy.tree(environ, start_response)

uwsgi.ini:

[uwsgi]
thread=3
master=1
uid = www-data
gid = www-data
wsgi-file = /var/www/testproj/wsgi_handler.py
chdir = /var/www/testproj
socket = /tmp/uwsgi.sock
logto = /var/log/uwsgi.log

安装应用程序堆栈

sudo apt-get install python-dev python-setuptools nginx
sudo easy_install pip
sudo pip install CherryPy==3.2.2 SQLAlchemy==0.7.10 uWSGI==1.9.11

启动开发服务器

python devserver.py

localhost:8000 gives me the first & the last name of the registered user in my database. If it does not exist, I create it & display it.

使用python uWsgi在Nginx上部署项目:

sudo mkdir -p /var/www
sudo cp -r testproj /var/www/
sudo chown -R www-data:www-data /var/www/testproj

创建nginx配置文件:

的/ etc / nginx的/位点可用/默认值:

server {
    server_name 127.0.0.1;

    listen      80;

    charset UTF-8;
    access_log  /var/log/nginx/access.log;
    error_log   /var/log/nginx/error.log;

    location / {
        uwsgi_pass unix:///tmp/uwsgi.sock;
        include uwsgi_params;
    }
}

重新启动Nginx&启动一个uwsgi守护进程:

sudo service nginx restart
uwsgi --enable-threads --ini /var/www/testproj/uwsgi.ini

这是区别。

On localhost:8000 I get the expected output:

Found user: John Doe

并且

127.0.0.1/

我明白了:

UnboundExecutionError:无法找到在映射器上配置的绑定器Mapper |用户|用户,SQL表达式或此会话

我不明白为什么在开发模式下会话的界限应该如此!

2 个答案:

答案 0 :(得分:1)

wsgi_handler.py脚本错误。您正尝试每次重新初始化整个cherrypy堆栈,而您应该在启动时创建它,然后调用WSGI可调用。

基本上你应该将所有代码移出'application'并将最后一行更改为

application = cherrypy.tree

除此之外,SQLAlchemy还不是fork()友好的,因此您应该始终在每个worker中完全重新加载您的应用程序。添加--lazy-apps就可以了解

答案 1 :(得分:1)

你的SAEnginePlugin.subscribe()永远不会触发start()方法,因为没有启动cherrypy引擎。

wsgi_handler.py的结尾应如下所示:

SAEnginePlugin(cherrypy.engine, '/tmp/data.db3').subscribe()
cherrypy.tools.db = SATool()
cherrypy.tree.mount(Root(), script_name='/', config=config)

cherrypy.engine.start()

application = cherrypy.tree