破解天下论坛's Archiver

wumingxiaozu 发表于 2010-5-24 16:54

服务器稳定性总结

[url]http://longzhuchen.spaces.live.com/Blog/cns!7FA626D2E1F1F2EE!417.entry[/url]
摘要:这篇文章主要讲述解决某应用服务器稳定性时发现的问题与所做的措施。


一、造成服务无法连接的原因
    1、数据库的慢查询
        这是导致WEB服务器挂掉的主要原因,造成这种状况的罪魁祸首是慢查询,与同一个表的写操作混杂在一起。若只是单纯的查询,数据库是可以并发查询的,还不至于造成数据库锁死的情况。但是如果读写操作混杂在一起,慢查询还未结束,写操作就处于LOCK状态,在写操作之后查询操作也会处于LOCK状态,因为有写操作的关系查询的并发量很小,之后队列就会越排越长,1000的连接数很快就占满了。数据库连接数满了之后数据库服务器不会死,倒是连着这个数据库的WEB服务器会死,死的时候无法ssh上服务器,没有得到服务器无法响应时的状态,但是可以肯定的是,这与数据库有直接的联系。

    2、文件系统的盗链
        曾经也有过这样的情形,WEB服务上有丰富的大文件资源供下载,造成盗链现象严重,WEB服务器连接数占满造成无法连接。由于网速带宽的限制,大文件的网络传输必定会占用一定的时间,如果短时间内有大量的大文件连接,就会造成apache的连接数被占满。按照我们目前的访问量来看,单纯的用户行为是不会造成这种情形的,造成短时间内大量大文件下载的往往都是程序写的爬虫,因此WEB服务器对大文件资源的防盗链也非常的重要,对于服务器的稳定性,带宽的节省都有很大的作用。

    3、挂载点(mount)不明原因无法连接
        由于我们拥有多台web服务器,并且独立资源服务器,因此使用了nfs来同步资源文件。但是曾经出现过这样的情形,通过mount连接的盘符无法访问,原因未知。由于之前数据库方面也存在着问题,不知道是否是由于数据库的连接数过载连带造成的原因,还有待考察,但是感觉nfs不是非常稳定,能够不使用尽量不要使用。

    4、tomcat服务器的不稳定
        我们使用的rpc服务是架设在tomcat上的java应用,经使用发现tomcat的服务器并不是很稳定,在程序经常抛出异常、或者有大量链接的时候经常会造成tomcat无法响应,并且无法自动恢复,因此会发现tomcat上的rpc服务无法正常工作。rpc服务中有一项比较重要的服务是单点登陆,如果这项服务出问题,那么所有使用到单点登陆rpc服务的系统都将无法登陆,因此后果非常严重。


