Differences between revisions 3 and 6 (spanning 3 versions)
Revision 3 as of 2008-09-30 14:22:02
Size: 1937
Editor: ZoomQuiet
Comment:
Revision 6 as of 2009-12-25 07:16:42
Size: 1942
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 4: Line 4:
##[[TableOfContents]] <<TableOfContents(1)>>
Line 10: Line 10:
[[Include(ObpLovelyPython/PCS300,,from="^##startInc$",to="^##endInc$")]] <<Include(ObpLovelyPython/PCS300,,from="^##startInc$",to="^##endInc$")>>
Line 12: Line 12:
[[Include(ObpLovelyPython/PCS301,,from="^##startInc$",to="^##endInc$")]] <<Include(ObpLovelyPython/PCS301,,from="^##startInc$",to="^##endInc$")>>
Line 14: Line 14:
[[Include(ObpLovelyPython/PCS302,,from="^##startInc$",to="^##endInc$")]] <<Include(ObpLovelyPython/PCS302,,from="^##startInc$",to="^##endInc$")>>
Line 16: Line 16:
[[Include(ObpLovelyPython/PCS303,,from="^##startInc$",to="^##endInc$")]] <<Include(ObpLovelyPython/PCS303,,from="^##startInc$",to="^##endInc$")>>
Line 18: Line 18:
[[Include(ObpLovelyPython/PCS304,,from="^##startInc$",to="^##endInc$")]]
 * [[Include(ObpLovelyPython/AbtZope,,from="^##startInc$",to="^##endInc$")]]
 * [[Include(ObpLovelyPython/AbtQuixote,,from="^##startInc$",to="^##endInc$")]]
 * [[Include(ObpLovelyPython/AbtDjango,,from="^##startInc$",to="^##endInc$")]]
 * [[Include(ObpLovelyPython/AbtEurasia,,from="^##startInc$",to="^##endInc$")]]
 * [[Include(ObpLovelyPython/AbtUliWeb,,from="^##startInc$",to="^##endInc$")]]
 * [[Include(ObpLovelyPython/AbtWebModules,,from="^##startInc$",to="^##endInc$")]]
<<Include(ObpLovelyPython/PCS304,,from="^##startInc$",to="^##endInc$")>>
 * <<Include(ObpLovelyPython/AbtZope,,from="^##startInc$",to="^##endInc$")>>
 * <<Include(ObpLovelyPython/AbtQuixote,,from="^##startInc$",to="^##endInc$")>>
## * [[Include(ObpLovelyPython/AbtDjango,,from="^##startInc$",to="^##endInc$")]]
 * <<Include(ObpLovelyPython/AbtEurasia,,from="^##startInc$",to="^##endInc$")>>
 * <<Include(ObpLovelyPython/AbtUliWeb,,from="^##startInc$",to="^##endInc$")>>
## * [[Include(ObpLovelyPython/AbtWebModules,,from="^##startInc$",to="^##endInc$")]]

PCS304 Python Web应用框架纵论

  • Web ~ 互联网的泛称;
  • Web应用 ~ 基于互联网的应用简称;
  • 框架 ~ Framework,是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架;(前者是从应用方面而后者是从目的方面给出的定义)
    • 框架是可复用的设计构件库
    • 框架就是某种应用的半成品
    • 框架是一个可复用设计,由一组抽象类及其实例间协作关系来表达的
    • 框架是在一个给定的问题领域内,一个应用程序的一部分设计与实现
    • 框架是为解决代码复用问题的一个最靠谱的尝试
    • 框架是,,,
  • Python Web应用框架 ~ 由Python实现的面向互联网应用开发的框架!
    • 对于开发者,就是一堆模块和使用这些模块的文档,基于这些框架,可以快速的完成有很高品质的互联网应用系统;而不用要求开发者有多么高深的相关领域经验;
    • 可以说,框架就是游戏里的组合技,掌握了,就立即可以从菜鸟变成高手!
  • 不过,在Python 世界,仅仅针对互联网应用的专门框架,就有不下20种!以至有人专门研究:"为何有如此多的Python Web实用框架?"

