September 2009 Archives

CPyUG 09年9月5日会课记录

| 4 Comments
在现场对hdcola讲的大容量高并发信息系统架构话题印象很深,虽然自己没有做过如此规模的程序,但是其中可以让我们学习到的东西有很多,我尽可能讲我所能记住和了解的细枝末节梳理一下。

hdcola构建的这个信息系统,是用于移动网络中的数据处理部分,前后分别连接移动网络的数据中心来接收用户提交的数据以及发出到用户的数据。整个系统不需要与单个的用户客户端直接打交道,但考虑到很多信息通知业务的特性,整个系统对于并发性能的要求很高。

整个信息系统应用的环境是:
  • 五千万用户、数百项服务
  • 网关速度最快发送也只有4千/秒
  • 不同省之间的网关速度不同
  • 不同的业务于服务有着不同的优先级

HD 首先给大家介绍了一下 JMS(Java Message Service) 的系统架构,这个是信息系统处理的标准架构,Sun已经总结成为Sun JMS 规范。

对于关于这个系统软件方案的选择:举例,目前流行的大负载量信息处理方案RabbitMQ,比较吃内存,在极高负载的情况下有阻塞的现象,此时消息无法接收、也无法传递。在底层上也不是十分适合这个应用,比如在传递1000个相同的信息时,就无法作为一次来传递然后分发,而是需要分别传递1000次。不过RabbitMQ这个软件可以应用于诸多领域,是一款通吃型的架构方案,未来前景很不错,只是不适合这个应用。
最终选择了通过Python来自主实现。

2002年的世界杯上,该系统用于发送进球通知短信的业务,使用硬件配置为:内存1G,硬盘带宽和CPU都是非常大的瓶颈,CPU为奔腾2代。

cpyug_hd_archi_c1.png
粗略的架构图

其中各个流程中均有Queue来进行管理,Queue的实现可以参考QMail里面的Queue实现。

信息的存储方式采用了文件系统,没有使用关系型数据库。不同的信息通过目录的形式逐级深入,最终的节点有文件,但是也不存储信息,仅用文件名来存储数据,减少IO需求。这样整个数据存储部分实际上就交给了操作系统的文件系统负责,需要调整性能的话,需要调整操作系统的文件系统参数。格式化硬盘的时候,好像要把文件结点设置大,70G硬盘格式化完以后只有30G了。

cpyug_hd_archi_c2.png
数据存储目录结构

采用的是ufs2文件系统,开始用过zfs,后来发现zfs的特性在rsync上比较好用,在这个项目中实际上用不到,所以就采用freebsd 的默认系统了。

Why not MySQL?
3百万数据需要在一瞬间读取,同时可能会有锁表的问题。整个程序会卡住
使用文件的话,也相当于有行级锁定的功能。
而且在当年的时候,MySQL也没有主辅库的功能。并且MyISM表锁表严重,InnoDB要好一点儿

2002年时的这个系统
单日处理过1300万
订购数超过1000万
9台服务器 其中1台监控
采用Python编写

---

2009年对于此系统的重构

Subscriber Demon (用于存储订阅用户信息)的数据库化(读写分离)
    减少数据库运算负载:每隔两小时,Slave数据库就将数据Dump到一个文件,有其它一个机器通过HTTP协议来进行读取(这个文件压缩后也要有400M),读取使用普通的bzcat命令,dump使用了自己写的py脚本。

Transfer Demon(存储待发送的信息) 的 Cache 内存化
    同时所有的消息同时也有持久性的存储。在硬盘上存储一个打包的数据(所谓的打包,是因为数据都相同,只是给不同的接收者,不是压缩),内存里存放的是单条的数据对应单个接收者。

信息接受(用户发来的订阅信息)和发送分离
    减少了磁盘I/O

2009年时的运行情况
    单日发送5-6千万
    订购关系超过5千万
    只有4台Server(因为单台的性能好过02年太多)
    采用Python编写

