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

    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和其它类型的数据库同样有效.

  • 线程里的全局变量threading.local

    通常情况下,子线程可以访问主线程中的变量,并且可以修改它。
    如果线程希望有自己的变量,不允许其它线程修改。这种情况怎么办?
    利用threading.local模块就可以解决这种问题。

    #encoding=utf-8
    from threading import local, Thread, currentThread
    
    threadeddict = local()
    threadeddict.name = "main"
    
    ctx = {"username": "abc"}
    
    
    class LocalThread(Thread):
        def run(self):
            print "-----local-------"
            print currentThread()
            print threadeddict.__dict__   # 可以访问到主线程中的变量,但是访问不了它的内容
            print ctx                     # 变量和内容都可以访问
            threadeddict.name = self.getName()  # 改变值不会影响主线程
            print threadeddict.__dict__
    
    if __name__ == '__main__':
        print "-----main-------"
        print currentThread()
        print threadeddict.__dict__
    
        A = LocalThread()
        A.start()
        A.join()
    
        print "-----main-------"
        print currentThread()
        print threadeddict.__dict__
    

    ctx变量对子线程而言有两个特征:

    1. 全局的(子线程内部都可访问)
    2. 可以访问修改的

    threadeddict对子线程而言只有一个特征:全局的。只剩下一个全局变量名。

  • 计算上月并且返回格式为xx

    要求计算上月,并且返回格式为XX的

    now = datetime.datetime.now()
    last = now - datetime.timedelta(days=now.day)
    print last.strftime('%m')
    

    计算下个月

    import calendar
    import datetime
    
    now = datetime.datetime.now()
    max_days = calendar.monthrange(now.year, now.month)[1]
    next = now + datetime.timedelta(days=max_days - now.day + 1)
    print next.strftime('%m')
    
  • libpq-fe.h:没有那个文件或目录

    安装psycopg2出现这个错误,需要安装postgresql-devel

    sudo yum install postgresql-devel
    
  • Python.h:没有那个文件或目录

    安装包出现上面错误,是因为缺少python-devel

    sudo yum install python-devel
    
  • ssh别名登录

    经常使用ssh远程登录,每次都敲一长串字符。重复多了,有砸键盘的冲动。后来发现可用别名代替。
    在~/.ssh/config 中加入以下内容

    Host mt98   //快捷名
    HostName 135.32.9.98 //主机名
    User monitor  //用户名
    

    然后可用ssh mt98登陆
    运行时出现错误

    ssh: Could not resolve hostname mt98: Name or service not known
    

    config文件需要改成和known_hosts的所有者一致.

  • 错误默认值造成的郁闷

    使用错误的默认值,会掩盖错误,又造成新的错误,最后将错误传入内部。

    # 前台传入三个参数,三个参数都是必需的.
    identifier = web.input().get("identifier", None)   # webpy框架
    year = web.input().get("year", None)
    taxpayer_name = web.input().get("taxpayer_name", None)
    # 传输如None参数,会导致接口出现错误
    result = service.inquiryTaxOwe(identifier, year, taxpayer_name)
    

    前台传入三个参数时,表现正常。

    上面使用默认参数是demo阶段时为了方面。实现时会出现下面的情况.

    如果人来用它,只传入部分参数,结果发现程序有异常(inquirytaxOwe函数内抛出的)。它会找到实现接口的人,告诉你你的程序有问题。这个时候你怎么解释? 异常是里面一层抛出的,这一层可不管调用者的事,调用者只负责传参数,然后拿到结果。这时你会发现多么的郁闷。说不是你的责任,但是有你一分。说不是调用者的责任,实际也有它一分。

    换一种方式

    identifier = web.input().identifier
    year = web.input().year
    taxpayer_name = web.input().taxpayer_name
    result = service.inquirytaxOwe(identifier, year, taxpayer_name)
    

    在不传入参数时,会及早发现,不会再有上面的郁闷了.

  • SimpleHttpServer出现unicodeDecodeError

    在win8系统下运行 python -m SimpleHTTPServer 出现Traceback (most recent call last):

      File "D:\Python27\lib\runpy.py", line 162, in _run_module_as_main
        "__main__", fname, loader, pkg_name)
      File "D:\Python27\lib\runpy.py", line 72, in _run_code
        exec code in run_globals
      File "D:\Python27\lib\SimpleHTTPServer.py", line 27, in <module>
        class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
      File "D:\Python27\lib\SimpleHTTPServer.py", line 208, in SimpleHTTPRequestHand
    ler
        mimetypes.init() # try to read system mime.types
      File "D:\Python27\lib\mimetypes.py", line 358, in init
        db.read_windows_registry()
      File "D:\Python27\lib\mimetypes.py", line 258, in read_windows_registry
        for subkeyname in enum_types(hkcr):
      File "D:\Python27\lib\mimetypes.py", line 249, in enum_types
        ctype = ctype.encode(default_encoding) # omit in 3.x!
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xb0 in position 1: ordinal
    not in range(128)
    

    在import SimpleHTTPServer时,同样出现上面的错误.
    在import sys后面加上下面语句

    reload(sys)
    sys.setdefaultencoding('gbk')
    
  • Building the main Guest Additions module [失败]

    在安装virtualbox的增强功能时出现错误

    Building the main Guest Additions module [失败]
    

    查看日志找到原因,缺少依赖

    Uninstalling modules from DKMS
    Attempting to install using DKMS
    
    Creating symlink /var/lib/dkms/vboxguest/4.3.6/source ->
                     /usr/src/vboxguest-4.3.6
    
    DKMS: add completed.
    Error! echo
    Your kernel headers for kernel 3.12.5-302.fc20.x86_64 cannot be found at
    /lib/modules/3.12.5-302.fc20.x86_64/build or /lib/modules/3.12.5-302.fc20.x86_64/source.
    Failed to install using DKMS, attempting to install without
    /tmp/vbox.0/Makefile.include.header:97: *** Error: unable to find the sources of your current Linux kernel. Specify KERN_DIR=<directory> and run Make again。 停止。
    Creating user for the Guest Additions.
    Creating udev rule for the Guest Additions kernel module.
    

    安装缺少的模块

    sudo yum install dkms kernel-devel
    

    应该是在编译一些东西,可能需要先安装gcc

    验证是否成功

    sudo /etc/init.d/vboxadd setup
    
  • 令人不爽的数据检查

    每当看到,用if检查数据,正确则把参数参入xxx函数执行,总是很不爽.
    不仅感觉难看,而且使逻辑混乱.
    数据合法性判断是函数自身的职责,如果将这个职责移到外面,它就是一个污染.
    使用这个函数的地方越多污染的也越多.