李迎辉

李迎辉的博客

他的个人主页  他的博客

uliweb中的新session模块

李迎辉  2009年08月27日 星期四 20:06 | 1341次浏览 | 3条评论

经过这几天的努力,我终于写出了自已的session模块,参考了许多东西,包括beaker和django的代码。代码放在lib/weto的目录下。

从设计上,分为三部分:Session,Storage和SessionCookie。配置信息也是分成三部分,分别是:SESSION,SESSION_STORAGE,SESSION_COOKIE。这样的好处是比较清晰。而beaker是混在一起的。

Session类主要完成Session的读取和保存,它其实是提供了一个框架。在创建Session对象时,用户可以传入一个key参数,这个参数是由 外部取出来,现在是从Cookie中来的。在beaker中,你不是传入一个key,而是一个evniron对象,然后去分析其中的 HTTP_COOKIE头。在一般框架的Request对象一般都提供了对cookie的解析,因此这块工作可以不作。所以在weto中,就是传入了一个 key。有了这个key,则执行load方法,去装载真正的cookie数据。Session类本身不会涉及session的处理,它是通过底层的 Storage类来实现的。为了实现可配置化,在Session的初始化参数中有一个type的参数,目前支持:file, dbm, database三种,分别表示不同的Session存储方式。file就是一般的文件,dbm是类字典的文件,database是使用 sqlalchemy模块,可以对不同的数据库进行处理。具体的值会使用cPickle进行序列化和反序列化处理。在Session的类属性中定义了一些 缺省值,可以让框架对其进行修改起到配置的作用。

在load时,如果key不存在,对于采用文件的存储方式的,则不会自动创建文件。这是beaker所不具备的功能。同时会进行加锁处理。对于数据库,采 用了事务的处理方式,并且使用了for_update=True的设置。据说是在mysql和postgres上可以用,不过我没有试过。在处理锁时分别 为:

acquire_read_lock()用来获得一个锁
release_read_lock(lock, True)表示成功时的释放,对文件类型无用,对于数据库类型可以使用db.commit()来完成事务
release_read_lock(lock, False)表示失败时的释放,对文件类型无用,对于数据库类型可以使用db.rollback()来进行事务回滚。

当处理有异常时,会调用release_read_lock(lock, False),如果成功,会返回release_read_lock(lock, True)

如果key不存在,则原session对象中的数据会被清空。

在load之后,就不再对存储进行处理了,直到你调用了save()或delete()方法进行保存。因此你的Request的处理不要执续时间过长。当然在其中你可以在处理中,主动调用load(key)来装载数据,以获得最新的数据。

session本身是一个dict对象,因此可以对其进行类字典的操作。当字典的内容被访问和修改时,都会引起对session对象的访问时间的更改。

当你想要保存session对象的时间,可以调用save()函数。它会将session中的值保存到存储中去,在保存前会先将其使用cPickle进行 序列化。而Session类不会简单的保存,它会判断这个session是否已经被删除,已经被删除的将不会被保存。并且如果你已经删除了一个 session,则再对其内容进行处理时会抛出异常。如果是使用文件方式,在删除时还会同时删除对应的session文件和锁文件。如果session没 有被删除,那么还要检查是否真正有内容,如果有内容则保存。如果没有内容,还要比较值是否发生了变化,也就是说在load成功之后,当时的值是会保存到 session的_old_value中去的,通过比较_old_value与现在值之间的差异就知道是否发生了变化。如果发生了变化,也要保存起来。

在这里,删除标志和对是否要真正保存的判断也正是beaker不足的地方。并且session的访问时间和过期时间都是放在session数据之外的,是单独的属性。这样容易去判断值的变化。而beaker是与session的数据放在一起了,不方便。

在保存时,也要进行加锁的处理。但这时变成了write锁,原理由read锁是一样的。

对于锁本身并不是在Session类中实现的,因为不同的存储方式可能使用不同类型的锁机制,因此它是由Storage类提供的。而是在Session中实现的锁的调用框架。

前面介绍了Session类,那么我们在处理session时还有一个很重要的就是要将生成的session的key保存到cookie中,这样下次就可 以使用了。在beaker中是在Session类中实现的,我认为这样很不方便,但是作为一个不依赖于其它模块的包来说是最方便的做法。在django中 则完全是在middleware中实现的,甚至包括cookie的配置处理。那么在Uliweb中,我定义了一个SessionCookie的类,它也并 不生成真正的cookie代码,但是在类属性中定义了一些配置项,可以方便让框架对其进行修改。每个session对象在创建时,都会生成一个绑定的 cookie对象。这样就允许在运行时,通过request.session.cookie对它的内容进行修改,如:domain, path, expiry_time等。这样用户会有很好的自主性。这一点django做不到,beaker会很麻烦。同时,session在保存会调用 cookie.save()处理,主要是重新设定cookie的expiry_time。这样,如果你没有定义cookie的expiry_time,则 会使用session的值。但如果你对cookie.expiry_time做了修改,则使用你设置的值。

在weto的backends目录下,是目前已经支持的session存储类型。主要功能是从beaker中来的,但基本上只是load()和 save()功能。其中base.py中定义了一个基类,所以有Storage类都应该从base.BaseStorage类派生。每一种类型对应一个文 件名,如:'file'类型对应的应该是backends/file_storage.py, 'dbm'类型对应的应该是backends/dbm_storage.py。每个storage文件中要定义一个从BaseStorage的类,类名必 须为Storage。因为在Session初始化时,会根据类型自动导入相应的模块和类。

在BaseStorage类中定义了一些必须定义的类函数,有关于锁操作的,还有象load(), save(), delete()。

在数据库支持中,beaker仍然使用了文件锁的方式,而weto使用了for_update语句,正象前面说的,没有经过怎么测试。同时还定义了一个配 置项SESSION_STORAGE/auto_create,如果为True,则自动创建session表,否则不创建,由用户自行创建,在 backends/database_storage.py中提供了建表的函数。缺省为True,自动创建表。

为了适应新的weto模块,我修改了contrib/session这个APP,配置项发生了变化,middle_session.py代码发生了变化, 包括:对配置项的处理,和返回response时增加了对删除的session的处理。对于cookie的写入是通过调用 response.set_cookie()函数来完成的,而原来beaker中是直接生成cookie的文本,实现起来比较麻烦。

在beaker中还有一块是加密的cookie值,不过感觉用处不大,在weto中就没有实现它。同时还没有实现原beaker中对memcache和GAE的支持,因为需要环境,不方便测试先做罢。

新的weto经过我的简单测试基本可用,不过可能还存在一些问题和不足,以后慢慢改进。

评论

我的评论:

发表评论

请 登录 后发表评论。还没有在Zeuux哲思注册吗?现在 注册 !
李迎辉

回复 李迎辉  2009年08月31日 星期一 22:32

你是不是有更好的session库的建议?

1条回复

王振

回复 王振  2009年08月31日 星期一 22:26

虽然没打算深入过,但是至少俺们还都是limodou半个徒弟,应该值得期待吧……

0条回复

暂时没有评论

Zeuux © 2024

京ICP备05028076号