---

总结,优化性能的要点
  • 让数据靠近CPU
  • Cache内存化
  • 队列遍历在内存中进行
  • 慢I/O是系统的瓶颈

性能优化的原则
  • 削减重复的计算,让计算前置(非常重要)
  • 集中信息的共性,传递消息包而不是单条信息
  • Task化,减少重复
  • 做重复的事情是愚蠢的,即使它是计算机

---

一个成熟的协议在这个系统中非常重要

长连接 HTTP 1.1 RFC2616
异步并发 MSN协议 ISO8585

cpyug_hd_archi_c3.png
连接队列处理示意

具体实现可参考Google Code上面的 XBayTable 开源项目,由hdcola主导的项目

---

网络能做的更多:底层网络协议可以完成很多东西,二层 CARP  负载均衡等

优秀产品的核心
  • 水泥+鼠标
  • 软件+运营!  --> Microsoft, ThoughtWorks 的劣势
  • 代码+系统
  • 紧密结合

这个系统不超过20K代码,代码越少,出错几率越小

使用了Py, WSGI----httpd的一部分进程自动开成mod_wsgi,节省资源。使用Apache主要是因为追求稳定。
Python模块里使用了asynccore --> 一个原生核心模块

4台普通的PC机从硬件上换掉了JMS+Weblogic+HP小型机+Oracle的系统,硬件投资从3700W变成了40W

总共只有4个TCPIP连接,因为通向网关的发出端口也就只有4个。为什么还能完成如此大量的数据需求,因为协议选的好,这个通讯流程永远保持连接,不会断。

分析日志只使用了awk+grep+uniq,效率很高。

---

整个演讲给了我诸多方面的启发:

进行这样的系统架构设计及实施,一定要了解和深入多方面的知识,而不是懂一门程序语言就能够解决的问题。解决方案往往是融合了操作系统、文件系统、脚本语言、函数库、系统负载与维护、网络协议、网络协议底层、架构设计、计算机硬件体系等诸多的东西。对于这些东西的了解,少了哪一样也不行。

对于细致问题的把握,在高流量的系统中,任何一个细微的部分都可能成为瓶颈,所以也一定要成为考虑的元素。多一个循环或许就是很大的问题了,在编程珠玑的算法优化部分中的O(n),如何减少n是非常重要的因素。

---
将自己思考的一点儿小东西先应用于一个数据库导入脚本的优化上进行了尝试,强化了一下自己对于上面讲到一些基本观点的印象。

这个导入脚本处理十余万条记录,中间经过数据处理,并将其导入一个格式不同的数据库中。

采用Python+SQLAlchemy编写,原始的第一稿性能非常不好,导入千条记录都要几分钟。于是开始着手进行优化。

用Python的cProfile定位性能瓶颈:
  • 起先在没有优化commit 的时候,问题最大的是commit. ---> 因为每个记录添加的时候都需要查另外一个表的记录,如果不符合还要新建。解决方式是将这部分放在内存中进行,最后commit
  • 后来变成了程序本身 ---> 优化循环,因为有很多记录是连续的,循环查比较浪费,在内存里设置了一个Cache,减少循环次数
  • 然后变成了数据库的query ---> 优化数据库query,加入cache,后来整体数据内存化
  • 直到最后的速度达到令人满意的境地

注:Python 的 cProfile 和 youxu 说的一样,只用一行语句就可以实现详尽的 profiling, 太方便了

完全应用了上面提到的性能优化原则:
    让数据靠近CPU --- 这个其实还可以把导入的原始库放在本地,并且采用效率更高的数据库引擎等方法来实现。
    Cache内存化 --- 把数据尽可能放在内存中
    队列遍历在内存中进行
    慢I/O是系统的瓶颈 --- 把读取和写入数据库的次数降到最小

