我的金字塔应用存在严重缺陷

2 投票
2 回答
522 浏览
提问于 2025-04-17 14:27

我最近发现了一个很大的问题,跟我的Pyramid应用程序有关。这个mongo的框架提示我,应该用事件处理器来处理数据库连接。简单来说,就是当有新的请求进来时,连接数据库;当页面渲染完成后,再断开连接。

所以我在我的__init.py__里是这么做的:

def connectDatabase(event):
    mongo = MongoDB()
    con = mongo.connectDatabase()
    db = con[Cfg_MongoDB_Database]
    event.request.con = con
    event.request.db = db
    redis = Redis()
    con = redis.connectDatabase()
    event.request.redis = con
    log.debug('newrequest')

def closeConnection(event):
    mongo = MongoDB()
    mongo.closeConnection(event.request.con) 
    log.debug('newresponse')   

def main(global_config, **settings):
    config = Configurator(settings=settings, root_factory=Dashboard)
    authentication = AuthTktAuthenticationPolicy(Cfg_Auth_Key, hashalg='sha512',\
        include_ip=False, timeout=3600*24*7, max_age=3600*24*7, reissue_time=3600)
    authorization = ACLAuthorizationPolicy()
    config.set_authentication_policy(authentication)
    config.set_authorization_policy(authorization)
    config.add_static_view('includes', 'includes', cache_max_age=3600)
    config.add_renderer(".html", "pyramid.mako_templating.renderer_factory")  
    config.add_route('dash', '/')
    config.add_subscriber(connectDatabase, NewRequest)
    config.add_subscriber(closeConnection, NewResponse)
    log.debug('main')
    config.scan()
    return config.make_wsgi_app()

不过我在连接数据库的方法里加了一些日志,结果发现这个方法被调用了超过10次。如果我在add_static_view里关闭缓存,调用次数超过100次。这就意味着每当请求css、图片或js文件时,都会新建一个数据库连接。这可真是个大负担!网站连接数据库100次居然要300毫秒!

/Users/jan/Documents/Test2/test2/data.py changed; reloading...
-------------------- Restarting --------------------
2013-02-01 18:32:48,351 DEBUG [test2.tools][MainThread] main
Starting server in PID 12374.
serving on http://127.0.0.1:6543
2013-02-01 18:34:14,451 DEBUG [test2.tools][Dummy-2] newrequest
2013-02-01 18:34:14,582 DEBUG [test2.tools][Dummy-2] newresponse
2013-02-01 18:34:14,952 DEBUG [test2.tools][Dummy-3] newrequest
2013-02-01 18:34:14,953 DEBUG [test2.tools][Dummy-3] newresponse
2013-02-01 18:34:17,459 DEBUG [test2.tools][Dummy-4] newrequest
2013-02-01 18:34:17,474 DEBUG [test2.tools][Dummy-4] newresponse
2013-02-01 18:34:17,482 DEBUG [test2.tools][Dummy-5] newrequest
2013-02-01 18:34:17,497 DEBUG [test2.tools][Dummy-5] newresponse
2013-02-01 18:34:19,158 DEBUG [test2.tools][Dummy-2] newrequest
2013-02-01 18:34:19,159 DEBUG [test2.tools][Dummy-2] newresponse

所以目前的解决办法是把连接移到模型里去。但是我怎么知道什么时候可以关闭连接呢?有没有人能给我更好、更优雅的解决方案?

2 个回答

3

在Pyramid框架中,推荐的做法是使用请求属性,而不是使用NewRequest事件。正如你所注意到的,这个事件会在每一个请求中触发,包括静态文件的请求。请求属性是懒加载的,只有在你需要的时候才会被计算出来,而且你可以设置它们在请求的整个生命周期内缓存(也就是保持不变)。下面我提供了一个示例,展示了如何添加request.db

https://raw.github.com/Pylons/pyramid_cookbook/master/database/mongodb.rst

下面是一些更新后的代码,应该可以满足你的需求。

def main(global_config, **settings):
    # ...

    db_url = urlparse(settings['mongo_uri'])
    config.registry.db = pymongo.Connection(
        host=db_url.hostname,
        port=db_url.port,
    )

    def add_db(request):
        db = config.registry.db[db_url.path[1:]]
        if db_url.username and db_url.password:
            db.authenticate(db_url.username, db_url.password)
        return db

    config.add_request_method(add_db, 'db', reify=True)
3

pymongo 自带连接池功能,具体可以查看这个链接:http://api.mongodb.org/python/current/api/pymongo/mongo_client.html#pymongo.mongo_client.MongoClient

使用连接池的好处是,不需要每次请求都新建一个连接。

另外,我觉得你的 pyramid 应用在生产环境中不应该处理静态文件,原因就是这样。像 apache 和 nginx 这些服务器是专门优化来处理文件的,这样你的请求就不需要经过你的应用程序了。

撰写回答