PyCon作为全球Python爱好者的盛会已在世界各地多个城市举办过活动,本月3号、4号,PyCon首次登陆中国,由GTUG(Google Technology User Group)、华蟒用户组CPyUG(China Python User Group)和TopGeek主办,多位重量级嘉宾一同为国内的Python爱好者带来了一场饕餮盛宴。
在主持人简单介绍大会的主办方、赞助商及志愿者之后,就开始了大会第一天的议程。陈世欣在暖场致辞中从“快、容、易、多、变、全”等多方面介绍了Python,并引用了Python社区的名言:
Life is short (You need python)
人生苦短、我用Python
随后,播放了几段由未能到场的Python大牛为大会录制的视频,包括Python创始人Guido van Rossum,目前在新加坡的上海Python用户组发起人侃嘉KJ,还有台湾中蟒项目的Glaze。Guido在视频中显得格外高兴,很高兴在中国能有这么多人使用Python,如今的Python已经进入Web、社交和移动领域,能够处理TB级别的数据,它还特别提到了国内的豆瓣网,希望明年也能继续在中国举办PyCon。Guido在Python官方网站的主页上也增加了本次PyCon的信息:
First China PyCon
The first PyCon in China will be held in Shanghai December 3-4.
eurasia作者,国内Python界的大侠沈崴首先登场,在《Python ,通向未来之路》中主要回顾了一下历史,他在2000年左右通过boost.python了解了Python,之后便爱上了Python,很喜欢去dohao论坛,还研读了《The Zope Book》(中文版标题《Zope指南》)。当Python从1.x升级到2.0时,他认为这是一个飞跃,而到了2.2时就感觉非常完善了,但随后的每一次小版本升级都能带来不少惊喜。到了3.0时代,兼容性问题尤为突出,Plone就是兼容性问题之集大成者。也有PyPy这样的非标准实现,为提升性能提供了一种可能。
简单对比Ruby和Python之后,他觉得两者相似,并打趣到如果一门脚本语言加了大括号,可以解决一部份人语言里要有大括号和end的需求,也算是造福人类了。Python诞生自1990年,由于发展得非常成熟,很早便步入中年危机,无事可做,开始折腾,进入Web领域,有重量级的Plone把该考虑的问题都考虑了,什么都可以生成,但深度定制能力不够,也有轻量级的组合CherryPy+SQLObject+Kid,还有后来大红大紫的Django、pyramid和国人Limodou开发的Uliweb。
沈崴指出今后异步将成为趋势,先后涌现了Stackless Python、gevent和greenlet等框架;面对多核时代的到来,最终会去掉GIL,方向就是Stackless+JIT-GIL,在新的PyPy中已经实现了Stackless+JIT,去除GIL指日可待。
不管用的人多,还是少,Python都是一门好的语言。
作为国内研究Python框架最多的人,Uliweb的开发者李迎辉(Limodou)对框架有自己的理解,他在《Web框架开发思考与实践 ― Uliweb》中就指出框架的设计更多的是和人本身有关,框架与库不同,框架提供了一个骨架,用户的主动性较少,而库则是用户主动调用的。Web框架就是专注于Web领域的框架,它是一种实践,其中固化了一些可以复用的内容。Web框架与Web应用的差异在于两者的目标、应用范围不同,前者提供通用解决方案,后者则是为了解决个性化业务问题。常见的轻量级框架有web.py、Bottle和Flask,中量级框架有Django、web2py、Uliweb和 pyramid,重量级的则是Zope。
面对框架,不同的人有不同的态度,可以完全接受,也可以自己做,或者先选一种使用,然后自己修改,亦或者做补丁和扩展等。不同的框架本身也有不同的理念,至于为什么会有全功能框架,则是因为问题域本身复杂多变,框架需要能解决大部分需求,自然会变得功能完整,但需要注意的是功能多并不一定就是复杂,全功能往往是一种缺省的实现。
说到如何学习一个框架,Limodou建议要把握核心,有精力的可以读下源代码,了解其设计理念,分析不同框架的相同点,比较不同点,并不断实践。
Uliweb目前版本0.0.1,被网友戏称为是史上最低的版本,Limodou谦虚地表示目前他还想更多地完善框架,等过阵子才会考虑提升版本号。Uliweb的目标是成为全功能Web框架,原则上要使用简单,重点在复用和配置化,要方便扩展和替换,支持标准的部署方式。框架采用了Model-View-Template模式,提供各种常见功能,自带命令行工具。其中最主要的外部核心组件是Werkzeug和SQLAlchemy,最小的开发单位是App,提供了App的依赖定义,这么做的优点是复用比较简单,还有很多特性有待使用者自己发现。
在开发Uliweb的过程中,Limodou也总结出了自己的体会:
用到了一些比较底层的技术;造了不少轮子,真正了解了不少实现细节;框架不是一成不变的,要学会吸收;坚持很重要!
来自Google的侯雍容为大家带来了《用coro-thread技术来扩展Web应用程序栈》,作为使用纯Python的公司,Slide(后被Google收购)从mod_python起步,随后慢慢转移到了Coroutine的平台之上。讲到多线程,由于Python的线程是真实的操作系统线程,由系统管理,再加之GIL的原因,导致Python多线程并不理想。有两种解决方案,一是Stackless Python,其中去除了一些限制,自己实现了线程调度;二是Coroutine,可以使用由Stackless Python派生出来的greenlet,他们也开源了一个基于greenlet的工具包gogreen,其中包含corohttpd、MySQL模块,实现了线程调度。
同样来自Google的叶剑烨介绍了他们的Schemaless数据存储系统,类似于NoSQL数据库,数据存储于MySQL之中,完全由Python实现。之所以要自己实现这套系统,完全是因为当时主流的NoSQL数据库产品都还没有进入人们的实现,没什么好的选择,加之希望能将数据集中存储于MySQL里,还要能支持事务。该数据库的数据结构比较简单,采用了树的结构,每个节点都是树(tree),节点靠边(edge)连接,每个节点都有自己的属性(properties),靠两张表来存储——GraphNode、GraphEdge, 只有6个API。属性是Python的dict,序列化由wirebin(C扩展)实现。
当数据量和访问量上升到一定程度后,可以根据节点ID的高8位进行分片(最多支持256个分片),根据节点ID的低8为进行负载均衡。数据的访问会被负载到Data Access Server上,其中使用了多个coro-threads线程(可以简单地理解为非常轻量级的Python线程,当不是CPU密集型应用时,不用担心程序会失去控制权)的Python进程,这一部分主要基于gogreen实现。此外,还有一套LRU算法的缓存,L1存储节点与边,L2则是数据,通过wireibn序列化并用zlib压缩,当服务器退出时,缓存还会被持久化到磁盘上,这样就不需要预热缓存了。
目前,一台服务器可以同时跑32个服务器实例(同CPU核数),每个实例128个Worker,每个DB建16个连接,可并行处理4096个请求,平均响应时间1.38ms,平均缓存命中率99.72%。折算下来,每个CPU每秒响应2000个请求,每台服务器每秒响应64000个请求。
始于2009年的SAE继PHP之后,即将推出Python支持,PyCon的每位与会者都拿到了SAE Python的邀请码。SAE团队的陈正在《SAE(Sina AppEngine) 的Python 版实现》中详细介绍了SAE中对Python的一些使用细节,例如禁用了os.fork、os.system、os.exec和os.popen,文件系统分为只读和可写目录。SAE选择修改CPython代码来实现沙箱拦截,去除了其中进程、线程相关的宏定义,禁止使用动态扩展,网络上做了白名单及访问频率控制。未来的SAE还会加入Git支持、保护模式下的交互式调试器,还要内嵌pypi,另外也可能会增加一些类似认证、记账这样的基础设施。
豆瓣作为国内最大的Python应用,给了国内的Python开发者巨大的信心,在豆瓣里他们用Python做几乎所有的事情。洪强宁详细介绍了Python在豆瓣的应用。
最早的豆瓣就完全使用Python开发,后台索引基于twisted,GUI使用Quixote,还用到了cElementTree,数据库是MySQL。所以豆瓣天生就有着Python的文化。在豆瓣,开发环境正逐步从Subversion向mercurial迁移,还使用了Python编写的trac和IRC,之所以选择IRC是因为IRC有很大的灵活性,可以编写Bot自动化很多东西。
前端方面,豆瓣使用Mako模板引擎来制作模板,还自己开发了一些东西来自动处理静态文件,比如自动生成静态文件URL、渲染模板时动态inline文件等,另外还计划引入pyScss。移动开发方面,豆瓣基于gevent、bottle和APNSWrapper自己开发了一套APNS Agent。在产品开发上,他们还在使用很老的Quixote 1.2,虽然古老却很简单、快速、稳定,尤其是traversal based特性,让他们能在每个级别加入特定的逻辑,更容易在目录级别进行控制;另外也大量使用了Python的一些语言特性,例如Decorators、Generators、元编程;还开源了OneRing,可以用Web技术来开发桌面应用。技术支持方面,豆瓣基于django-piston、sleepy.mongoose开发了Restful MongoDB Service,提供商业合作方的数据存储接口。QA方面,通过pylint进行静态代码检查、SQL注入漏洞检查、XSS漏洞检查;还基于Django开发了一套上线管理工具,可以自动打tag,确认测试结果,发送上线报告。
豆瓣自己实现了一个MySQL客户端,使用了部分C代码,只支持select,返回一个迭代器,只有数据被访问时才生成Python对象。算法层面,用C++实现算法,boost.py进行数据的加载和初始化,两者结合提供高效灵活的服务。平台部分,豆瓣实现了一个性能剖析工具,可以看到请求中各部分的性能开销;基于thrift实现了一套RPC服务框架DoubanService,用PasteScript实现代码生成,还将Twitter的thrift_client移植到了Python上,提供负载均衡和failover;分布式计算框架部分,也是豆瓣自己实现的Dpark,相当于支持迭代式计算的Hadoop,由Spark演变而来,基于mesos实现资源调度;此外,在豆瓣内部还有一套Douban App Engine,定位为内部的PaaS平台,用virtualenv做依赖关系处理,gunicorn和gevent实现服务器,内嵌了一些最佳实践。
洪强宁表示豆瓣是一个多语言环境,大部分是Python,在某些场景内也会使用C、C++、Go、R、Object-C、Java、C#等各种语言。
Zope中文社区创始人潘俊勇在《易度PaaS云开发平台技术内幕》中为大家回顾了Zope的历史,总结了Zope风光不再的原因,指出框架应该渐进地演变,必须快速适应外界变化,而且得要有简化问题的能力,互联网已经开始影响企业软件了。易度作为一个PaaS平台,主要思路如下:
易度希望打造一个类似Force.com的企业应用PaaS,提供组织架构、人员、权限、表单、流程等功能,在浏览器中就能进行开发、绘制流程图。索引采用xapian,zodb充当数据库,消息日志存储于MySQL中,Redis提供队列、缓存、Session等服务,BlueBream/Pyramid的应用服务器置于Nginx之后。易度提供了一个安全运行沙箱,使用RestrictedPython进行限制恶意代码的运行。演讲结束时,潘俊勇再次推荐大家要去学习Pyramid。
在《系统工程师的非专业课——大规模视频网站的计费与流量管理》中,来自土豆网的黄冬为大家深入讲解了网络带宽、流量与计费相关的知识与“潜规则”。首先,黄冬指明带宽指的是bps(Bits Per Second),而非常用下载软件中出现的Bps(Bytes Per Second),而在运营商处,1Kbps=1000bps,而非1024bps。一般在计算时,一周前的数据按30分钟汇总,2个月后按照2小时汇总,到2年后每个数据点就是1天;常见的5分钟对齐指的是5分钟采集一次数据,8:02发出的包算在8:05的点里。
常用的带宽峰值计算方法有包月包端口和峰值计费法(按月计费,每天得到一个峰值点,月度第n最高峰值计算),商业CDN一般采用95计费法(按月计费,每5分钟得到一个峰值点,月底第95%个最高峰值计费)。带宽数据汇总可以多端口各取峰值,也可以多端口合计峰值,后者总是小于等于前者,计费周期也有自然月与自定义周期之分。在实际环境中的测试显示,通常的带宽损耗率为3%左右,不要盲目相信国内的TCP整形加速说的能从80%提高到90%。
在付费时,要进行财务审计,并且具备真实性、完整性和合法性,结算审计时相同的算法必须得出相同的结果、整体核算差异应该低于3%。他还提出了一些关键事项,例如带宽采集间隔(5分钟采集)、带宽计算方法(5/30/120分钟均值)、峰值计算方法(第n峰值/95计费),尤其是在选择峰值流量计费时,第1峰值和第4峰值计费几乎没有差别,但是第4峰值和95计费的差异基本固定在3%左右,根据统计来看,国内的网民在每天晚上9点、10点,周六及周日上网比较频繁,峰值可能出现在这些时间。
在用户体验上,必须要能进行度量,一般遵循8秒生死线原则,其中首屏的展示时间最为重要。例如,响应时间的度量就有以下几种方法:
应用层负载调度可以通过Dispatcher来实现,客户端访问Dispatcher,要么302转发给具体的服务器,要么返回一组可用服务器列表,由客户端自己调用。要分析带宽究竟被谁占用了,可以进行流量分析,根据服务器列表、HTTP Log和IP库推算出IDC、区域、时段和流量。
黄冬将自己的演讲总结为:用尺度量用户体验、用计费去优化成本、将来源匹配向最合适的服务点、事后度量、事后审计,要用数据说话!演讲最后还做了精彩演示,通过Python程序,结合标准输入输出,分析了一些日志文件。在QA环节,有人问及土豆网如何抵御DDOS攻击,黄冬自信地回答这其实就是一场资源的比拼,相信国内没有人能够DDOS掉土豆网。
Python中文社区创始人周琦(Zoom.Quiet)为大家带来了精彩的《基于MQ解耦应用开发》。之所以要引入MQ是因为碰到过太多复杂的东西,其根源在于业务复杂,也有来自于设计的压力,系统就“被开发、被耦合”了,导致系统代码蔓生。要解决这些问题,就要有勇气去抵抗业务的乱入和乱变。周琦提出的对策基于以下理论假设:
伸缩性与分层无关;事务序列化的范畴与伸缩性有关;绝大部分应用至少使用1次;确保了幂等性,就能很简单地使用消息来协调
在这一理论前提下,就有了MOP(Message-Oriented Programming),常见的邮件提醒功能就是最好的例子,增加一个MQ层,避免发送邮件时的阻塞。在实现上,业务原子化是前提,分为几个层级:
最好先识别出前3种,然后引入MQ来对这些进行支持。此处的M不仅仅是消息,可以是各种东西。MQ中的Queue要有很强的迸发能力(一下子会进入很多请求),内容要有原子性,健康程度可自查(可回收)。
Celery是一个纯Python实现的分布式任务队列,有多种后端可供选择,能与多种框架结合使用,通过gevent和Eventlet实现多进程并发,但配置比较复杂,类似的还有Perl写的Gearman(基于RPC的远程分布式任务调度框架)。而MongoDB则是作为MQ后端的好选择,拥有内存映射机制mmap,内置Capped Collection定长集,有很多文档级的原子操作,支持Javascript。虽然不能直接作为MQ使用,但有纯Python编写的Karait,它采用了MongoDB作为后端,提供跨语言支持,拥有众多特性。
目前的MQ缺少工作状态的监控,数据传输时缺少活动序列管理,灾难响应时缺少灾难迁移,还缺少实时统计。采用了MQ后,实现KISS时可以选择最土的方案,成对维护生产者和消费者,系统里内置测试,要守住Master(MQ就是单点,但单点不是问题,单点HOLD不住才是问题),还要时常重构。
要问Python有多简单?来自网易的林伟(skywind)在《Python游戏开发探索与发现》中告诉大家Python已经足够简化到让策划人员也能写代码,用Python来描述一个游戏的场景。国内WebGame已经超过国外,在网易大量游戏项目用Python相关内容开发。他就网络游戏开发做了展开讨论,首先纠正了一些常见的误区,例如,游戏开发不等于网络编程,不要过分追求单机速度,通常瓶颈在网络IO,广播才是最致命的影响游戏在线人数的问题。Python 2.7的异步模型(epoll、kevent)在内网环境中进行4-5万人的连接管理不成问题,每秒可以响应40-50k的请求。他曾在某项目中将30%用C写的代码,用Python重写,其代码量下降到了10%,且问题更少。他建议:
林伟回顾了网游的一些网络模型,最经典的就是始于1979年的早期MUDOS,采用单机模型,单进程单线程,用文件来存储信息,但这种模型在用户数达到一定数量后就会产生问题,磁盘压力太大;随后是中期的分离化模型,将数据库分离出来,4、5年前的大多数分区网游、RPG和休闲游戏大多采用这一模型;再进一步就是引入模块化,在数据库前增加一个前置,由它来负责缓存、抽象数据访问以及总体控制;还有一些其他的网络模型,例如路由模型、状态模型、分布式对象模型等等。他还介绍了一种模型,在模块化基础上,又增加了更多的层次和模块,虽然功能强大,但过于复杂。
多一台机器,复杂度往往成几何倍数增长!先考虑简单的模型,等人数上千上万,有需要时才考虑重构。
Python之父Guido也指出过:
一个难以向人阐述的实现,往往是一个坏的实现。
普通的聊天服务器,通常可以让Session服务器连接Mirror,如果是同一Session内的可以直接聊天,不同Session则由Mirror转发。战网服务器的实现比较复杂,基于P2P实现,需要有Session服务器、房间服务器、登录交易,网关等等,存在一些安全性问题,需要要引入一套仲裁机制。P2P应该尽量穿透,在无法获取外网IP时可以由服务器转发,一般会有70%的连通率,30%要靠服务器转发,具体的P2P实现可以参考pyp2p。
第二天下午的两场主题演讲更是全与WebGame有关,先是珠三角技术沙龙带头人之一的赖勇浩带来的《Python之于Webgame的应用》, 他先推荐了两个自己比较喜欢的演讲内容,一个是洪强宁的《Python于Web 2.0网站的应用》,另一个则是沈葳的《Python编程艺术》,两人恰好都是大会第一天的演讲嘉宾。赖勇浩在演讲中介绍了Webgame服务器端的技术与一些工具。
在库的方面,他建议库应该与业务逻辑分开存放,lib就放在site-packages中;不要手写配置脚本setup.py,尽量使用工具生成,PasteScript提供了创建、安装、测试、部署、运行等众多功能;可以使用pbp.skels来生成众多代码,比如生成命名空间包;在部署时,一定要有一个纯净的Python环境,这时可以使用virtualenv。
插件方面,主要是善用setuptools,以棋牌游戏为例,游戏插件可以分为接口与实现两部分,接口中主要是房间进程交互、通用功能(例如踢人)、计时器管理、定义接口,实现中主要是实现接口、实现业务逻辑,实现部分可以不接触网络编程,不接触数据库。对插件感兴趣的还可以学习一下Trac Component Architecture。
游戏和I/O有着密不可分的关系,Node.js的作者说过:
I/O needs to be done differently.
他也同意这一观点,只是不太认同Node.js的实现方式,Node.js的改变太多,他认为接口应该尽可能与以前一样,但是底层的实现方式则应该与Node.js类似。从目前来看,Coroutine将成为趋势,可以考虑使用gevent,它在libevent之上通过greenlet提供了一套API。另外也可以参考沈葳的eurasia。
其他方面,赖勇浩建议在通信时使用二进制协议,比如Google的protobuf,他们自己开发了一个名为abu.RPC的通信框架,使用了protobuf、libevent和greenlet,因此可以实现更小的数据量、更快的传输和同步API,支持并行管线和双向调用特性。受到Falcon语言(这是一门开源的多范式语言)的启发,开发了python-message,实现了进程内的发布订阅,应用于任务、邮件、好友等子系统内。此外,还做了些小组件,比如absolute32来实现一些标准库的封装。目前,他所开发的纯Python游戏服务器,单台8核8G内存,可以承受最高1500人同时在线。
另一位演讲者王健在《中型角色扮演网络游戏服务器的Python应用》中同样分享了自己在游戏服务器开发方面的经验,他分析了网游服务器的主要性能瓶颈,基本就是磁盘I/O和网络I/O(Webgame中还有特殊的战斗回放数据存储需求),而且Python无法很好地使用多核CPU。他在实现时使用的架构与林伟在第一天的演讲中介绍的架构类似,由Flash实现客户端,通过网关层(处理网络信息收发与压缩)与游戏服务器(负责游戏主要业务逻辑,读数据库并在启动时全部缓存)交互,在游戏服务器与MySQL数据库中有一个DB前置,负责处理数据的串行写入,MySQL数据库使用MyISAM引擎,不支持事务,效率更高;还有一套管理界面,客户端使用LAMP实现,连接eurasia实现的管理服务器,再由该服务器访问游戏服务器和MySQL数据库。令人意想不到的是这么多的服务都部署在同一台服务器上,采用多进程部署,Socket通讯的方式,大大提升了服务器的利用率,发挥了多核的性能,另外还是用了Psyco和PyPy大大优化了脚本解释的性能,可谓是充分挖掘了单机的性能。
纵览两天的主题演讲,多位嘉宾都提到了Python在多核环境下的多线程问题,认为Coroutine将是发展大方向,Python的多线程弱点可以通过多进程和Coroutine来解决,而在解释器方面,性能也在逐步提升,而且还有PyPy这样的非标准实现可供选择。多个演讲主题都与游戏服务器有关,这从另一个侧面反映了Python在游戏领域的应用已经比较普及,让Python在科学计算以外的游戏领域中占据了一席之地。
大会还有一个快速演讲环节,也有不少有趣的内容,没有参加本届PyCon的同学可以访问大会官网下载讲义,也可以在此观看大会录像。本次大会十分火爆,到场300人,场外有400多人观看现场直播,期待明年的PyCon大会能有更多精彩内容奉献给大家。