效率:刚写出来时的版本(完全没考虑速度)估计导入全库的时间要10余个小时,更改了数据库操作的方式后,所需时间为原来的1/3,加入内存Cache以后,速度又快了一倍, 最后把所有数据内存化,整个导入完成的速度是6-6.5分钟。目前源数据库和目标数据库都在远程,如果搬到本地,再进行下数据库引擎的优化,应该还有优化的余地。我想用这个简单得不能再简单的小东西来描述和体验一下让程序高效率的原则,还是十分合适的。

---

谈回 CPyUG。我在当天还听了qingfeng关于Tokyo Cabinet的内容,讲得非常好,我记录得也很详细。但是自己没有深入接触过TC,发现记录的很详细的信息在几乎完全不了解的情况下,也无法阐述的十分清晰,怕是难免有误导。于是就先将这部分记录留着,等着日后对这个话题或者其中的一些部分有着更多的了解和认识的时候,再分别做些讲解和学习。还请大家期待。



OpenParty "溪窗听雨"

| 2 Comments
这次的 OpenParty 上自己听到的话题都是期待已久的,所以对于记录这些话题有着精心的准备,随身携带的记录本基本上留住了绝大多数我想要了解的细节。以下的文章包含了我在敏捷项目咨询、X-Moto开源游戏以及wxPython编写的个人财务管理软件三个话题中的如实记录,个别部分是当时演讲话题的细节呈现,整体可能略微缺乏条理,还望大家海涵。如果对于本次OpenParty "溪窗听雨"活动还不甚了解,欢迎您访问本次活动的介绍页面

首先听的第一个话题是ThoughtWorks带来的 一次敏捷的咨询经历。整个话题中牵扯到了一个软件项目开发和管理中的太多方面,同时现场还有不少热心的听众参与讨论,毕竟敏捷是一个十分热门的话题。下面我就根据自己记住的内容分要点罗列一下。

对于项目持续集成的改进:
    实施多阶段的持续集成方案,从底层组件起逐步集成,这种方式适合于大型项目,上百人的团队。

对于项目测试的改进
    自动化测试部分,在该咨询项目中,原本几乎没有什么正规的测试流程,TW的咨询师们首先加入功能测试,选用了Selenium作为解决方案。但是有一个问题是,进行功能测试的部分,应该由谁来撰写?是开发人员?还是测试人员?(在这个咨询案例中,TW认为应该该由开发人员撰写,此问题也引发了在场几位听众的讨论,最终没有一个压倒性的结果,应该说还是根据项目的需要而有所不同)
    通常情况下,自动化的测试流程覆盖了软件测试中的大多数功能,那么测试人员的角色究竟是什么呢?TW的一位测试人员说,通常在测试中会把相关的一个行为作为一个可以被识别并评估的Story,逐一进行测试。测试人员所做的,应该说是从一个真正软件用户的角度,来使用并尝试发现问题。因为程序各种功能的测试,可以说只是最核心的功能实现,但是要注意软件最后打交道的是人,一些需要由人在使用中在识别和认知方面引起的偏差和错误,是必须经过实际使用的测试才能够保证相当高的质量。

程序员管理中暴露的问题:
    程序员是否一直很忙?在这个测试项目中,TW咨询师发现,在开始在客户处工作之后,提交代码最多的居然是TW的人员!而通过查询版本库显示,发现这个100个人的团队中,经常贡献代码的开发人员仅为20个左右,而这些贡献的人的平均代码量仅为10几行/每周!对程序员团队的管理不当,有可能是整个流程存在的最大的隐患。

最终在这个项目中,应用了如下的解决方案:
  • 实践、分阶段的持续集成
  • 测试(Ant, Selenuim, Badboy)
  • 代码规范检查 StateSVN