二、我们所做的措施
    1、数据库读写分离
        由于数据库与程序的耦合性太大,slow sql很难避免,因此我们从修改表结构外的其他途径来减轻数据库的负担。最简单有效的是利用数据库的主从同步服务器来进行读写操作的分离,将一些查询负荷较大查询放到slave服务器上。实践证明,这样做的效果非常明显,数据库不再那么容易锁死了。
        读写分离时也需要注意,由于主从数据库同步有一定的延时,一些实时性要求高的数据必须从master服务器上读取,尤其是session表与使用数据库做缓存时使用的cache表。由于master与slave的同步会有延时,从slave读取实时性较强的数据库会导致程序的逻辑错误。拿cache表举例,做cache的时候将数据库写入master,再次读的时候从slave读取,但是此时数据还未从master同步至slave,就会造成cache miss,那么cache就不会起作用,比不用cache时的性能更差。session表就更严重了,会造成无法登录的情形。实际的情形需要由程序的逻辑与具体的需求决定,主从数据库的延时也取决于从数据库的读写冲突情形,如果slow sql情形很严重,高峰期的延时会比较明显,但是也就是几秒的数量级。
        slow sql的获取可以通过mysql的slow log,slow log显示的查询时间往往比单独执行那句sql的时间高出很多,一句单独执行1秒多的sql出现在slow sql里面肯能执行了10多秒(不算lock time),应该是由于高峰期IO的瓶颈所致的。slow sql中出现的slow sql被频繁查询的话很有可能造成数据库的锁死,因此要避免正常用户操作导致的slow sql,将这些sql分到空闲的数据库服务器上查询,或者做其他的措施。

    2、缓存
        除了数据库的读写分离,添加缓存也是减少数据库负担的一种有效途径。目前的web站点都离不开缓存的使用。
        1)缓存的介质
            数据库缓存:将获取速度较慢的数据存入获取较快的数据库表中进行缓存,这类缓存方式会增加数据库的查询,但是处理的逻辑相对简单,尤其是对于多台web服务的时候,数据较容易同步。
            文件缓存:将获取速度较慢的数据存入文件进行缓存,可以是纯数据格式也可以是动态语言格式的,这类缓存在文件结构安排较为恰当的时候可以在不影响速度的前提下减轻数据库的负担,但是数据的存入与读取的逻辑要相对于数据库缓存稍微复杂一点,需要自己遵循自定义的格式,还需要自己处理读写冲突的情形。
            内存缓存:将获取速度较慢的数据存入内存进行缓存,一般情况下会以key、value对的形式存取,这类缓存类似数据库缓存,由于存储介质是内存,因此速度上有一定的优势,但是容量上会有限制。memcache是一个非常好用的内存缓存组建。

        2)缓存级别
            页面级别缓存:
            页面级别的缓存,比较难复用,数据量比较大,但是逻辑处理简单,可以整个页面一起进行缓存,每个页面之间不怎么会互相影响。每个站点都会有页面级别的缓存,缓存的介质主要有数据库与文件。discuz是将页面缓存至文件的,drupal将页面缓存至数据库。我们还有许多站点是使用smarty的模板引擎,其中也有页面缓存功能,可以将页面缓存至文件,因为其灵活性个人非常喜欢使用。smarty可以对于一个页面(即一个url)设定多份缓存,也可以将一个页面中的某个部分不缓存,因此基本可以满足常用的缓存需求。
            数据级别缓存:
            使用数据级别的缓存,可以利用他的数据小、可复用率教高的优点,但是逻辑处理也相对较为复杂,当多个应用公用同一份缓存数据,数据的有效性不同会使得代码逻辑复杂。drupal直接将数据以key、value的形式保存在数据库,每个页面请求初始的时候读入内存作为缓存;discuz中使用的数据库缓存是将数据从数据库中读出,写成动态的php文件作为缓存;drupal和discuz的数据缓存都是为应用定制的,因此只会在各自的应用上单独使用。memcache是一个缓存组建,是与应用无关的,即可以被各个应用同时使用的。站点有许多slow sql是分页查询中的总数查询造成,使用memcache将分页的记录总数缓存起来,不会影响太多的用户体验,也可以极大的提高数据的获取速度。
           
        3)高并发页面不要太依赖于缓存
            有一点需要特别注意,若一些并发量较高的页面中有数据库读取数据耗时较长,而可能会造成数据库表锁死,不要指望缓存能够彻底解决问题,因为缓存总是会有失效的那一个时间点的,哪怕是很简短的一段时间,在并发量较高的情况下,那些慢查询会瞬间进行查询,造成很长的查询队列,再混杂一些写操作,很容易就会把数据库表给锁死造成数据库瘫痪。因此并发量高的页面要尽量避免慢查询,通过修改表结构等其他途径来改善。

        4)缓存的用户体验
            在使用缓存的时候,很有可能会造成用户刚提交的数据无法显示出来,如果这些数据实时性要求较高就比较影响用户体验了。适当的控制缓存的时效就不需要再细说了,这是缓存的基本,按照需要具体制定。这里主要强调一点,关于缓存的后台操作。在一些作了缓存的页面很有可能其中的数据是管理员进行编辑的,在编辑完毕后需要立即清除缓存,否则在管理员在编辑完内容后无法检验到效果,对管理人员会造成比较大的困扰。

    3、限制功能
        discuz一些许多自带的功能会造成比较严重的慢查询,比方说用户数据统计功能,搜索功能,这些功能在代码修改方面可以做的并不多,因为已经是比较完善的代码了,考虑到所有站点的稳定性,因此需要限制一些数据库负荷较大功能使用的使用屏蔽,一些非必要的功能甚至可以禁用。

    4、防盗链,squid,apache
        大文件的下载如果数量一多,会对带宽与服务器连接数有很大的影响,因此需要添加防盗链的机制来避免这种情形的发生。一般会对以下一些规则过滤:
        1)host
            这个在使用squid的时候会用到这个域,在非法的host请求都需要过滤掉
        2)referer
            这个域表示请求引用自哪个页面,对于人为的资源外泄可以有效的防范。若资源链接被人放到其他论坛或者其他站点中,直接的点击请求的头部中的referer域可以看出请求的非法性。但是这个域不能过滤referer为空的请求,会导致很多问题的出现,例如在线播放器或者flash的请求可能会不带referer域,另外单单访问该资源也可能不带refere域,因此就很难过滤爬虫程序的资源抓取,因为那些请求一般都不是不带referer域的。
        3)cookie
            在合法站点中,每个页面的请求的时候都给浏览器设置一段特定的cookie,然后在资源服务器上判断请求是否带有指定的cookie来判定请求是否合法,这种规则的过滤是最有效的,虽然说不可能100%过滤非法请求(因为请求永远可以伪造),但是过滤的效果已经可以满足防盗链的需求了。

    5、调整网站架构,将各个应用错开
        由于前期有一个站点非常不稳定,而所有的站点都是完全分布在所有的web服务器上,因此导致一个站点出问题,所有的服务器都出问题。因此我将有问题的站点独立在两台服务器上,使得一个站点的崩溃至少在web服务器上不会影响到其他服务器。

    6、在薄弱的tomcat rpc上添加nginx的load balance
        由于一台rpc服务总是会默默的死掉导致登陆功能无法使用,有时候甚至站点无法打开,因此利用nginx的load balance功能,在tomcat前搭建了一层跳转,请求先发送给nginx,再由nginx发送给tomcat。nginx的背后有两台tomcat服务器做支撑,一台跑主要应用,另外一台做back up,在主服务器出故障时候能够做应急处理,nginx能够自动判断跳转的服务器是否处于正常工作状态。目前nginx支持的load balance只有两种策略,一种是简单的计数轮循,支持给每个服务器添加轮循的权重;另一种是ip映射,这种策略可以使得一个客户端的请求自始至终都将请求发送到同一台服务器。在nginx的支持下,至少在一个tomcat服务器无法响应的时候,另外个tomcat服务器可以做应急处理,然后等待管理员修复主服务器后恢复正常。

   7、添加报警脚本
        简单的图表监控已经没法满足经常出问题的服务器监控了,添加报警脚本也非常重要。一旦服务器应用无法使用的时候,会发送邮件、短信(使用139邮箱)通知管理人员,让管理人员及时处理情形。目前服务器监控的服务有WEB服务器的80端口,数据库端口,rpc端口,主从数据库的同步状态,硬盘的使用情况,发现状况后都会邮件与短信通知,在这里非常感谢张勇编写的监控脚本。