{{{脚注: 访问地址:http://xlp223.yculblog.com/post.1634226.html 精巧地址:http://tinyurl.com/6yajuo }}}

{{{脚注: 访问地址:http://bitworking.org/news/Why_so_many_Python_web_frameworks 精巧地址:http://tinyurl.com/6kv9j3 }}} 在此文章中,Joe Gregorio 为了展示任何人可以通过Python快速创建自个儿的Web应用框架, 当场使用 8个文件(6个Python脚本,一个页面模板文件,一个服务器脚本)创建并运行了一个含有足够功能的应用框架! 以些来回答了为何有如此多的Python Web实用框架?:

  • 因为,实现一个忒简单了! 接下来,允许笔者将此框架整体展示一下:

Joe Gregorio的超级框架

组成
  1. model.py ~ 数据库设计模板脚本

       1 from sqlalchemy import Table, Column, String
       2 import dbconfig
       3 
       4 entry_table = Table('entry', dbconfig.metadata,
       5              Column('id', String(100), primary_key=True),
       6              Column('title', String(100)),
       7              Column('content', String(30000)),
       8              Column('updated', String(20), index=True)
       9          )
    
  2. dbconfig.py ~ 数据库连接配置脚本

       1 from sqlalchemy import *
       2 metadata = BoundMetaData('sqlite:///tutorial.db')
    
  3. manage.py ~ 服务管理脚本

       1 import os, sys
       2 
       3 def create():
       4     from sqlalchemy import Table
       5     import model
       6     for (name, table) in vars(model).iteritems():
       7         if isinstance(table, Table):
       8             table.create()
       9 
      10 def run():
      11     '''使用WSGI模式启动服务
      12     '''
      13     import urls
      14     if os.environ.get("REQUEST_METHOD", ""):
      15         from wsgiref.handlers import BaseCGIHandler
      16         BaseCGIHandler(sys.stdin, sys.stdout, sys.stderr, os.environ).run(urls.urls)
      17     else:
      18         from wsgiref.simple_server import WSGIServer, WSGIRequestHandler
      19         httpd = WSGIServer(('', 8080), WSGIRequestHandler)
      20         httpd.set_app(urls.urls)
      21         print "Serving HTTP on %s port %s ..." % httpd.socket.getsockname()
      22         httpd.serve_forever()
      23 
      24 if __name__ == "__main__":
      25    if 'create' in sys.argv:
      26         create()
      27    if 'run' in sys.argv:
      28         run()
    
  4. main.cgi ~ 服务器运行脚本

    import manage
    manage.run()
  5. urls.py ~ 基于URL的对象选择器

       1 import selector
       2 import view
       3 
       4 urls = selector.Selector()
       5 urls.add('/blog/', GET=view.list)
       6 urls.add('/blog/{id}/', GET=view.member_get)
       7 urls.add('/blog/;create_form', POST=view.create, GET=view.list)
       8 urls.add('/blog/{id}/;edit_form', GET=view.member_get, POST=view.member_update)
    
  6. view.py ~ 基于WSGI应用的多个视图

       1 import robaccia
       2 import model
       3 
       4 def list(environ, start_response):
       5     rows = model.entry_table.select().execute()
       6     return robaccia.render(start_response, 'list.html', locals())
       7 
       8 def member_get(environ, start_response):
       9     id = environ['selector.vars']['id']
      10     row = model.entry_table.select(model.entry_table.c.id==id).execute().fetchone()
      11     return robaccia.render(start_response, 'entry.html', locals())
      12 
      13 def create(environ, start_response):
      14     pass
      15 def create_form(environ, start_response):
      16     pass
      17 def member_edit_form(environ, start_response):
      18     pass
      19 def member_update(environ, start_response):
      20     pass
    
  7. robaccia.py ~ 模板处置脚本

       1 import kid
       2 import os
       3 
       4 extensions = {
       5     'html': 'text/html',
       6     'atom': 'application/atom+xml'
       7 }
       8 
       9 def render(start_response, template_file, vars):
      10     ext = template_file.rsplit(".")
      11     contenttype = "text/html"
      12     if len(ext) > 1 and (ext[1] in extensions):
      13         contenttype = extensions[ext[1]]
      14 
      15     template = kid.Template(file=os.path.join('templates', template_file), **vars)
      16     body = template.serialize(encoding='utf-8')
      17 
      18     start_response("200 OK", [('Content-Type', contenttype)])
      19     return [body]
    
  8. list.html ~ 页面应用模板

    <?xml version="1.0" encoding="utf-8"?>
    <html xmlns:py="http://purl.org/kid/ns#>">
    <head>
     <title>A Robaccia Blog</title> 
     </head>
    <div py:for="row in rows.fetchall()">
    <h2>${row.title}</h2>
    <div>${row.content}</div>
    <p><a href="./${row.id}/">${row.updated}</a></p>
    </div>
    </html>
使用
  1. 创建数据库

    ~$ python manage.py create
  2. 通过交互环境,初始化数据

    ~$ python
    Python 2.4.3 (#2, Apr 27 2006, 14:43:58)
    [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import model
    >>> i = model.entry_table.insert()
    >>> i.execute(id='first-post', title="Some Title", content="Some pithy text...",  
       updated="2006-09-01T01:00:00Z")
    
    >>> i.execute(id='second-post', title="Moving On", content="Some not so pithy words...",  
       updated="2006-09-01T01:01:00Z")
  3. 独立运行

    ~$ python manage.py run
    Serving HTTP on 0.0.0.0 port 8080 ...
    • 也可以部署前述 main.cgi 到各种Web服务器中运行发布应用
能力

是一个完备的,全功能Web应用框架!通过极少的配置和开发,就可以完成一个动态数据展示页面!

现状

正是因为使用Python 能够轻易的将各种即有模块组合出一个Web应用框架来;

所以,不象其它语言世界,仅仅有唯一或是极少数Web应用框架,,,

在Python 世界,有太多太多的应用框架,以致于大家被选择哪个所深深困惑! 即怕选择错框架,带来开发的麻烦;又怕一但选定了框架,将来享受不到其它框架更加美妙的特性,患得患失中,痛苦渡日;-)

这里行者们根据一些使用过的框架,综合考虑各种因素,大致的给出比较靠谱的Python Web应用框架理解来,以便帮助大家面对异常丰厚的Python Web应用框架,如何保持"平常心",正确对待各种框架;

回顾

按照笔者接触到框架的时间顺序来排列大致是这样的:

  • Zope->Plone->Twisted->Snakelets->CherryPy->Quixote->Karrigell->WukooPy->web.py->Django->TurboGears->Pylons->Eurasia->Web2py->Uliweb

这里没有给出详细的开发社区情况,以及笔者体验框架的详细日期和情景,只是大致的列出先后接触/了解/应用框架的顺序;以此为基础,分享一点,对Python Web应用框架的体验:

  • 各个框架都有鲜明的个性! 关注的问题领域各不相同;
  • 各个框架都有明确的社区, 有专门的开发团队来维护(即使仅仅是一个开发人员);
  • 各个框架都能在不同程度上和其它框架/模块联接使用(这是Python 的胶水特质决定的);
  • 各个框架的模板理解各不相同,甚至于有专门研究模板系统的社区!
  • 模板系统
    • Template ~ 模板,在Web应用开发中,是为了解决应用逻辑和网页表现间的矛盾而产生的!这一矛盾的根本原因在于: 应用逻辑是在脚本代码中制定的,是必须程序化的,动态化的;而网页表现是在HTML代码中定义的,是要求文本化的,静态化的! 而且,应用逻辑和页面表现分离,也有利于设计和开发团队并行,提高网站的开发效率! 所以,模板系统就是一种解析器,可以将类似HTML代码的文本,和应用逻辑代码结合,输出含有动态数据的静态HTML页面!
    • 在Python 中,其实是有内置模板支持的:
    • 但是,这种支持是非常简单的,在一个复杂的功能网站开发中:
      1. 设计团队期望模板系统支持的模板和HTML是非常接近的,可以直接在游览器中观察到效果的(即使没有动态数据嵌入);
      2. 开发团队期望模板系统完成以下基础任务:
        • 可以从模板文件中理解/辨别出显示用的HTML代码
        • 可以将模板文件和实际生成的数据结合,生成输出页面
        • 允许同时处理多个模板
        • 允许模板的嵌套
        • 允许对模板中的某个单独的部分进行处理
        • 用起来要简单
    • 所以,前端模板系统也逐渐成为一个独立的领域,流行模板汇总:
    • 对于Python Web应用框架的模板系统一般分两类:
      • 一则"编译型",事先编译成可执行中间代码,输出时可以直接融合入数据; 这类模板一般是从其它语言继承过来的,一般使用XML或是其它独特的中间数据结构声明文本,以便和HTML的样式模板结合进行编译;
      • 二则"解释型",需要动态加载Python 运行渲染出结果再输出; 这类模板一般允许嵌入Python 语句段,可以更加灵活的组合页面输出;
      • 可能伴随Ajax 技术发展,出现的第三种模板:"JS模板"~完全由JScript 脚本在客户端动态绘制出来! 这方面暂时没有成熟的模板系统可以直接和Web应用框架结合,都需要人工进行开发和调试,暂时不推荐使用;

分类

进一步的,根据前述"Joe Gregorio的超级框架",可以明确一个功能完备的Web应用框架,至少应该包含以下方面的功能支持:

  1. 服务发布
  2. URL分发
  3. 模板支持
  4. 数据库处理

各种框架一般前三个方面都支持的不错,那么可以根据对第4个方面的态度大致将所有Python Web应用框架分成以下几类:

平台性框架
这类框架,认为框架不应该依赖数据库,是关注服务的高效构建和运行平台的稳固;Zope和Twisted属于这类,都是非常深遂的框架!
  • Zope 发布年代之早,开发时间之长,引发的影响之大,甚至于有人认为:"未来互联网就是Zope!";开创了一系列Web应用开发的新技术和理念,形成了宏大的社区和模块树; 虽然学习曲线实在太陡,但是过来人都说:"只要是作Web应用开发,学到底就会发现,其实一切都在Zope 中实现过了!"
  • Twisted 也是老牌社区的作品,关注网络应用底层支持,支持几乎所有网络协议,关注企业级的网络服务构建;只是缺少立等可取的应用框架,一切都得用Twisted 去现实,算是提供了成套的钻石级工具箱!
轻小型框架
这类框架,认为数据库不是应用的必要因素,是关注友好快速的简单任务型网站开发的;CherryPy/Quixote/Karrigell/web.py等等都属于这类框架,标志特性是没有内置的数据库(类似MySQL 的关系型主流数据库)支持模块;
  • 突出的共同特色有:
    • 配置简便
    • 模板系统简单轻便,或是可以自由使用外部模板系统
    • 调试相对方便,有的甚至有专门的问题回溯机制
一站式框架
这类框架,认为数据库是应用的重要因素,是关注如何仅基于本身就可以快速独立的实现一个功能型网站;Django/TurboGears/Eurasia/UliWeb等等都属于这类框架;
  • 突出的共同特色有:
    • 有内置的ORM模块支持数据库的对象化操作;
    • 有内置的事务性功能支持(比如说登录认证);
    • 有高级的模板系统,支持复杂的页面组合,有的甚至于有内置的Ajax 页面动态效果支持;
  • 根据关键功能组件的实现,又分两类:
    • 一则"All in one 式":任何方面功能都是自行开发内置包含的;
      • 胜在内部契合严密,所以运行效率比较好;但是,一但有问题就非常难以清查,而且一般很难作到平滑的版本兼容;吻合框架针对的领域开发起来比较爽直,但是难以定制支持另外的情景;
      • Django 是其中之翘楚;
    • 二则"Mix-in 式":多数方面功能是直接使用第三方模块来完成的;
      • 胜在选择自由,可以使用各种类似的已熟悉的功能组件来替代默认的,学习成本小;而且各个组件可以单独升级,版本兼容危机小;但是,由于要兼顾各种组件接口,隐患要多些,又是通过中间层来进行配合的,数据通过了很多类转发,运行效能可能比较低;
      • TurboGears 是其中经典作品;

选择

现在,虽然可以快速理解现在所有的Py框架,但是谁无法保证不再出现新好框架;那么,如何理性的靠谱的为自个儿的应用选择一个合适的框架?

笔者推荐一个思路
  • 根据项目面的特征问题来进行评定,取舍! 核心问题有:

A. 项目是否需要长期运营?
B. 项目是否足够复杂多变?
  • 判决矩阵:

A

B

 

问题状态

框架选择

Y

Y

平台型框架最佳

Y

N

轻小性框架即可,平台型框架最佳

N

Y

一站式架最佳

N

N

轻小性框架最佳

  • 以上是综合考虑到以下因素,而总结的决策矩阵:
    1. 长期运营时,关键成本在维护,这要求框架足够稳定和容易谅解;
    2. 业务复杂时,关键成本在开发,这要求框架有足够的内置支持,以便减少开发投入;
    3. 平台型框架都有浑厚的积累,可以满足一切要求,但是需要一定的学习成本,以便恰当的运用好这些特性;
    4. 轻小型框架由于代码精悍,一般都有很好的负载能力,而且代码简洁,容易定制和扩展;
    5. 前端模板系统,的选择主要考虑:
      • 编译型模板运行效率要高过解释型;
      • 但是开发时,调试方面则是解释型的比编译型的要直观方便了;
    6. 功能网站的运行效率,很大部分就是和模板效率以及数据I/O处理效率相关!

但是

世事无常,虽然我们反对:"手中有锤子,就将世界看作是由钉子组成的!"

  • 但是,在Python 世界中,咱们这锤子可是金箍棍的种!尽管选择对口的框架可以事半功倍;
  • 但是,毕竟框架是那一群牛人在总结了他们的开发经验后,提炼出的框架;
  • 毕竟,自个儿工作领域不可能百分百的刚好和那群牛人面对的问题域相同;
  • 所以,更多时候,用熟悉的框架,比用流行的框架要靠谱!因为Python 本质特性,是支持我们快速扩展模块,成为全新的工具!UliWeb/Eurasia 以及久远之前,停止开发的 WukooPy(悟空智轮)这些原创框架,都是增补他人的框架厌烦后,产生的全新框架,也都是可以解决一定领域中的需求;

  • 所以,在Python 世界,框架从来不是最重要的,最重要的应该是我们使用 Python 的决心和Pythonic 带来的愉乐感,促使我们可以高效的再生出更加好的工具来!

导读

基于以上对 Python Web应用框架的体验,笔者精选出非常有代表性的框架,特邀专家,分别撰写了技术文章,在此,笔者满怀敬意的先进行小小的导读:

Zope ~ 超浑厚框架
  • 由中华Zope 推广第一人Jungyong Pan 亲笔撰写! 老Pan 对Zope 技术之痴迷到了职业水平,为推广Zope 技术组建了"润普科技"(http://zopen.cn)专职进行基于Zope 的企业信息化解决方案的实施;而且创建了 CZUG~中国Zope 用户组(http://czug.org)翻译/整理了大量的Zope 相关技术文档,为我们学习使用Zope 平台提供了丰厚的资源! 在这篇文章中,可以学习到Zope -- 这一从开始就冲着改造世界这一宏伟目标去的技术平台 -- 的基础思想和发展路线,从而树立信心,敢于跳入这一深海中;

Quxiote ~ 豆瓣动力核心
  • 豆瓣网(http://www.douban.com) 已经成为全球流量最高的纯Python实现网站! 而使用的应用框架 ~Quxiote 却是个轻小型框架,豆瓣团队是怎么作到的呢?! QiangningHong 是豆瓣核心开发成员,由他撰写的介绍文章,从实际的开发体验出发道出了 Quxiote ~ 唐吉诃德,这一个性框架的本质思想和运用技法,非常难得!

Eurasia ~ 关注高性能的原创框架
  • 沈崴 ~ 人称沈游侠,是因为那篇雅俗共赏的妙文:"Python 历史书·GUI 部"
    • 访问地址: http://wiki.woodpecker.org.cn/moin/PyHiStory/PyGuiHistoric

    • 精巧地址: http://bit.ly/3R8nHK

    • 事实上沈崴作为大型IT公司的资深架构师,一直在亲手打造适合自身的应用框架! 仅仅从 Eruasia3的内部名称:Genhiskhan~ 成吉思汗 再结合本身的名称Eurasia(欧亚),就可以知道是有多么的自信和自豪了!这一切是源自StacklessPython 的变态能力,以及开发团队变态丰富的项目经验,再加上沈游侠变态的架构能力,最终将这一恶魔级别的框架带到了人间,大家可以从这篇流畅的短文中体验到 Eurasia 这一源自真实的大规模应用需求的高级框架的思想和个性;-)

UliWeb ~ 关注平衡感的自制框架
  • 这又是一个中国人原创应用框架,作者是Limodou,实际上笔者体验过的各种框架,都是在Limodou 的带领下逐一进行的; Limodou 长期独自在业余时间进行Python 的学习和开发,先后完成了不少作品,其中最成功和知名的就是 UliPad(http://ulipad.appspot.com )~ 这是个纯Python 实现的编辑器,支持各种高级的辅助编程功能; 正是在丰富的开发和应用经验基础上,Limodou 逐渐发现了现有的所有Python Web应用框架,都不是完美的,都有这样那样的缺点;所以,Limodou 决定自个儿写出满足自己所有期望的好框架来,这就是UliWeb 的由来! 而且UliWeb 很大程度上应用了很多在桌面编辑器软件 UliPad 中的开发经验,提供很多命令行的框架应用操控支持,这是其它框架所没有特性;虽然现在UliWeb 还在开发中,但是已经可用,而且形成了独特的应用世界观,大家可以亲自从框架作者的简介中去感受这一世界观;-)

  • Zope 史话

    什么是Zope

    现在很流行轻量级开发框架。在python社区,就有Django、Pylons、Quxiote等框架,简单易学好上手。做一个网站,费不了多少功夫就可以完成了,而且集成了很多最新的AJAX效果。

    但是在某些情况下,我们需要面对更复杂的应用,需要考虑可重用性,需要组织大规模的开发。这时候,这些轻量级框架,可能就存在一些瓶颈了。比如企业级关键业务系统,比如银行交易等。在Java的世界里面,这些被认为是J2EE的专有领地,虽然有很多Java人不喜欢J2EE的过于复杂。

    在Python的世界里面,是否有类似J2EE的企业开发框架?如果有,在以简洁漂亮著称的Python世界里面,他是否也会如同J2EE般的复杂?

    恩,我来告诉你:有的,她就是Zope ( http://zope.org ),一个Python上的应用服务器。她比轻量级的web开发框架来得厚重,但远比J2EE开发简单。

    谁在用Zope?

    Zope由来已久,早在1996年就出现了,因此用户是满天下。包括GE、美国海军、波士顿在线等,都在用Zope.

    开源内容管理系统 Plone (http://plone.org) 是基于Zope开发的,Plone被很多世界500强的公司以及各国政府所使用,包括美国中情局以及Novell等。

    Ubuntu的社区开发站点launchpad(http://launchpad.net), 也是基于Zope开发的。launchpad类似code.google.com,或者sf.net这样的定位。

    在国内,易度互联网在线工作平台,http://everydo.com ,就是基于Zope开发的。这个平台提供了一组集成个人、部门、工作组、产品和项目工作管理的套件产品,让普通企业利用互联网作为基本工作平台成为可能。

    ZOPE: 对象发布环境

    Zope的完整名字是 Z Object Publishing Environment,也就是Z对象发布环境,这里的Z没有什么特别的意义。这个对象发布环境,是Zope的一个关键特性。

    我们来看看一个典型的路径:

    传统的web服务应用,会有一个控制器,将上面的URL路径映射到实际的功能模块。这个是以功能为中心的。

    但是Zope是完全以对象为中心的,也就是说路径中的products、project都是一个个对象,Zope自身负责对象的定位查找和调用。这个过程,也就是所谓的对象发布过程。

    对象发布,是传统Web开发的一个大的变革,她更简单直接。特别是对于内容管理系统,由于内容之间一般存在明显包含关系,这种对象发布模型尤其适合。

    ZODB: 对象数据库

    对象发布环境中,一切都是对象。那么对象如何存储呢?

    如果把对象存放到关系数据库中,一定需要一个O-R映射过程。虽然有很多工具来自动化这个适配过程,但是仍然是存在阻抗的。

    Zope自带了一个对象数据库ZODB(Z Object Database),允许你直接把对象存放到数据库中。使用对象数据库,真正实现了对象数据存取的零阻抗,你根本不会感知到数据存取的过 程!

    ZODB也发展了10年多的时间,现在已经非常稳定,已经很成熟了。ZODB可以脱离Zope独立使用。

    对象发布,配合ZODB,让开发过程异常简单。

    ZCA: 组件架构

    Zope自身是一个复杂的应用。Zope上的其他应用,比如Plone,更加复杂。

    面对复杂应用,我们需要让系统扁平化,减少依赖,降低耦合,提升代码的可重用性。所有这些内容,也是 设计模式 (Design Pattern) 所关注的课题。

    组件开发,正是应对这一需求的应对技术。微软的COM,Mozilla的xpcom,都是著名的组件开发框架。

    Zope也提供了一个组件开发框架 ZCA(Zope Component Architecture),他为Python引入了接口的概念,同时提供了接口注册和查询的机制,使得基于接口而不是实现编程成为可能。

    传统的开发是直接基于类开发,这样,和具体的实现就紧密耦合了。接口是功能的契约,实现同一接口的类可能有很多。通过接口开发更加灵活。

    在ZCA中,最基本的组件是适配器组件。大部分对象之间的关系,都可以用适配器来描述。在现实生活中,比如显卡就是从PCI接口到VGA接口的适配,电源就是220V交流到9V直流的适配,投影仪就是从200V电源和适配信号到投影光信号之间的多适配。基本上可以说,一切皆适配。这也是ZCA的核心理念。

    在适配组件的基础上,ZCA还衍生出工具组件、事件订阅组件等高级组件。这些组件都可以通过XML文件来装配,构成最终的应用。

    ZCA是实践设计模式的最佳技术手段。使用ZCA,你根本不需要太多思考,便可解决依赖问题,你可让设计模式成为一种习惯和标准。同时也让经过设计模式处理过的代码不再怪异,让代码的阅读和维护更加轻松。

    ZCA是Zope总结经验教训的一个产物,也是Zope最有价值的产品之一。ZCA可以脱离Zope使用。其实,ZCA其实更应该说是Python的组件开发框架,现在也已经被twisted等项目采纳。

    由于Zope内核采用ZCA开发,Zope更加可扩展,这也是Zope适合企业级开发的一个原因。

    你可在这里查看ZCA的文档: http://docs.everydo.com/zope3/ca (精巧地址: http://bit.ly/1UCdvX)

    Repoze: 让Zope融入Python世界

    我们一直在说Zope厚重。这种厚重,让Zope在Python世界里很另类。很多习惯了简单的python开发人员,不大爱Zope的这种一眼望不见底。

    Zope的很多的特性,包括认证、对象发布、事务管理、授权等,功能都很强大,但是几乎无法在Zope世界外使用,这样Zope世界显得有些封闭。

    这个现象,其实也不是Zope独自存在的。Python上大量的web框架,大都是各自为政,彼此互通的很少。

    WSGI是解决这一问题的途径。WSGI (Web Service Gateway Interface),定义了Web服务器和Web应用以及Web中间件之间的交互协议。这样,只需要支持WSGI,那么各种web服务器、web应用和中间件,就能相互对接了。比如,你可轻松让你的网站wiki采用MoinMoin,而发布系统采用Plone.

    而Repoze(http://repoze.org) 做了什么了呢?Repoze是一个 "拆卸工",他把复杂/强大的Zope,逐一分解成一个个WGSI组件。这样,Zope基本消失了,Zope的强大特性,可以被Zope外的各种框架所使用。

    目前,Zope的可插拔认证系统、Zope的事务管理、对象发布,均被Repoze给WSGI化重写了。Zope坚硬的外壳,已经被Repoze敲开,营养已经被Repoze所吸收,Repoze太狠了!

    Repoze又推出了自己的开发框架repoze.bfg(http://static.repoze.org/bfgdocs/ 精巧地址:http://bit.ly/1Sz2Ou) ,这个是利用了ZCA的一个可以一眼见底"轻量级"开发框架,和pylons和Django有神似的地方。

    Repoze.bfg实际上是Zope的一个分支,Repoze.bfg未来非常值得期待。虽然现在还处在早期,但是早有蜻蜓落上头,已经有很多应用基于Repoze.bfg开发了。我相信,Repoze是Zope的终极出路,是众望所归。

    Zope曲折史

    Zope发展其实有一段曲折的历程,如同Z字的形状,一波三折。

    Zope第一个辉煌,是突破了传统CGI编程的复杂性,推出了直接通过浏览器进行脚本开发,这大大简化的web开发过程。然而脚本开发存在不方便代码管理的问题,很多开发设计模>式无法用上。大量轻量级开发框架的普及,逐步淡化了这一特性。但是目前在Plone中,直接通过浏览器进行定制,仍然是Plone的关键特性之一。

    Zope第二个辉煌,应该是Plone内容管理系统的流行,Plone的流行,让Zope的用户和开发人员迅速扩展。Plone是基于Zope内容管理框架CMF开发的。

    Zope第三个辉煌,应该是Zope3的推出。Zope3是对Zope从前版本的重写,组件架构ZCA就是在这个版本中引入的。但是由于Zope3面对的是复杂应用,这种曲高导致了和寡。Zope在>整个Python社区并不十分流行。特别是在现在ROR/Django势强,而J2EE势微的年头。

    现在正在走入第四个辉煌,那就是Zope3的轻量级化,让Zope3成为每个开发人员的挚爱。Grok(http://grok.zope.org) ,就是一个尝试,他模仿了ROR的很多概念,开发过程简单>很多,不再需要配置XML文件。前面提到的repoze.bfg,则是另外一个尝试,bfg并不忌讳XML,因为他的配置XML很简单,我个人更看好bfg的前途。

    如何上手?

    Zope的世界太庞大,你困惑了吗?

    恩,如果你喜欢Pylons的简洁,你是一个思维严谨的开发人人员,你希望一切都可操控,那建议你选择repoze.bfg,他会让你满足。

    如果你在开发一个想当复杂的、相当严肃的应用,你还是选择 Zope3吧。Zope 3有几乎所有你想要的东西,该走的弯路,别人都走过了,你可省很多力气。 当然你需要有一个可以相互学习的团队,需要准备一定的入门门槛过程。

    如果你只是想做一个网站,那选择Plone吧,这个是专业级别的,你只需要掌握一些定制技术。

    你根本不做web开发?哦,那你去看看ZODB、ZCA吧,相信他们会对你有益的。

    关于作者

    潘俊勇,易度网(http://everydo.com) 首席架构师,CZUG(http://czug.org) 站长,《Plone定制开发中文指南》主要作者,Zope技术在国内的主要推广者之一。

  • Quixote ~ 豆瓣动力核心

    本文作者

    洪强宁,网名Qiangning Hong或hongqn,2002年开始接触Python,2004年起完全使用Python工作。豆瓣网( http://www.douban.com )02号程序员,技术负责人。

    缘起

    Quixote是由美国全国研究创新联合会(CNRI,Corporation for National Research Initiatives)的工程师A.M.Kuchling、Neil Schemenauer和Greg Ward开发的一个轻量级Web框架。和几乎所有的开源项目一样,Quixote也是为了满足实际需要而出世的。

    CNRI当时在进行一个名为MEMS Exchange的项目( http://www.mems-exchange.org/ )。MEMS是微机电系统的缩写,制造一个MEMS设备往往需要多种制造设备,单个工厂可能无法提供所需的所有设备。因此,MEMS Exchange项目就是要整合起多家制造厂的资源,利用互联网派单和追踪制造过程,形成一个分布式的MEMS设备制造网络。

    起初,他们做了一个Java版的客户端程序提供给用户,但他们发现,没有人愿意使用这个客户端程序,大家还是习惯性的用邮件发送加工过程。最终他们认识到,虽然客户端的表现力更强,功能也更完整,但相比起要下载一个庞大的程序,用户更加愿意使用他们每天面对的浏览器来做事情。于是,他们决定改到web界面上来,要做一个web应用。但是用Java的servlets开发web应用是一件非常低效的事情,所以他们选择了Zope(和现在不同,在1999年,Python的web应用框架没有什么选择的余地,基本上是Zope一家独大)。3个月的开发之后,他们得到了一个运转良好的系统。

    然而,Zope带来的快乐并没有持续多长时间。几个月后,他们想提供更加复杂一点的界面,却发现用Zope写的代码难以维护和调试,在浏览器的文本编辑框里写代码也实在不是什么好的体验。由于当时除了Zope之外也没有什么别的Python web框架,他们决定:自己写一个!在2000年,编写一个新的web框架是类似于向风车挑战一样的事情,开发团队自嘲的用堂吉诃德的名字命名这个框架:Quixote。

    特性介绍

    以下使用Quixote 1.2版本为例进行介绍

    Quixote有以下几个特点:

    1. 简单的URL分发规则

    所有的web框架要解决的第一件事情就是:当用户输入一个URL,应该调用哪段代码来响应用户需求呢?这就是URL分发。与Django之类的基于正则表达式匹配来实现URL分发的框架不同,Quixote是基于URL的目录结构进行逐层查找实现的。

    一个Quixote应用对应着一个Python的名字空间(通常是一个包)。URL的每一级目录映射到一个子名字空间上(模块或者子包),URL的最末一级映射到上一级名字空间中的一个函数上。该函数返回一个字符串,就是返回给浏览器的页面。例如,如果你的应用是放在app这个包下的,那么 http://www.example.com/hello/john 就对应app.hello.john(request)。末级目录则对应于_q_index函数,如http://www.example.com/hello/ 对应于app.hello._q_index(request)。就这么简单。

    这种层级查找的结构还可以带来额外的好处:在目录层进行控制。在每一个目录层级进行查找之前,Quixote都会先运行当前名字空间下的_q_access(request)函数。比如你需要 http://www.example.com/settings/ 下的所有路径都只能由登录用户访问,那么只需要在 app.settings 这个名字空间(app/settings.py,或者app/settings/__init__.py)中定义一个_q_access(request)函数,在其中检查当前用户的登录状态(cookie可以从request对象获得),如果发现是未登录状态,抛出一个 quixote.errors.AccessError 异常即可。

    当Quixote执行代码时碰到异常,它会首先检查当前名字空间下有没有_q_exception_handler函数,如果有的话,则由这个函数处理异常,返回的字符串则为出错页面。如果没有定义这个函数,或者在执行它的时候又抛出了异常,则向上一级的名字空间查找_q_exception_handler函数,直至找到为止。所以,我们一般只需要在app/__init__.py中定义_q_exception_handler函数,就可以方便的实现定制出错页面了(当然你连这个也不定义也可以,Quixote提供了默认的出错页面)。

    2. 易于实现RESTful的URL

    刚才我们说 http://www.example.com/hello/john 对应 app.hello.john(request),那除了john之外,还有成千上万的用户的名字怎么办?不能每个为每个用户都定义一个函数吧?传统的方法是把会变的部分用URL参数的形式传进来,比如 http://www.example.com/hello/?name=bob 。但这种风格的URL既不好看,又不利于搜索引擎收录,要是能都像john一样直接成为URL的一部分多好啊!幸运的是,Quixote帮助你实现了这一点。

    我们只需要在 app.hello 这个名字空间中定义一个 _q_lookup(request, component) 函数,当Quixote查不到 bob 这个名字时就会调用这个函数,把需要查找的名字 bob 传给 component 参数,用这个函数返回的结果作为找到的子名字空间或函数。在我们的例子里,代码就是:

       1 # in app/hello/__init__.py
       2 def _q_lookup(request, name):
       3     def hello(request):
       4         return say_hello(name)
       5     return hello
    

    这里我们返回了一个函数(也可以用定义了 __call__ 方法的类实例代替),这个函数将被调用以生成HTML页面。

    3. 显式标记,拒绝魔术

    Zen of Python中有一句“Explicit is better than implicit”,Quixote也是这个理念的贯彻者。所有可以通过URL访问到的函数和方法,必须在当级名字空间的 _q_exports 变量中列举出来(除了用 _q_lookup 实现的动态URL)。也就是说,要让 http://www.example.com/hello/bob 可以访问,必须在 app/__init__.py 中定义

       1 _q_exports = ['hello']
    

    4. 非常类似Python的模板语言

    呃,或者说,就是Python语言。为了让程序员不用再学习一门模板语言,而是直接使用已经掌握的Python语法写模板,Quixote的模板语言PTL(Python Template Language,以.ptl作为文件名后缀)的语法和Python一模一样(除了后面要说到的[html]标记),在执行了quixote.enable_ptl()之后,这些.ptl文件就可以像普通的.py文件一样作为Python模块import进来。

    但是既然是模板语言,就会有一些专门为了生成字符串的语法糖。在函数定义时,加上一个 [html] 标签就可以改变这个函数的表现,使得每执行一条语句,运行的结果如果不是None的话,就会以字符串的形式添加到函数的返回值里,而无需再使用return语句了。假设hello.ptl的内容如下:

       1 def say_hello [html] (name):
       2     "Hello, "
       3     "<em>%s</em>!" % name
    

    我们来看看say_hello的返回值是什么:

       1 >>> from quixote import enable_ptl
       2 >>> enable_ptl()   # 使得我们可以import ptl文件
       3 >>> from hello import say_hello
       4 >>> print say_hello("Quixote")
    

    因为say_hello函数由两条语句组成,这两行语句的运行结果分别返回一个字符串,因此运行结果就是 Hello, <em>Quixote</em>! 。这个设定可以大大方便模板的编写。

    5. 内置的安全性支持

    互联网上有很多寻找网站漏洞的攻击者,跨站脚本(XSS)是一个常见的攻击手段。这种攻击通常会在页面显示数据的地方插入一段恶意javascript代码,当有人浏览这个页面的时候,就会运行这段代码。比如在say_hello这个例子中,恶意用户输入的名字(也就是name参数)是"<script>alert('haha')</script>"的话,在模板没有安全性处理的情况下,页面就会输出Hello, <em><script>alert('haha')</script></em>,浏览这个页面的用户就会中招。幸好,PTL帮助我们解决了这个问题,它会自动将输入的数据进行HTML转义,就是用&lt;&gt;等HTML实体替换掉危险的<>等字符,这样输出的结果就是Hello, <em>&lt;script&gt;alert('haha')&lt;/script&gt;</em>,安全了。

    PTL是怎么实现这一点的呢?实际上,打了[html]标记的PTL函数中的字符串常量(比如 "<em>%s</em>!")并不是普通的str对象,而会自动转成quixote.html.htmltext对象,这个对象的接口和str几乎一样,但和普通的str对象做操作时(比如+、%等运算),会自动把str对象中的HTML特殊字符进行转义。

    6. 高效的模板

    PTL的速度非常之快,在豆瓣,我们曾经拿它和号称最快的Python模板Mako进行过PK,PTL胜出。当然,这一部分是由于PTL的核心代码是用C写的(比如前面说的htmltext类),另一方面PTL的功能非常基本,完全依赖于Python语言自身的特性。

    7. 没有内置数据库支持

    Quixote专注于URL分发和模板系统,对于数据库没有提供直接支持。开发者可以根据自己的需要选择合适的数据库软件,如SQLAlchemy、SQLObject或者直接使用MySQLdb等。

    快速起步

    那我们来看一个完整的应用示例吧,就是我们前边举的例子:通过URL获得一个名字,在页面上显示对他的欢迎信息。

    首先安装Quixote,在 http://quixote.ca/ 下载 Quixote 1.x版的最新版本(目前是1.2版)并安装。

       1 # app/__init__.py
       2 
       3 _q_exports = ['hello']
    

       1 # app/hello/__init__.py
       2 
       3 from app.hello.hello_ui import say_hello
       4 
       5 _q_exports = []
       6 
       7 def _q_index(request):
       8     return say_hello("everyone")
       9 
      10 def _q_lookup(request, name):
      11     def hello(request):
      12         return say_hello(name)
      13     return hello
    

       1 # app/hello/hello_ui.ptl
       2 
       3 def say_hello [html] (name):
       4     header(title="Hello")
       5     "Hello, "
       6     """<em class="name">%s</em>!""" % name
       7     footer()
       8 
       9 def header [html] (title):
      10     """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
      11 <html xmlns="http://www.w3.org/1999/xhtml">
      12 <head>
      13 <title>%s</title>
      14 </head>
      15 <body>
      16     """ % title
      17 
      18 def footer [html] ():
      19     """</body>
      20 </html>
      21     """
    

    以上我们就已经创建了整个web应用。下面的问题是:怎么运行呢?

    有好几个方式可以用来运行Quixote应用,比如mod_python、FastCGI和CGI等。在豆瓣我们使用的是SCGI ( http://www.mems-exchange.org/software/scgi/ 精巧地址: http://bit.ly/2GMFUb ),这是Quixote的开发团队制作的一个简化版本的FastCGI,使用和web服务器独立的进程运行web应用。著名的轻量级web服务器lighttpd ( http://www.lighttpd.net/ )直接内置支持SCGI,我们下面用lighttpd来运行我们的hello程序。

    首先安装scgi python包:到 http://python.ca/scgi/ 下载最新版本并安装。

    创建SCGI服务程序scgi-server.py

       1 #!/usr/bin/env python
       2 
       3 from scgi.quixote_handler import QuixoteHandler, main
       4 from quixote import enable_ptl
       5 
       6 enable_ptl()
       7 
       8 class MyHandler(QuixoteHandler):
       9     root_namespace = 'app'
      10 
      11 if __name__ == '__main__':
      12     main(MyHandler)
    

    这个程序运行时会在默认的4000端口上开启一个SCGI服务,端口号可以使用命令行参数 -p 更改。更多控制参数请使用 python scgi-server.py --help 查看。

    创建lighttpd的配置文件 lighttpd.conf:

    server.modules = (
            "mod_scgi",
    )
    server.document-root = "."
    server.port = 8080
    
    scgi.server = ( "/" =>
                    ( "localhost" => (
                            "host" => "127.0.0.1",
                            "port" => 4000,
                            "check-local" => "disable",
                            )
                    )
    )

    这个配置让lighttpd监听8080端口,把所有请求都转发给4000端口上的SCGI服务处理,再把处理结果发给用户。

    运行SCGI服务和lighttpd(以linux下为例):

    $ python scgi-server.py
    $ /usr/sbin/lighttpd -f lighttpd.conf

    访问 http://localhost:8080/hello/bob ,就可以看到 "Hello, bob!"了

    需要注意的一点是,对代码进行修改后,需要重启SCGI服务才能看到效果。我们可以做一个简单的重启SCGI服务shell脚本,以简化操作:

    PIDFILE=/var/tmp/quixote-scgi.pid  # 默认的PID文件路径
    
    kill `cat $PIDFILE`
    sleep 1
    python scgi-server.py

    案例讲解

    作为老牌的web框架,Quixote已经被证明了它足够支撑起相当规模的网站。除了MEMS Exchange之外,LWN(Linux Weekly News, http://lwn.net/ )也是使用Quixote搭建的。在国内,Quixote最著名的使用者就是豆瓣网 ( http://www.douban.com/ )。

    豆瓣网是一个致力于帮助用户发现自己可能感兴趣的书、电影、音乐、博客等信息的网站。2005年3月正式上线,当时Python社区的web框架屈指可数,Django、TurboGears等新兴框架还没出现,因此选择了Quixote作为整个网站的基础框架。至2008年10月,豆瓣网已经发展到200万注册用户,每日1000万次动态页面点击,但仍只需要两台Quixote应用服务器即可轻松负担。这充分说明了Quixote的性能和可扩展性。

    小结

    Quixote是一个轻量级的框架,简单,高效,代码也十分简洁易读。用豆瓣网创始人阿北的话来说:“用quixote的时候你的注意力大部分在要实现的内容上,framework不会拦在中间跳来跳去。”

    但是,Quixote毕竟是2000年的框架,现在看来已经略显老态。比起Django、TurboGears这些后起之秀来,确实存在某些劣势。大略有以下几点:

    1. 调试开发不够方便,没有内置的调试用web服务器;
    2. 修改代码后必须重启服务才能看到效果;
    3. URL的末尾带和不带"/"会被解释到不同函数;
    4. PTL模板适合Python程序员编写,却非常不适合美工独立编辑;
    5. 没有内置的WSGI支持;
    6. 没有内置的用户认证系统、数据库支持等等。

    但Quixote最大的优势也在于它的资格够老,基本上已经没有bug。在使用它时不用担心框架会出什么意想不到的问题。而且它只提供了最基本的功能,用户认证系统、数据库访问层等需要自行实现,虽然麻烦一些,但更利于根据你的应用量身定制。如果你的目标是追求稳定和性能,而非追求最新最炫的技术,Quixote仍然是一个很好的选择。

  • Eurasia3

    关注高性能的原创框架

    作者

    • 沈崴, 高级架构师。在 Zoom.Quiet 的胁迫下写下此文, 作为 Eurasia3 的作者, 我很高兴和大家一起走进 Eurasia3 的世界, 去了解 Eurasia3 的历史、其特性, 去感受 Eurasia3 的设计思想。

    概述

    • Eurasia3 高性能应用服务器, 同时也是一套简洁高效的开发框架。除此之外, Eurasia3 项目还开发了对象数据库 MissileDB 和 JavaScript 框架 NJF, 形成了一套完整的开发体系。Eurasia3 应用服务器及其组件基于 Python2.5, 基本上采用 BSD 协议开源发布, 可以用于闭源的商业应用; 也有少数组件使用自由软件的授权协议, 使用时必须遵守自由软件规范。最后, 使用 Eurasia3 是免费的。

    Eurasia 沿革

    • 尽管 Eurasia3 的客户端框架从 1999 年就已经开始设计了, 但是 Eurasia 项目正式出现是在 2004 年。 当时我正致力于人工智能的研发, 现在这套程序已成功发展为国内乃至世界上最大的人工智能应用之一, 值得一提的是它完全是使用 Python 设计开发的。虽然最早我使用 Yacc/Lex 来设计人工智能描述语言, 不过后来我们的团队很快编写了上百万行的人工智能, 这使得原来的编译器不堪重负, 为此我必须修改程序, 将描述语言改成了 Python 的无栈实现。同时带有一整套 PyQT 编写的 IDE 开发环境。 然而接下来, 怎样有效管理如此之多的逻辑数据便成了一大难题, 为此 Plone 和 Zope3 被尝试用来搭建数据平台, 管理界面也从 Tkinter 转到 Web 上来。这产生了 Eurasia 项目, 它基于 Plone/Archetypes、采用 ZODB 对象数据库。Eurasia 使用 NJF 提供 AJAX 操作界面 ── 此时 NJF 开发已近五年。 随后我们使用 Plone 以 Archetypes 的开发速度很快建立起庞大的知识管理、办公系统和信息网络。此后, 我开始考虑将 Plone 用于外网。在当时, Plone 性能不佳是众所周知的事情, 故一般仅用作内部系统。这和 Zope2 的实际性能严重不符, 显然 Plone 复杂的页面用到了太多 IO, 用轻量级的界面替换即可。这样 Eurasia2 就产生了。

      Eurasia2 的产生非常具有戏剧性, 因为在 Eurasia2 设计好后我发现了一个几乎一模一样的东西, 那就是 TurboGears。作为一个非常讨厌重复制造轮子的人, 可以肯定的是如果先发现 TurboGears, 我绝然不会开发 Eurasia2。然而事实却是 Eurasia2 和 TurboGears 不仅理念雷同, 而且几乎是同时开始设计、同时完工, 对我来说这是一个令人震惊的巧合。

      Eurasia2 和 TurboGears 最大的区别是 Eurasia2 采用 Plone 作为后台数据库和管理系统, 这意味着 Eurasia2 可以直接利用 Plone 的权限和工作流, 所以相对地 TurboGears 还是造了太多轮子, Eurasia2 胜出。在 Eurasia 的基础上 Eurasia2 使用了 CherryPy 等轻量级技术, 把 Plone 从内网延伸到了外网, 在诸如招聘系统等公网项目中得到了实用。 2006 年 9 月, 经过讨论, 为回馈开源社区, Eurasia 2.0.2 以 BSD 协议开源。在这一年仿佛是突然被发明出来一样, AJAX 变成了炙手可热的东西, 一下子火了。然而对 Eurasia 而言, AJAX 已是落日余晖, 我清楚地知道一个新的时代即将到来。就在 Eurasia2 发布的同时, 一个新的框架已经从构想逐渐走向现实。 在技术界有些人喜欢发明概念, 比如 Comet, 而另一种人务实行动。在商人还没有来得及提出概念的时候, Eurasia3 出现了。

    Eurasia3 简介

    • Eurasia3 作为 Eurasia2 的下一代技术最初源于一系列关于网页游戏的尝试。我试图在没有插件和 Flash 的情况下, 在浏览器上实现即时类游戏效果, 并维持数万人在线。 漫游地图, 把周边玩家和怪物的行动即时反馈给你 ── 很多人认为单靠浏览器和 HTTP 协议是无法实现的, 你得用 Socket。这没错, 不过别忽略 HTTP 长连接, 事实上它就是 TCP。目前多数框架都支持长连接, 问题在于要维持数万、数十万乃至数百万人同时在线。Asynchronous 单线程循环很合适, 但到了应用层过于复杂, 故多数框架主要使用线程来维持连接状态。一万个长连接就需要一万个线程或进程, 这显得很荒谬。

      轻便线程 MicroThread 可以在单线程中模拟出成百上千万 "看上去像是原生" 的线程, 且每秒可以完成百万级的调度, 正是我们想要的。Eurasia3 便是这种基于 Asynchronous IO 和 Stackless Python 轻便线程的框架, Eurasia3 隐藏了底层细节, 对用户而言 Eurasia3 看上去和传统框架其实并没有什么区别, 就像这样。

       1 from eurasia.web import config, mainloop
       2 def controller(httpfile):
       3     httpfile['Content-Type'] = 'text/plain'
       4     httpfile.write('hello world!')
       5     httpfile.close()
       6 
       7 config(controller=controller, port=8080)
       8 mainloop()
    
    • Eurasia3 带有完备的 Web 开发支持, 除 Response 外还包含表单读取、文件上传、POST 报文读取等接口, 他们都简洁到可以一言以蔽之。

    表单词典       = Form(httpfile)
    文件句柄       = SimpleUpload(httpfile)
    请求头部       = httpfile['Http-Header']
    请求报文       = httpfile.read(size) / httpfile.readline(size)
    • Eurasia3 唯一需要用户自己处理的是 httpfile.path, 即 URL。如何对待 URL, 是个见仁见智的问题, 甚至是许多框架的关键分歧。Eurasia3 采取了更为聪明灵活的策略, 不下定论, 把选择的权利交还给用户。不过这也让 Eurasia3 看上去似乎更像是一个底层框架。事实上 Eurasia3 项目已经模拟出了 CherryPy、Eurasia2 等框架, 以后无论是基于 Pylons、Quxiote 还是 Django 等框架的程序都可以运行在 Eurasia3 上, 并能在一个程序中混用多个框架。 然而最让 Eurasia3 感兴趣的还是去实现一个 Plone, 于是就有了 MissileDB 数据库。在 Eurasia3 中编写数据库应用就像是这个样子。

       1 from eurasia.shelve2 import open, Persistent, BTree
       2 
       3 # 定义持久化对象 (Persistent) User, User 对象可以直接以对象形式保存在数据库中,
       4 # 不需要进行对象关系映射 (Object Relational Mapping), 这也是对象数据库的特点
       5 #
       6 class User(Persistent):
       7     def __init__(self, username, password):
       8         self.username = username
       9         self.password = password
      10 
      11     def hello(self):
      12         print 'Hello Im %s, can I make friends with you?' %self.username
      13 
      14 db = open('test.fs', 'c')       # 创建并打开数据库 "test.fs"
      15 db['user'] = BTree()            # 创建 BTree 结点 "user", 相当于在关系数据库中建表
      16 obj = User('william', '******') # 创建一个 User 对象
      17 db['user']['william'] = obj     # 把 User 对象存入数据库
      18 db.close()
      19 
      20 db = open('test.fs')            # 重新打开数据库
      21 db['user']['william'].hello()   # 访问数据库中的对象
    
    • 基于 Eurasia3 能让你的应用获得数倍乃至数十倍的性能提升。但 Eurasia3 主要还是为高并发和长连接设计的, 所以在这方面的支持要自然得多。下面这个例子将向你展示 Eurasia3 如何与浏览器建立长连接, 并且进行长连接通信的过程。

       1 # -*- coding: utf-8 -*-
       2 html = '''\ # HTML 页面
       3 HTTP/1.1 200 OK
       4 Content-Type: text/html
       5 
       6 <html>
       7 <head>
       8     <title>Comet Example</title>
       9 </head>
      10 <body>
      11 <script language="JavaScript">
      12 
      13 // 待会服务器会远程调用这个函数
      14 function message(msg)
      15 {
      16     confirm(msg);
      17 };
      18 
      19 </script>
      20 <!-- 建立 Comet 长连接, 借助 iframe -->
      21 <iframe src="comet" style="display: none;"></iframe>
      22 </body>
      23 </html>'''
      24 
      25 import eurasia
      26 from eurasia.web import config, mainloop, Comet
      27 
      28 sleep = eurasia.modules['time'].sleep
      29 
      30 def controller(httpfile):
      31     # 输出普通页面 (当 URL 不是 comet 时)
      32     if httpfile.path[-5:] != 'comet':
      33         httpfile.write(html)
      34         return httpfile.close()
      35 
      36     browser = Comet(httpfile)
      37     browser.begin()           # 开始和客户端之间的通讯
      38     browser.message('start')  # 使用原生 Python 代码直接调用浏览器端
      39                               # 名为 "message" 的 JavaScript 函数
      40                               # 发送消息 "start"
      41 
      42     sleep(2)                  # 每隔 2 秒调用一次客户端 message 函数
      43     for i in xrange(1, 3):
      44         browser.message(i)
      45         sleep(2)
      46 
      47     browser.message('finish')
      48     browser.end()             # 断开长连接
      49 
      50 config(controller=controller,
      51     port = 8080, verbose=True)
      52 mainloop()
    
    • 和多数基于事件的 Comet 实现不同, Eurasia3 可以使用原生的 Python 代码, 在任何时候远程调用浏览器上的 JavaScript 函数。

    • Eurasia3 项目的 JavaScript 库 NJF 对 Comet 提供了完善的支持。

    Eurasia3 VS Django

    • 这是一个伪命题, Eurasia3 并不是一种和 Django, 或者 Pylons、Zope 直接竞争的东西。它看上去要更底层一点, 更接近于 Twisted ── 其实 Eurasia3 同样可以用来开发 TCP 服务器。

       1 from eurasia.web import config, mainloop
       2 def echo(sockfile):
       3     while True:
       4         data = sockfile.readline(1024)
       5         if data == 'quit':
       6             sockfile.close()
       7             break
       8         else:
       9             sockfile.write(data)
      10 
      11 config(tcphandler=echo, port=8080) # 与前面不同, 这里使用 tcphandler
      12 mainloop()
    
    • 和 Web 应用一样, 代码出奇地简单, 你看不到任何底层细节, 然而这个 echo 服务器却可以轻松支持数十万用户同时 telnet 上来。简约是 Eurasia3 的设计原则, 它试图以最简单的方式提供最强大的功能。所以 Eurasia3 并不完全像它看上去那样是一个底层框架, 尽管非常简洁, Eurasia3 实际上已经提供了足够多的功能。
    • 完备的 Web 开发支持
    • 多地址、多端口虚拟主机支持
    • 常规请求每秒 8000 次以上的简单动态页面响应能力
    • 数十乃至数百万的同时在线支持, 只要你有足够的内存
    • 长连接 Comet 应用开发支持, 支持以 Python 原生代码远程调用浏览器 JavaScript

    • 无须外部模块程序便可以无修改地运行在 FastCGI 上, Eurasia3 本身就是另一种高效的 FastCGI 框架
    • 高性能 TCP 服务器开发支持
    • 多核 CPU 支持

    开始使用 Eurasia3

  • UliWeb

    作者介绍

    李迎辉,网名Limodou。Python是我的最爱,但我的主业不是Python,它仍然是我的纯业余爱好。喜爱开源事业,虽然不是刻意去做,但是独立完成大大小小的许多Python开源项目,还参与了许多Python项目。最大的项目就是UliPad。而下面要介绍的Uliweb是我在web框架方面的尝试,在做此之前,研究了一段时间Django,并大力进行宣传,还编写过《Django Step by Step》教程。本文的写作一方面是应Zoom.Quiet的邀请,另一方面是为了向大家介绍我对web开发的理解,并把我个人的研究成果介绍给大家。只要你努力,一样可以以自已的方式做出让自已满意的框架。

    概述

    Uliweb是一个新的Python Web Framework,它之所以会产生是因为现有的框架多少有些令人不满意的地方,而且许多情况下这些不满意的地方或多或少对于Web开发有影响,因此在经 过对不少框架的学习之后,我决定开发一个新的框架,希望可以综合我认为其它框架中尽可能多的优点,同时使这个新的框架尽可能的简单,易于上手和使用。不过 这个框架目前主要还是一个人在做,并且是业余在做,所以在进度上相对要慢一些。

    Uliweb按照GPL v2协议开放源代码。Uliweb并不是一个从头开始的框架,它使用了一些较为成熟的库,如:用来进行命令行、URL映射、Debug等核心处理的Werkzeug;用来生成和处理请求、响应对象的webob;强大的ORM库SqlAlchemy等等。Uliweb在开发中还借鉴了象web2py的Template模板模块,Django的一些设计思想和成果。

    功能特点

    • 组织管理
      • 采用MVT模型开发。
      • 分散开发统一管理。采用App方式的项目组织。每个App有自已的配置文件,templates目录,static目录。使得Uliweb的App重用非常方便。同时在使用上却可以将所有App看成一个整体,可以相互引用静态文件和模板。缺省是所有App都是生效的,也可以指定哪些App是生效的。所有生效App的配置文件在启动时会统一进行处理,最终合成一个完整的配置视图。
    • URL处理
      • 灵活强大的URL映射。采用Werkzeug的Routing模块,可以非常方便地定义URL,并与View函式进行绑定。同时可以根据view函式反向生成URL。支持URL参数定义,支持缺省URL定义,如:
        • appname/view_module/function_name
    • View与Template
      • View模板的自动套用。当view返回dict对象时,自动根据view函式的名字查找对应的模板。
      • 环境方式运行。每个view函式在运行时会处于一个环境下,因此你不必写许多的import,许多对象可以直接使用,比如request, response等。可以大大减少代码量。
      • 模板中可以直接嵌入Python代码,不需要考虑缩近,只要在块结束时使用pass。支持模板的include和继承。
    • ORM
      • 支持Model与数据库的简单自动迁移,包括自动建表和表结构的修改。
    • i18n
      • 支持代码和模板中的i18n处理。
      • 支持浏览器语言和cookie的自动选择,动态切换语言。
      • 提供命令行工具可以自动提取po文件,可以以App为单位或整个项目为单位。并在处理时自动将所有语言文件进行合并处理。当发生修改时,再次提取可以自动进行合并。
    • 扩展
      • plugin扩展。这是一种插件处理机制。Uliweb已经预设了一些调用点,这些调用点会在特殊的地方被执行。你可以针对这些调用点编写相应的处理,并且将其放在settings.py中,当Uliweb在启动时会自动对其进行采集,当程序运行到调用点位置时,自动调用对应的插件函式。
      • middleware扩展。它与Django的机制完全类似。你可以在配置文件中配置middleware类。每个middleware可以处理请求和响应对象。
      • views模块的初始化处理。在views模块中,如果你写了一个名为 begin 的函式,它将在执行要处理的view函式之前被处理,它相当于一个入口。因此你可以在这里面做一些模块级别的处理,比如检查用户的权限。因此建议你根据功能将view函式分到不同的模块中。

    • 命令行工具
      • 可以导出一个干净的工作环境。
      • App的创建,会自动包含必要的目录结构,文件和代码。
      • 静态文件导出,可以将所有生效的App下的static导出到一个统一的目录。
      • 启动开发服务器。
    • 部署
      • 支持GAE部署。
      • 支持Apache下的mod_wsgi部署。
    • 开发
      • 提供开发服务器,并当代码修改时自动装载修改的模块。
      • 提供debug功能,可以查看出错的代码,包括模板中的错误。
    • 其它
      • Uliweb是一个Demo与源码合二为一的项目。它不仅包括所有核心代码,还同时包括了 uliwebproject (http://uliwebproject.appspot.com) 网站的源码,同时还有其它的一些Demo,所以你可以直接使用这些代码。

      • 对于静态文件的支持可以处理HTTP_IF_MODIFIED_SINCE和trunk方式的静态文件处理。

    简单入门

    本教程将带你领略 Uliweb 的风采,这是一个非常简单的例子,你可以按照我的步骤来操作。我们 将生成一个空的页面,它将显示"Hello, Uliweb"信息。

    准备

    http://code.google.com/p/uliweb (精巧地址: http://bit.ly/OKSj3) 下载最新版本或从svn中下载最新版本,放在一个目录下。 因为 Uliweb 本身包含 uliwebproject 网站的代码,所以在我们这个简单的例子中其实是不需要的。 它们都存在于apps目录下。一种方式是你将它全部删除,因为我们会创建新的app。另一种方式就是修 改apps下的settings.py文件,只让我们新创建的app生效。再一种方法就是将代码导出到其它的工作 目录下,这样环境比较干净,然后开始工作。这里我们将采用第三种方法。

    创建新的项目

    Uliweb 提供一个命令行工具 manage.py, 它可以执行一些命令。在Uliweb的下载目录下,进入命 令行,然后执行:

    python manage.py export ../uliweb_work

    这里export后面是一个目录,我是将它建在与uliweb同级的uliweb_work下了,你可以换成其它的目录。

    在执行成功后(成功不会显示任何内容),在命令行进入新建的目录,在这个目录下是一个完整的Uliweb 的拷贝,但是没有任何APP存在,所以是一个干净的环境。

    创建Hello应用

    然后让我们创建一个Hello的应用。在uliweb_work目录的命令行下执行:

    python manage.py makeapp Hello

    在执行成功后,你会在apps下找到:

    apps/
      __init__.py
      settings.py
      Hello/
        __init__.py
        settings.py
        views.py
        static/
        templates/

    好了,现在 Hello 已经创建好了。再一步就是如何创建“Hello, Uliweb”了。

    输出"Hello, Uliweb"

    打开 Hello/views.py,你会看到

    #coding=utf-8
    from uliweb.core.SimpleFrame import expose
    
    @expose('/')
    def index():
        return '<h1>Hello, Uliweb</h1>'

    以上几行代码是在执行 makeapp 之后自动创建的。甚至我们都不用写一行代码,已经有一个 Hello, Uliweb 的View函式了。

    @expose('/') 是用来处理 URL Mapping的,它表示将/映射到它下面的view方法上。这样,当用户 输入 http://localhost:8000/ 时将访问 index() 方法。如果一个函式前没有使用expose修饰, 它将不会与任何URL对应,因此可以认为是一个局部函式。

    这里index()没有任何参数。如果你在expose中定义了参数,它将与之对应。但因为这个例子没有定 义参数,因此index不需要定义参数。

    然后我们直接返回了一行HTML代码,它将直接输出到浏览器中。

    启动

    好了,让我们启动看一下结果吧。

    在命令行下执行

    python manage.py runserver

    这样就启动了一个开发服务器。然后可以打开浏览器输入: http://localhost:8000 看到结果。

ObpLovelyPythonEd/PCS300 (last edited 2009-12-25 07:16:42 by localhost)