ThoughtWorks的工作方式,
  • 结对编程,两个头脑并行工作有利于保证工作的高质量。在场很多同仁也纷纷表示,结对编程带来的好处是效率非常高,虽然在形式上看上去是降低了人员利用率,但实际上通过保证高质量所节约的成本是非常显著的。
  • 迭代、日报(给项目经理以上的领导汇报使用)、ShowCase
  • 站立会议:减少会议时间,绝对不拘泥于形式,注重解决问题。
  • 沟通、沟通、再沟通

ThoughtWorks的敏捷原则:
  • 不为敏捷而敏捷
  • 只有领导支持是不够的
  • 敏捷推进必然有组织结构的改变

敏捷的目标,不是实施敏捷。一个问题所需要的解决方案从来不是解决方法本身。在推动一个问题解决的过程中,我们脑海中首先要谨记的是,我们的目标究竟是什么?不为敏捷而敏捷,我们的真正目标是提高效率。而借由这些方法、工具、理念来推动我们工作的过程中,我们原有的一些观点,是要被替换掉的,比如计算程序员的生产力水平的标尺,就不应该再使用代码行数这样的标准了。TW 举了一个例子,在某个咨询项目的过程中,经过两个月的工作,若说代码行数的变动,实际上是负值,因为整个团队在致力于将原来复杂的八个模块重构和精简为四个,大大提高了代码的可维护性,但是这样的工作的成效,就不能简单地用代码行数来衡量的。有一个可以值得考虑的标准是价值点的评估,即完成的这些工作,可以为最终的、客户满意的交付提供多少作用。只要是在朝着面向客户的最终交付这个方向上进行的努力,均可以认为是积极地完成了工作。

可以进行些梳理的是第三条,敏捷作为一个涵盖企业管理的概念,在很多情况下应用起来都会对组织结构的改变提出要求。但是我们应该怎么去作?如何去推动?上来就大刀阔斧地推动是完全不切合实际的,这些改变究竟是不是必须的?我们需要有相应的数据来支持。如果你能够有相应的证据可以表明,现有的一些体制,确实制约了我们在提高效率,和生产力上的努力,那么组织结构上的变化也并不是不可以的。

ThoughtWorks的大牛提到了"响应式设计"这个系统设计理念对于他个人的一个启示,即当所有的需求和限制摆在你面前时,你又尝试去完全考虑他们,那么此题目必是无解的。敏捷问题也是这样,很多时候的很多项目,都存在非常多的问题,但是我们始终要明确目标、以及要做的是什么东西,抓住重点来出发解决。错误是允许的,不允许错误就没有成功,

-----

接下来 Vincent Du 介绍的 X-Moto游戏话题。X-Moto 这个游戏是 Elasto Mania 类型的游戏(商业游戏的最新对应是在Xbox 360 Arcade Live上的 Trials HD),玩法很有趣。

这是一个需要很多技巧的游戏,完全免费,开源。游戏画面的感觉,是很有趣的"皮影戏"感觉,尤其是按下空格键控制摩托转向的时候,在场大家都在感叹:"哇,皮影耶"。游戏画面还有一个更加简洁的Ugly版,适用于低配置的机器。这个版本的画面,完全是用线条来表现的 :)

游戏很细致,我开始一直认为这就是个核心如NES游戏的那种游戏,没想到物理引擎的设计贯穿了整个游戏(虽然物理整个的感觉并不完全要展示显示世界的情况,如在游戏中为了达到很多特殊效果,引力偏小)。规则的设置也很细致,玩家的角色不能碰触任何物体,而摩托车则可以。游戏有着现代竞速和技巧类的游戏都具备的元素,录像功能,Ghost鬼影功能,更有一个成熟的网上社区,玩家可以交流游戏技巧,比拼游戏技术(世界排名),单账户的多点信息同步、更可以交流自制地图。

