技术宅,fat-man

增加语言的了解程度可以避免写出愚蠢的代码

导航

webpy框架:db模块分析

Webpy

是python的轻量级web框架

 

源码位置(ubuntu10.10)
/usr/local/lib/python2.7/dist-packages/web/db.py

DB
 - MySQLDB
 - PostgresDB

定义DB是为了抽取出一个统一的API屏蔽掉不同数据库API的差异

1,程序执行时,创建一个字典并注册若干数据库操作类(写入字典)


class MySQLDB
class PostgresDB
...
_databases = {}
def register_database(name, clazz):
_databases[name] = clazz

register_database('mysql', MySQLDB)



2,调用函数database,传入数据库类型以及连接信息,从字典_databases查找操作类并实例化一个对象出来,这个就是我们代码中的db

db = database(dbn='mysql', user='root', pw='dh6svp132e', db='test')

def database(dburl=None, **params):
dbn = params.pop('dbn')
if dbn in _databases:
return _databases[dbn](**params)


3, db对象的构建流程

class MySQLDB(DB):
def __init__(self, **keywords):#1
import MySQLdb as db
DB.__init__(self, db, keywords)

class DB:
def __init__(self, db_module, keywords):#2
self.db_module = db_module
self._ctx = threadeddict()
#基于线程存储的字典,这个类被重载,不一定按照字典的语法使用

def _connect(self, keywords):
return self.db_module.connect(**keywords)

def _load_context(self, ctx):
ctx.db = self._connect(self.keywords)#5
#等价于 ctx['db'] = MySQLdb.connect(user='root', pw='dh6svp132e', db='test')

def _getctx(self):#4
#看看当前线程的字典里有没有定义_ctx['db']
if not self._ctx.get('db'):
self._load_context(self._ctx)
return self._ctx

ctx = property(_getctx)#3
#每次使用DB.ctx属性时就执行_getctx



 

4, SQL执行

db.update("table",...)

class DB:
def _db_cursor(self):
return self.ctx.db.cursor()
#执行_getctx函数,如果线程之前有执行过SQL操作就会定义db,直接使用它,否则创建一个新的db,用db创建游标对象

def _db_execute(self, cur, sql_query):
out = cur.execute(query, params)

def update(self, tables, where, vars=None, _test=False, **values):
db_cursor = self._db_cursor()
self._db_execute(db_cursor, query)



总结:
db 是属于进程的全局变量,虽然各个线程都会使用db操作数据库,但是db却是安全的,因为db._ctx对象是一个基于线程存储的字典,线程A在操作数据库 时需要一个数据库操作对象,它会去db._ctx里检查有没有属于自己这个线程的db,有就使用它,没有就创建一个,并以字符串'db'作为key,新建 的连接对象作为value存进db._ctx,因为这个key只在线程A里可见,线程B需要操作数据库的时候也会自己建立一个数据库连接对象,因此不同线 程的数据库操作就被隔离开(很像单例模式)

推测:
webpy是MVC框架,用户的请求都可以映射到一个函数上,而 每次用户的一个请求都启了一个新的线程来执行,由这个线程来调用这个被映射的函数,而这个函数里可能写了大量的数据库操作,体现在代码里就是很多的 db.insert(...),db.update(...),db.delete(...)这种操作,只有执行第一个函数调用的时候,我们才创建一个真 正的数据库连接对象,之后的函数调用都是复用这个连接,等这个函数执行完毕,线程销毁或者回归到线程池,如果线程销毁则_ctx.db也会销毁,如果是回 归线程池,则必然有_ctx.db.close(),然后.ctx.clearAll()的操作

 

webpy的threadeddict实现
import threading
class ThreadedDict(threadlocal):
_instances = set()

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

threadeddict = ThreadedDict

posted on 2012-01-13 18:25  codestyle  阅读(1977)  评论(0编辑  收藏  举报