三 核心模块图
习武之人往往追求一个高境界,但最后都归结到“德”上,由武人转变为哲人。“以无法为有法,以无限为有限”,“无招胜有招”等等,都是他们最高追求。我们写代码的人不也是一样么?曾经对GOF设计模式,boost等甚是追求,追求着追求着,慢慢发现这些只不过是一种具体的技术(应该说里面还是有很多思想的成分),只要你找到解决问题的方法,他们就像公式一样,套过来就可以用了,而那个方法是比较难找的。核心模块的设计往往不会涉及到具体的代码技术,它更多的是体现设计者的思想。
举个实际点的例子来说明我的意思吧。在设计大中型终端程序时,人们往往会用到MVC这种编程模式。当年C++基本的语法还有问题时,看了一遍又一遍的MVC文章,看了似乎明白了,可过一段时间又忘了,再看再忘,呵呵。也不知道自己是什么时候搞懂了它,再也不会忘了。M就是用来管理数据的,V就是用来显示数据的,C就是用来做交互控制的,这样做的目的就是将数据和显示分离,降低耦合度,如此简单(其实要真正做到也挺复杂的)。那么MVC没有一行具体的代码,它只是一种Xerox PARC提出的一种设计模式,也就是一种思想。MVC在各种开发语言中被广泛应用,足可见思想的威力。在我面试五年工作经验的人时,鲜见能清晰地将MVC表述出来,这就让我很担心他们是否能真正做好终端,或者说开发出的产品能否经得住2年的时间考验。
如果说MVC只是某个局部的设计思想,那么CDN可以说是更大层次上的架构设计思想。负载均衡、内容分发与复制、缓存技术在支撑着CDN,我认为缓存技术应该和MVC是在一个层次,CDN高一个层次。
而SDN则是更大层面的思想,它是一种概念性的颠覆。与其说CDN是技术创新,还不于说SDN是产品创新。不过这些新技术,新概念都是由于人们的技术积累而产生的,所以我认为它不会抛弃我们已经掌握的技术,所谓的“IT人每天大脑都需要更新”我觉得有点危言耸听,只不过是知识的升华而已。云计算,SAAS等等,接触一下也就没什么可怕的了。
我发现我说话有点跑题,呵呵。核心模块设计往往都是功能目的和性能要求作为前置驱动的。比如CDN就是要提高网络响应速度,那么将需要响应的东西缓存下来,需要用的时候直接拿出来不就加速了吗?但由于网站访问的人很多,还需要处理高并发问题,也就是多台服务器的负载均衡。请求来了,数据也有了,那么就需要把数据分发给请求者,这就是分发。数据分发给请求者,响应也就完成了。
想法有了,就该考虑如何实现了。数据如何缓存,如何分发与复制,响应如何均衡等等,这些问题可能就会涉及到内存管理、资源调度、NoSql、数据结构、算法、网络等一系列技术,极大地考验研发人员的能力了。在我的经验里,发现合理线程职责定义和布局,是设计好核心模块的根本。
我这里有两个子系统的设计,可简单地与大家分享一下。
1)前端抓包分发系统
- 前端RouteMgr就是分治模式,后端并行部分就是流水模式;
- 这是一个比较典型的反应服务器吞吐性能的应用。
2)实时数据存储系统
- 前端index就是索引,后端stream就是数据包;
- CDRID可以快速地找到indx,index可以快速找到最后一个stream,后面就顺藤摸瓜了;
- stream是一个连续的内存空间,即可按快存储,以提高效率。
不过我还有几点体会可与大家分享:
1)不要使用90%的力气处理5%的问题
曾经我的上司(一个北大的博士,当年湖南省的状元)面试我的时候,问我怎么做到高性能的时候,我说我采用的是流水模式,他就问我,那CPU之间的拷贝难道不需要时间吗?当然需要,但只要我总线带宽足够,缓存一步也不会带来多大的性能消耗,而这些消耗,集中的表现结果也不过就是多消耗了一个CPU的资源,现在的计算机能达到几十个核,多消耗一个核又何足挂齿。后来和我们的CTO出差聊天时,CTO说这个博士在做DPI时,居然要控制CPU的时间片。靠,不知道真的假的,反正我觉得是用不着。再后来就听跟了这个博士多年的同事说他就喜欢在技术上死磕。我觉得吧,花这么大力气就是为了节省那么一点效率,我觉得大可不必,使用简单通用的技术也能做的很好。
2)不要闭门造车,多多向身边的同事请教
也是和这个博士共事的时候,他碰到了一个问题,如何每秒存8GB(大B啊)的数据到硬盘上?我们当时都知道需要把数据hash到各块硬盘上,但却不知道如何分流出来。冥思苦想几天也没有结果,无意间问了另外一个做大数据的博士(他是做java的),没想到人家一句话搞定,说是用RAID卡就可以了,问题里面迎刃而解。
3)关上一扇门,打开一扇窗
还有一件事,也和这个博士相关。当初我在设计一个可靠性极高的系统时,他当初拿出的方案就是步步冗余,以保证系统绝对可靠。可百密终有一疏,当步骤很多的时候,冗余带来的系统复杂度直线上升,而且步骤间的冗余会带来数据冲突,让我苦不堪言,但你却不可挑战他的权威。终于有一次见他心情比较好的时候,和他讨论了一下。我提出只要数据源能保证双机冗余可靠,后续处理过程足够快,就能保证可靠。即使后续的处理过程宕机,我从数据源开始,重新再处理一遍就可以保证数据可靠又保证实时性。这样就避免了步步冗余带来的复杂度。
这个思路也是那段时间午饭后,总喜欢浏览一下军事新闻,里面就提到超高音速飞行器。意思是说当人们发现隐身飞机并不能完全做到隐身时而开辟另外一条道路,那就是飞机比雷达的速度更快,从而避开隐身的问题。我当时也在想,是不是我能不采用他那种步步冗余的方案,而采用点其他方案呢?有时候关上一扇门,上帝会为你打开一扇窗。
4)不要做过于复杂的初始设计
还是要说到这个博士,呵呵。开始我设计这个系统,搞的极其复杂,他立马否定,告诉我说,系统开始设计的时候,一定要看上去非常简单,将来才可能成功,而且会越来越好用,反之就一定会失败。现在看来真是非常有道理,而且我一直也遵循着,非常受用。
5)不要犯方向性的错误,多多细分问题
想当年刚接触做性能优化时,我们产品线的负责人就和另外一个技术负责人为争论抓包效率问题而争的面红耳赤。一个说要用零拷贝技术,一个说要用硬件来支持。当年也不太懂性能优化,后来随着技术的增长,发现他们争论的根本不是问题点。前端接包如果直接在应用层上做,确实会产生系统拷贝,当数据量很大时,CPU效率确实跟不上,所以需要从驱动层实现零拷贝技术。不过这个他们当时已经购买了专用的采集卡,已经不是问题了。但接到数据包后,处理也跟不过来,这应该就是软件的问题了,而不是一定需要硬件来解决。后来我通过优化,将系统的性能由原来的30MB提高到4Gb。我想说的是,在做出系统决策前,最好还是多分析一下系统,梳理一下各个环节的职责,这样更容易发现问题。
6)当公司有成熟的系统或有开源软件时,尽量把系统中间产生的过程数据记录下来,保证系统的可追溯性。我的经验是,这往往能发挥意想不到的效果,甚至会成为系统不可或缺的组成部分,尤其是在可靠性要求比较高的产品;
7)在开发一个效率较高的产品时,应该同时将性能监控系统设计进去,这将大大方便后续的调试与测试。