游戏的地图编辑器技术十分有趣,使用了InkScape 这款自由的矢量绘图软件,游戏的编辑器功能被设计成了此款软件的一个插件。这是一个巧妙的、典型的自由化设计。试想对于游戏设计者来说,只要开发一个小插件,就可以拥有一个用户繁多的全功能编辑器可以使用,而对于用户来说,为一个游戏或软件做出些创意性的设计也变得不再复杂,只需使用自己熟悉的编辑工具即可。

而在这个游戏背后的技术,有一些也值得一提:

SDL 技术几乎已经是跨平台游戏的一种基础技术了,Vincent使用一个实例程序对使用SDL进行了简单的讲解。

ODE(Open Dynamics Engine)是一个开源的物理引擎,不只可以在游戏环境中使用,还可以应用于其它应用领域,如工程模拟等。采用BSD授权。现场展示了一个ODE设计者对于这个引擎的简介slide,有兴趣的朋友可以去看看。有一些商业游戏也使用了ODE引擎,包括 Bloodrayne 2, 还有 Resident Evil: Umbrella Chronicles(来源)

想了解更多关于这个游戏的信息,可以参考这个游戏的网站:X-moto

-----

由JiaKuan朋友带来的"用wxPython开发并理财",这个话题本来就是我关注的重点,因为我打算使用wx来开发一些GUI程序,想要学习些技巧。不过最终这个话题带给我的收获远远不限于wxPython技术,而是多方面的收获。

Jiakuan在开发这个软件之前,已经使用Java技术开发了三个版本,但都遇到了效率、可维护性等的问题。决定开始一个更好的版本。

为什么选择wxWidgets 而不是QT? QT也是不错的选择。进行过简单的比较,感觉wx的原生控件会给用户更好的体验,而且整个库打包起来会比较方便。正是因为存在这些问题,在确定了使用wx库进行开发之后,所要实现的目标都十分简单,使用wxPython版本要实现的几个目标:10M大小以内的安装包 。

接下来JiaKuan分段落讲解了一下整个软件在几个不同部分中遇到的技术问题和解决方式。

程序的国际化部分:
使用了Python的 getText模块
PO/MO 国际化部分翻译 应用程序的字符串,使得程序可以支持多种语言
自己写了一个小脚本在每次发布的时候对翻译的字符串文件进行合并,自动处理好之前存在的翻译,这样每次只需处理新的翻译就可以了

GUI设计
我自己(CNBorn)在开始GUI项目的时候,而令我最为头痛的是wxPython的界面设计。我使用过wxGlade来进行设计,但是真是感觉很难用。虽然去年的Gnome Asia 08峰会上专门有一个话题来帮助大家学习使用Glade,但是时间长了以后,这个软件又变得很难用了。JiaKuan在设计这个软件时,先使用的是Sizer,后来变成了使用XRC方式进行构建,即先使用程序生成XML代码,然后根据XMl代码来生成wx的窗体代码。wxFormBuilder这个工具使得设计工作更为简单直观。使得设计流程更加快捷。

在界面设计上,JiaKuan使用了一个叫做GUI Design的软件来生成界面原型,十分漂亮,不过显然这个软件是个商业软件。

自动化发布
使用了PyInstall来生成发布文件夹,这个工具十分方便,可以自动复制相关的依赖库直接到文件夹里。然后在通过脚本调用来生成安装程序。这整个过程完全来通过脚本执行,完全自动(的确是《卓有成效的程序员》所推崇的观点:让计算机来完成重复的工作。)

封装时的脚本做了一个Hack:有打包和生成的过程中,一些地方需要输出日志信息,但是无法同时在屏幕输出,又向日志文件输出,于是就自己写了个函数,来通过另一个线程来判断日志文件的增量,基本达到同步输出。设计时考虑到可能会有效率问题,但是实际使用中发现不明显。

单元测试
应用了不少的Unittest,主要都集中在程序的核心部分,作为一个需要严谨的技术软件,核心部分的质量必须过硬。一般来讲,先写出单元测试,然后在与其结果进行调试,这种做法符合TDD的要求。单元测试也是逐步增多的。

