- 论坛徽章:
- 0
|
第三篇 新年了
随着圣诞和新年的来临,我们准备从另外一个方面来改变和优化系统以提高网站的性能和响应速度。(从以往看来节日期间是我们流量比较小的时候,人们更愿意花时间跟家人团聚而非泡在社区里)
×××,再次回到memcaced的优化上来了。通过debug发现了我们memcache封装的问题(它是负责通过key来自动查找社区名和用户名,或者社区名或用户名),许多在memcached的查找都失败了。查找本身并没有失败,而是从memcached中返回的对象实例有部分“失败”了。
这句话什么意思?也就是说花费时间很长的计算结果被放到了缓存中,但是重新从缓存中获得它们的时候失败了。结果再次从新计算(这时memcache封装里面的一个回退机制)。因此没有达到我们认为的节省时间、降低负载的效果。
然而,这个跟先于对象定义的Ruby声明的类没有关系,显然和返回的marshalled数据有关。在Google上面搜索了这个错误信息没发现任何明显犯同样错误的人,也没有任何解决办法。
(译者评:看看别人解决问题的过程比知道优化技巧这样的结果更加重要,比如作者也走过很多盲目的弯路,但这些弯路也是思考问题的方式)
通过查看Debian的更新日志提到了一些 Ruby 1.8.4关于marshalling,并一次同时在Rubyonrails.org’s download page 看到了如下信息:
We recommend Ruby 1.8.4 for use with Rails. Ruby 1.8.2 is fine too, but version 1.8.3 is not.
因此升级了Ruby,我们从升级到了1.8.4,重新编译了所有C扩展的的包,比如Ruby-MySQL和RMagick,然后上线看看。
结果是没有变化!
接着在一月的第三个星期,Robot Coop 发布了他们的memcache-client 库,作为Ruby-Memcache的替代,现在后者的开发停止多时了。
使用新的memcache-client库系统运行得非常流畅。它甚至做了我们自己封装的memcached包装器的大部分工作,请大家为Robot Coop的工作欢呼三次,太伟大了!
由于有了如此好性能的memcahced我们冒险向前走了另外一步。我们将session的存储从ActiveRecordStore(读Mysql表的存储)移到了memcached中。我们希望通过这样做也是为了减少前面所述的Master-Master模式中只有一个线程往另一个Master中写的压力。同时,这样也能将每次请求页面而需要到数据库的比例比11月份上线时减少了1/3。
另外通过Robot Coop memcache 客户端我们可以有理由去跨多台机器做分布式缓存。memcached对于我们大部分的机器无论是在内存消耗上,还是CPU使用上都是非常不错的。
我们临时将所有机器都配置上memcache来应对它的连接数问题。为什么说是临时的?因为我们有个登录问题需要debug出来。有时侯我们不能去再次使用我们自己的机器。用户像是坐在一个很大的公司中,有太过敏感的防火墙和内容过滤器,以至于其它人不能再登录进来。
许多问题随之被发现了,他们甚至没有看见我们种的且将过期时间设置为2010年的cookies。为了让他们看见,我们甚至尝试换个cookie名字(这样做是为了想避免一个自己的胡乱猜测,什么以session命名的cookies会在浏览器关闭的时候就自动过期)
(译者评:笑可笑之人,有时候找不到问题,不就是根据自己一点点经验去胡乱尝试么?这也是技术的一部分)
多台机器分布式的memcached的配置和session的存储有过什么联系么?哦,天知道?最后没有人清楚的记得当用户登录正常时,是不是我们只是做了将用memcached做session存储这一件事让它的好的。(这一个改变对于我们系统减轻了许多压力)
为了简化调试(也为了减少潜在的隐患)我们又返回去用单台机器配置一个memcached和一个MySQL的做法。memcached放在一台(只做数据同步和广告服务)比较空闲的数据库服务器上。顺便提一下memcached的配置非常简单。经常需要去变的参数是分配的内存大小,需要记住的是分配的内存可以很大,但memcached也必须去调度这么大的内存空间。到了一定时候它将会到达它的极限。我们当前给memcache了1024M的内存空间,这个对于文本信息绰绰有余。
这是基于我们系统7周时间的memcached的统计数据。(不要问过关于二进制字节的读写比率,我认为这是颠倒的???)
get_misses: 59,571,775
get_hits: 235,552,563
total_connections: 2,002,697
bytes_read: 79,799,051,834
bytes_written: 734,299,301,670
curr_items: 1,421,982
total_items: 76,452,455
cmd_set: 76,453,343
cmd_get: 295,124,338
bytes: 717,612,826登录的错误也在不久以后解决了,原因是当关闭浏览器的时候cookie就过期了。无论什么原因?这个问题的解决没有太多的逻辑推理。仅仅是找一种便于管理的
折中办法才行。
(译者评:对称是种美,与其将cache散落到各处,不如简单点让一台没有压力的机器单独来承担。不对称也是一种美,两台数据库服务器,只在一台上装了
memcached)
出现新的访问速度变慢的问题
在一月份的前半个月我们一天就可以支撑110万的流量,此时的流量达到了95G。接着到了一月份的后半个月系统出现问题,几乎不能工作了。虽然以前我们所做的所有修改和调优(原本这些都非常好),但我们碰到了新问题,发现系统在变慢。
是正常的变慢,还是糟糕的变慢?实际上是不好的变慢。到了一月份的最后一周变得跟去年11月份一样慢了。为什么会这样?哦,这是一个好问题。我们已经优化了系统的每一个部分(如果你读完了这一系列文章,你也应该清楚)。在过去的几周内,事情看上去都不错,但现在我们又回到了开始时那样。
先还是把系统结构图图画出来吧,这样清楚些,不如从图中找问题。(译者评:我就是在机器上傻看数据,退一步看看整个架构更容易发现问题)
我们首先发现是整个系统全部变慢,甚至有时候不能访问,但所有的机器压力还是很小,应该说是太小了。调整lighttpd的fastcig。debug发现侦听虽被指定去处理连接,但是它坐在那里一动不动。当有一般的请求焊死在那里,这再明显不过是说它不能响应所有的请求。
(对于这些似乎你很眼熟,我以前在poocs.net写过一篇文章叫“温柔地杀死我”)
使用tpcdump来监控侦听端口的流量,什么也没有,没有一个字节通过管道。使用strace来看看那些忙一些的侦听在干什么,它们在wait,也没有做任何事情。郁闷的是,如果你重启lighttpd或者×××,最终和开始看到的一样。我的同事对防火墙做了各种配置,我开始调整应用服务器和lighttpd代理服务器的/proc的参数,我猜测是到了某个参数的上限。用netstat也发现有几百个连接在那些管道中,状态都是TIME_WAIT和CLOSE_WAIT,很像遭受了synflooed攻击。但这是我们内部机器,不会被外面看到。下一步,根据公共可利用的资源来调整/proc中的参数,具体如下:
echo "1024 65535" > /proc/sys/net/ipv4/ip_local_port_range
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
echo 1800000 > /proc/sys/net/ipv4/tcp_max_tw_buckets
echo 256000 > /proc/sys/net/ipv4/tcp_max_syn_backlog
echo 1024 > /proc/sys/net/core/somaxconn
echo "128000 200000 262144" > /proc/sys/net/ipv4/tcp_mem
echo 2097152 > /proc/sys/fs/file-max
echo 0 > /proc/sys/net/ipv4/tcp_timestamps
echo 0 > /proc/sys/net/ipv4/tcp_window_scaling不要在家使用这些命令,因为它一点都没有帮到我们。请求始终停在那,网站的性能让人看上去恶心。另外一个人企图在每个应用服务器上都启动一个lighttp
(这样来代替远程的fastcgi侦听),然后放一个lighttp的负载均衡的方向代理在前面。事实证明系统还是慢。
剩下比较“土”的办法,我写了一个脚本来搜索所有的可用侦听,如果它们在一定周期内没有响应,就kill它。被kill的请求会有Rails的spinner/spawner很快的重新
启动,lighttpd只是多花几秒钟来重新连接socket。虽然对于业务来说不能持续的来监控它们了。
这个方法虽然看上去不漂亮,但它工作的很好,并且让我们熬过了一月来到了二月,配置如下:
![]()
剩下最后一篇是关于系统扩展性的问题,也总结一下,哪些有帮助,哪些没有,同时展望一下将来系统调优的计划。
第四篇 速度快和稳定
在2005年的11月至2006年的3月,许多优化的工作都在这期间完成。这里面不少工作都不得不变成了配置的一部分(比如第三篇提到的请求分发的监控脚本)。最终经过了几周的运行,这个网站被证明是稳定且速度快的。另外,我们也已经能完成一点从用户和社区运营人员那里的需求。
完成过程中的闪光点
二月份,一些小的调整让系统性能变得更好。
第一,当用户编写个人消息和在论坛发帖时,我们利用AJAX来对其内容进行预览。虽然这不是性能的杀手,但把这因素剔除来减轻压力是有意思的。呃,在AOL浏览器中prototype会崩溃。
另外,将作为lighttpd守护进程对待。这样崩溃的现象在1.4.8及以后的版本就很少出现了,但仍然需要监控进程的情况。要知道如果lighttpd down了整个网站就down了。所以得看好它。(译者评:这里可能会出现单点失败的情况,clear & dirty)
将lighttpd作为daemontools 来跑是非常简单的。简单配置以后(具体配置这些写得很清楚 http://www.thedjbway.org/daemontools.html)你在ROR的service树下面用一行脚本来用damontools 配置lighttpd服务。你会知道并且爱上Rails最初的script/server。
/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
这样就启动好就运行了。你可以通过lighttpd的配置来简单的设置一下,发送lighttpd的进程ID或这信号SIGINT到你后台的监控中。然后需要注意的是如果你的网站流量非常大就需要把那些不能再完成了服务请求通过SIGKILL杀死。新发布的lighttpd1.4.11分发请求时候的僵死越来越少了,似乎这种情况完全没了。但我们将继续通过脚本监控它。
到此是这一些列文章的结束了。现在服务器每天支撑1.2M的PV(100GB的流量)。
总结以及以后的计划
以下是这四个月被证明是非常有用的优化手段:
系统优化:
>使用Linux 2.6代替2.4
>使用自己编译的Ruby 1.8.4
>使用Mysql官方的二进制版本
>使用lighttpd 1.4.11代替以前的
>使用memcache-client代替Ruby-MemCache
>使用了更少数量的dispatchers
>并且监控你的dispathchers
代码优化:
>避免使用ROR的组建 components
>用memcached储存费时的计算
>用memcached来存储session
>如果你的站点很受欢迎就不要使用live-previews
>使用异常通知exception notification来捕捉可能的异常
另外不要完全相信这些总结。优化是一个发展中的东西。
你需要一直对网站进行监控,包括你的服务器和所有相关的软件。
强烈建议不仅仅只监控服务是否起来了,还好监控服务器的压力,响应时间等等。用Nagios和Cacti结合起来做这些工作被我们证明是很有用的。
提醒注意的是,需要读读所有你使用的软件包的改变日志,看看新的版本中解决了什么已经存在的问题,可能产生哪些新问题。不需要强制升级所有的更新,但对你正困扰的问题在新版本中别解决了,你就一定要升了。你可以在测试环境中进行测试,减少当机时间,避免升级带来的潜在问题。
请留心你网站代码的变化。一般来说,应该多想想我要做什么。一个像Rails这样聪明的框架会让你有机会去思考,而不是每天写些重复性的代码。要聪明的使用时间。
一条SQL语句或单个循环可能在你开发用的笔记本上跑的很快,但是在产品环境中同时并发执行成百上千次或产品中数据量比较大都有可能导致网站变慢。
性能调优准则
总的来说,不太容易把网站的性能调节好。
一种方式是让网站处于非生产状态,也就是测试状态,自己产生一些流量来测试,这样的流量不同于真实的用户产生的流量。这样模拟的网站数据集也不同于线上的正式产品。这种情况下所有的调优结果都要根据线上真实网站的情况进行一下转换。
另一种方式是对线上实际网站逐步地进行性能调优。这样有许多好处,你有真实的用户在使用你的功能,使用你的系统,正如数据一样所有这些都是真实的,比测试环境有价值的多。但这种方式主要问题是,如果你的网站访问量特别大,系统的日志production.log将会大量快速的被写到硬盘上,这样你就很难找到问题。如果做日志的分离,将实际的日志相互关联起来也不太容易。那么将日志重定向成系统日志Syslog(通过 SysLogger,在RailsAnalyzer Tools包里面),它能将每个日志同一个线程ID关系,这样就非常方便了。
写大文件的日志意味这你整个系统的IO补偿糟糕,如果你在产品环境中不要写太多太详细的日志,系统将会比你测试的结果跑得好得多。
哦,当网站调优时,拆分用户将会有比较好的效果,但更重要是的要不断听声网站的使用体验。
使用过的工具:
将前面提到的Rails Analyzer Tools包放在手边,这些工具在类UNIX系统里面非常管用。你还需要会几个命令,cat,tail,grep,awk,ps,netstat。另外,安装一下ngrep和tcpdump来debug网络流量,还可以用mytop来查看Mysql线程列表。
把这些都准备好需要一些时间、耐心和知识,也偶尔需要Google搜索一下。
将来还要处理的事情
随着memcache-client库的发布,Robot Coop公司又发布了另一个小的库,名字叫做cached_model,这是基于memcached用于减轻数据库重复查询,原理就是在查询之前通过子类Active::Base来检查memcached中的内容。
我在1.0版本它出现的时候,看过一下,觉得还是很有发展的。那个时候它不能很好的集成,经常胡乱抛错。由于当时我们忙于调试其它的问题,就没有仔细地去解决这些问题。在此期间,cached_model版本升级到了1.1.0,也修复了多个bug。这个东西也将包括与我们将来优化的路线图当中了。
在第三篇的时候我们碰到了一个“分发请求僵住”的问题,我们将回来再看看FastCGI ,更通常的办法是用lighttpd也支持的SCGI。
有Zed Shaw发布了新的软件Mongrel 已经开始出售了。它作为“比WEBrick”更好的应用服务器,将纯HTTP放到FastCGI中,非常值得多看看。在读者评论中
有人说早期Dan Kubb提到用Canditional GET ,它的潜在好处是在缓存页面不变时它可以用浏览器来缓存页面。我只是简单地看了一下它的标题,Rails插件看上去还不错,很容易集成进来。
有个比较大的变化,尽管我曾经提倡使用Mysql的全文检索,但现在我正在基于Rails的一个搜索插件工作,它很容易无缝滴集成到HyperEstraier搜索引擎中。如果它跑的很好(性能好和数据保护),我们将丢掉全文检索,弄一个纯InnoDB的数据库配置,并且向锁表和非事务的测试说再见了,同时这样也不能使用ROR的schema.rb了。
说道这里,我们升级到了了最近的Rails1.1。尽管这次升级对于性能并没有太大的必要,但是它有另外受欢迎的地方,它使得我们代码变得漂亮简介了。
谢谢看过这一系列文章的人们。我真诚的希望我对我们案例的详细描述能够避免再去做我们已经做好了的一些研究和调试工作。如果你需要任何帮助,只要记下我的email,通过联系limited overload我可以作为咨询顾问来帮助你。
![]() |
|