Results tagged “CPyUG”

CPyUG 09年9月5日会课记录

在现场对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,发现记录的很详细的信息在几乎完全不了解的情况下,也无法阐述的十分清晰,怕是难免有误导。于是就先将这部分记录留着,等着日后对这个话题或者其中的一些部分有着更多的了解和认识的时候,再分别做些讲解和学习。还请大家期待。



1