DB应用及大数据量测试
很多问题只有在大数据量的时候才能够暴露出来,那么如何进行大数据量测试?大数据量有两个来源,一个是JiaKuan细心记账的几年间积攒的数据,还有就是写了一个机器人,基本根据人们消费、收入的习惯生成了一个7万条左右的数据。

数据库使用SQLite。基本上,在大数据量的情况下,程序会暴露出问题,如果没有问题,说明测试可能有问题 :)

在几万条的情况下,有些功能速度明显特别慢。如何优化?优化SQLite,索引的使用很关键,如果你在一个表中建立了多个索引,那么通常其实只有一个索引是有效的,要注意这点。另一个就是在一个非常复杂的查询语句中,尽量保证所有的Condition都有效地运用到索引,这样可以大幅度提高速度。应用了这些之后,软件中有一个查询的速度从39秒提高到0.29秒。

程序中实现了一个简单的ORM,这个并不是本意,本来是打算采用SQL语句进行操作,随后在系统逐渐变化的过程中,数据库操作也逐步复杂,在把相关部分的处理重构以提高复用性以后,就成为了一个简单的ORM,传入的是对象,输出的也是对象。

自动生成站点
生成静态文件供用户展示。采用Maven+Python,使用了自己设计的类似模板的技术来生成静态文件。

接着JiaKuan谈了很多和这个软件理念上的东西。如何利用科学的会计管理方法来管理你个人的财务信息。我简单地记录了如下要点:

在个人会计、理财方面,数据记录是为了分析,而分析是为了观察是否在哪里存在问题。如果你希望通过数字来证明自己的生活水平至少是从帐面上看是逐步提高的,那么一个增长的财务分析曲线可以帮助你更清晰地来看到你目前的情况。从报表统计中发现规律,从而发现问题。

不了解会计怎么办?会计是一种规则,不需要什么创造性和灵活性,只要遵循这个规则来操作就可以了,并不是很难掌握。

还不明白软件中与会计术语相关的条目?软件内建的大众模式可以用普通用户很好理解的模式来录入信息,同时软件将数据映射到与理解会计相关知识的用户使用的专业模式一致,最终都可以生成符合会计原理的报表,同时在接触这些报表中,不太理解的用户可以学习其中更多的概念。

关于这个软件的更多信息,欢迎访问官方网站 : 家宽理财

----

关于每次OpenParty回顾文章规模的问题:我开始使用一个笔记本来记录所有一切我认为值得记录的细节,便于自己回忆和理解。但是写下来之后,发现最终总结到文章的很多东西都是简单的堆砌。一般来讲,自己选择去听的项目都有着相应的准备和充分的理解,所以即使是细节积累的记录,都是于自己比较有意义的。但是我不清楚对于一个对此话题不甚理解的读者,这样的意义是否还重要。这样的方式还有一个缺点就是文章会非常地长,文章会长到我的众多朋友实际上都很少看完我写的整篇文章......

除此篇幅本身的结构以外,我能想象到的一个形式上的改进就是适当分清主次,着重针对一个话题梳理出脉络,随后进行延展,而其它的话题则可以忽略掉一些细节,只着力于主题即可。这样也避免了自己对于某些技术了解的不甚深入(譬如本篇的敏捷话题部分,我于此的实际经验要小于书面上的理解,如果有误欢迎指出),而堆砌细节反而可能弄巧成拙的情况。

自己也考虑在未来多多尝试几种风格,尽量让大家更有效率地获取知识。既不是迷失在字数近乎无限的冗长的细节迷宫中,又不是在精简到渺渺几行的语录体文章中揣摩技术细节的痛苦,这个介于两者之间的程度,我会努力找到更适合的方案。

所以我很希望听听大家的意见,这样的细节大餐或者主次分清的形式你更喜欢哪种?如果有什么更好的建议欢迎提出,谢谢!