三、总结心得
    1、遇到问题不要心慌,不要急于重启
        在一开始遇到服务器出状况的时候,心里都会非常慌张,急于恢复服务的正常运作,第一时间重启服务,后来发现,这样做往往发现不了问题,出现问题的时候不能急于重启服务,而是要观察服务相关的数据,例如进程数、cpu内存占用情况、服务的状态等等,从中找出问题的根源。虽然这样单次的恢复时间会有延迟,但是长期来说可以尽早的根治问题。

    2、记得去查看各项日志
        服务出问题多多少少会在日志中留下一些痕迹,包括当前服务本身的日志与系统的日志,或许日志中记录的信息不够全面,又或许日志中可能压根没有看到某此出错的信息,但是通过日志找错误是一个很好的习惯,通过百度google这些出错信息可以帮助尽快的定位问题。

    3、多注意数据库的性能瓶颈
        目前出现的问题感觉主要还是在数据库上,在程序开发的时候一定要多考虑数据库的性能问题,因为数据库出问题带来的影响是非常严重的,会导致使用该数据库的服务都无法正常工作。可以多使用缓存或者多搭建数据库进行读写分离来改善数据库的情况。

    4、推荐nginx
        我们试用了nginx服务器,测试下来发现nginx服务器的效率比apache高出许多,而且还带有load balance、proxy等实用的功能,因此感觉非常强大。目前试用该服务器的站点已经运作了一个多月,并没有发现什么异常。将来如果apache服务器出现性能瓶颈的时候,可以考虑将服务器更换成nginx

    5、尽量少用nfs挂载

        在半年内的服务器维护经历下,感觉nfs服务本身不是很稳定,很容导致服务器完全锁死没法响应,只能重启。因此在文件同步的时候尽量少用nfs来进行挂载,如果能够用rsync来达到文件同步的效果的话,就尽量不要使用nfs,避免出现一些莫名其妙的问题,不过这是个人的使用感觉,如果有不同意见可以一起探讨一下。

页: [1]

Powered by Discuz! Archiver 7.0.0  © 2001-2009 Comsenz Inc.