说说 JavaEye 网站架构

偶然看到了Robbin的一篇文章,说到了一些JavaEye的一些实现解密,那就来看看有哪些有意思的东西。

我正在参与做的一 个项目,在某某地方上线,需要几十块单板集群;在某某地方上线,又需要怎样的一个集群组网。咋听起来兴许觉得能有怎样的业务逻辑处理和怎样的用户量呢?可 是JavaEye让我很吃惊,我先前只知道与CSDN比起来,JavaEye确实是一个小规模一些的网站,专业一些的网站,可是服务器呢?只有两台!

http://wenku.baidu.com/link?url=jWy8PbTw5mQQV4VMk0bQQqhh7wr0h3eDibkfKj4GgZzGGUKwb7FrJPlT2JvmKtqcfpi0AlEOd_zxk59XTgMZSn6Epvhabz-kMRVwirJrbMW

这是那台Web Server:

• AMD Opteron 2.4GHz单核 * 2 颗
• 8G内存
• 146G SCSI硬盘

这是那台DBServer:

• AMD Opteron 2.0GHz单核 * 2 颗
• 4G内存
• 73G SCSI硬盘

实在不能说有多么优秀的硬件配置,JavaEye又得面对怎样的访问量呢?

150万动态请求/天

image

这个是JavaEye封杀网络爬虫的简单匹配表达式:

image

JavaEye采用Ruby作为实现语言,看来Ruby很慢是没有说头的,看看Google Adplanner Data:

image

这张图表就很有意思了:

image

CSDN拥有JavaEye的3.5倍访问量,但使用了三十多台服务器集群,中国最大的几个IT站点,使用ASP.NET、Ruby、PHP的都有,但看起来JavaEye的性能或许是最佳的。

-----------------------------------------------------------------------------------------------------------------------------------------------------

JavaEye网站架构进化:

(1)2006年9月

• lighttpd
• ruby 1.8.4, rails 1.1.2, 以fastcgi方式运行
• mysql5.0

FastCGI 像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork- and-execute 模式)。因为是多进程,所以比CGI多线程消耗更多的服务器内存,举例来说,PHP-CGI解释器每进程消耗7至25兆内存,将这个数字乘以50或100 就是很大的内存数。

其实小网站来说,使用FastCGI+Lighttpd是一个非常优秀的组合

(2)2007年1月

• 添加了第2台服务器
• 把web和DB分开
• 系统瓶颈在数据库IO端

系统瓶颈出现在DB IO上面是很正常的,我自己这边的项目经常遇到在Java侧锁瓶颈,无疑是非常奇怪的,一方面是性能测试的用例未必能反映现网真实情况导致,另一方面我还是觉得当整个架构过于复杂,远程方法过多,就会导致这样的问题。

(3)2007年2月

• 把posts表的大字段剥离出来
• posts表的select count操作从30秒减少到
0.1秒

把大表的大字段剥离出来,这是一种基于性能考虑的常用的DB重构方法

剥离前:

• posts(id, ..., body)
• 磁盘存储空间2GB

剥离后:

• posts(id, post_text_id,...) 50MB
• post_texts(id, body) 2GB

(4)2007年3月

• 数据库瓶颈仍然存在
• 引入memcached和CachedModel
• 自己编写了简单的查询缓存
• 240 sql query/s 下降到 140 sql query/s
• memcached缓存命中率在75%

这一次的改进主要在缓存上面,其实在做性能优化的时候,需要经常关注的一个东西就是缓存命中率。

(5)2007年9月

• 引入全文检索
• 使用ruby的ferret
• 中文分词使用单字拆分法

主要是对搜索引擎的优化。

(6)2008年1月

• JavaEye网站代码重写
• 缓存框架改用cache_fu
• 缓存命中率上升到84%
• sql query下降到 50条/s

回去打算去了解一下cache_fu,这里有两篇文章可以参考:

http://weekface.javaeye.com/blog/133797

http://iceskysl.1sters.com/?tag=cache_fu

• cache_fu不对AR对象进行任何拦截,全部交给用户编程
• 用户有完全的控制权,但所有的缓存代码要自己手工编写

(7)2008年5月

• 中文分词算法改用rmmseg-cpp

(8)2008年10月

• 自制山寨cache plugin
• 缓存命中率上升到96%以上

• 抛弃ferret,自己编写全文检索服务器
• 使用Java的lucene作为全文检索引擎
• 自己实现C/S架构的内部调用

(8)2008年11月

• 实现博客,新闻制作PDF功能

(9)2009年3月

• SNS feed功能
• twitter绑定功能
• 开放API

• 废弃Google Analytics
• 自己编写简单的网站流量分析系统

image

(10)2009年12月

• 添加Web IM
• 添加一台服务器
• 合理规划服务器

image

一个生命周期较长的WEB应用每发展到一定阶段一定要面对的是架构上的重组,有时哪怕牺牲一些性能的代价,有时则是牺牲可维护性的代价,带来的是结构层次清晰,便于短期内扩展等好处。这个过程每次都可能是痛苦的,但又是不可避免的。同时,我认为,在项目初期不应当也不可能把架构的融合性和扩展性考虑得太远,那样反而作茧自缚。而在应用发展过程中不断地重构却是更有价值的。

-----------------------------------------------------------------------------------------------------------------------------------------------------

进化总结:

(1)对象缓存原则:

• 数据库表的设计要细颗粒度
• 把有冗余字段的大表拆分为n个互相外键关联的小表
• ORM的性能瓶颈不在于表关联,而在于大表的全表扫描
• 尽量避免join查询,多制造n+1条SQL

上面第一条我觉得还是要看表容量而定,第四条我深有体会,记得在iBatis的使用中还有这样一个专题。

(2)对象缓存的意义:

• Web应用很容易通过集群方式实现横向扩展,系统的瓶颈往往出现在数据库
• 数据库的瓶颈往往出现在磁盘IO读写
• 因此要避免数据库的全表扫描和大表的数据扫描操作
• 如何避免:拆表和臭名昭著的n+1条SQL

……

image

• memcached缓存命中率96%
• cache get : sql query = 4 : 1

另外,Robbin还提到,Ruby的字符串处理,尤其是正则表达式处理性能不好,解决方法也是使用缓存。

cache_money:

• 出自twitter开发团队之手
• 可能是目前最强大的ruby cache框架
• 支持分页查询缓存,支持条件查询缓存

全文检索:

image

猜你喜欢

转载自mrcuiliodng.iteye.com/blog/2047495