webpy+DBUtils是如何将数据库连接数占满的

2014年1月10日 11:41

webpy简单易用,而且非常轻量。这些特点很容易让人忽略它作为web服务器的主要目标,而把它当作一个小模块使用。
现在就把它当作小模块,只使用它操作数据库的api.
 
下面是个小程序,目的就是访问数据库,然后关闭连接。
import web

def main():
    db = web.database(port=5432, host='localhost', dbn='postgres', db='tax',
                      user='postgres', pw='postgres')
    rs = db.query("select count(*) as count from pg_stat_activity")
    print list(rs)[0].count
    db.ctx.db.close()

if __name__ == '__main__':
    main()
在正常情况下,上面程序表现很正常。不会有任何问题.
 
如果变成周期程序,并且安装了DBUtils模块
import time
import web
import psycopg2

def main():
    db = web.database(port=5432, host='localhost', dbn='postgres', db='tax',
                      user='postgres', pw='postgres')
    rs = db.query("select count(*) as count from pg_stat_activity")
    print list(rs)[0].count
    db.ctx.db.close()

if __name__ == '__main__':
    while True:
        main()
        time.sleep(1)
输出的count值不断增加,最终将连接数占满。
 
为什么close没有效果,数据库连接数不断增加?
 
第一个问题:close关闭的是什么?
在有DBUitls时,close关闭时的PooledDB.connection()产生的连接.
在无DBUtils时,close关闭的时psycopg2.connect()产生的连接.
 
第二个问题: 在有DBUtils时close为什么不会减少连接数?
写了段代码,来看看close是怎么回事
import psycopg2
from DBUtils import PooledDB

def main():
    #mincached,maxconnections限制连接池中的连接数
    pool = PooledDB.PooledDB(psycopg2, port=5432, host='localhost', dbname='postgres', database='tax',
                             user='postgres', password='postgres',
                             mincached=2, maxconnections=2)
    # 从连接池中取一个连接
    conn = pool.connection()
    cur = conn.cursor()
    cur.execute("select count(*) as count from pg_stat_activity")
    rs = cur.fetchone()
    print list(rs)
    cur.close()
    # 不会影响这个连接池的连接数量. pool.close()关闭连接池,会关闭所有连接.
    conn.close()

if __name__ == '__main__':
    while True:
        main()
        time.sleep(1)
发现连接数始终不是变,单个连接的close,不会影响到这个连接池。
 
试试将上面cur.close() conn.close()注释掉,会导致连接数增加吗?
答案也是不会. pool是局部变量,变量销毁,连接会自动关闭。
 
得到结论:
* 单个连接关闭,不会影响连接池
* 连接池变量释放,或者程序停止,连接会自动关闭
 
 
可推断webpy中定有全局变量,不释放而导致连接增加.
 
顺着这个思路阅读web源码,发现web.ctx是个TreadedDict类型
class ThreadedDict(threadlocal):
    """
    Thread local storage.

        >>> d = ThreadedDict()
        >>> d.x = 1
        >>> d.x
        1
        >>> import threading
        >>> def f(): d.x = 2
        ...
        >>> t = threading.Thread(target=f)
        >>> t.start()
        >>> t.join()
        >>> d.x
        1
    """
    # 原因就在这里
    _instances = set()

    def __init__(self):
        ThreadedDict._instances.add(self)

    def __del__(self):
        ThreadedDict._instances.remove(self)

    def __hash__(self):
        return id(self)
    ....
原来它有个静态属性ThreadedDict._instances保存每个实例,而每个实例中又保存web.database产生的PooledDB对象.
所以pool一直不释放。
 
所以原因就是: web.database每次新建一个PooledDB对象,而对象又保存在全局变量TreadedDict._instance中.
由于不断新建连接池,又不释放连接池,最终导致连接数不断增加。
 
 
webpy如何解决这个问题?
使用polling参数,禁止连接池
db = web.database(port=5432, host='localhost', dbn='postgres', db='tax',
                  user='postgres', pw='postgres', pooling=False)
 
这上面得出的结论对oracle和其它类型的数据库同样有效.
 
 
 

评论(5) 阅读(6545)

webpy中auto_application的cookie共享问题

2013年11月16日 09:27

在webpy中使用auto_application划分网站的url结构,会发现是个很好用的东西。

使用过程中却发现每个auto_application默认有自己单独的cookie。

范例

#encoding=utf-8
import web
appA = web.auto_application()
appB = web.auto_application()

urls = (
    '/save',   appA,
    '/get',    appB
)

class SaveName(appA.page):
    path = '/?'

    def GET(self):
        web.setcookie("name", "wyq", expires=24 * 60 * 60)
        return web.cookies().get("age")


class GetName(appB.page):
    path = '/?'

    def GET(self):
        return web.cookies().get("name")

app = web.application(urls, globals())

if __name__ == '__main__':
    web.config.debug = True
    app.run()
    #print app.request("/save").data
    #print app.request("/get").data

上面代码,生成两个auto_application appA与appB。

appA处理所有以/save开头的url请求,appB处理所有以/get开头的url请求。

appA = web.auto_application()
appB = web.auto_application()

发送http://0.0.0.0:8080/save请求,将name信息保存在cookie中。

web.setcookie("name", "wyq", expires=24 * 60 * 60)
 
发送http://0.0.0.0:8080/get请求,获取name值。
预想是返回name值,却发现结果为空。什么原因呢?
观察下面从chrome中取到的截图。
/save请求
 
注意上面的Set-Cookie,多了个path=/save/。原来在application中保存cookies时,会设置cookies的path属性。
 
cookie的path属性,用于限制cookie的访问域,只有此域下的请求才能取到此cookie信息。
换句话说只有url以/save/开头的请求才能,取到此cookie的信息。
上面appA属于/save/域下的,appB属于/get/域下的。它们属于不同的域,所以appB访问不到appA中的cookie。
 
如何解决呢?办法就是在设置cookie时添加path="/"的属性,即可解决。
web.setcookie("name", "wyq", expires=24 * 60 * 60, path="/")
 
 
 

评论(6) 阅读(3039)