由于最近LZ负责的业务系统频繁宕机,导致LZ疲于本命,上一个星期(因为现在是周一了,0.0)连续加了五天班,其中还包括周末,就是为了出一套应急方案。宕机的根本原因,现在已经渐渐真正的明晰了,最早的一次是因为消息发送与数据库操作的顺序错误导致的数据库宕机,目前已经通过顺序的调换解决了数据库的压力。然而数据库的问题解决之后,则开始是应用服务器出问题。最近频繁宕机的原因是连接数经常爆满,完全无法应付nginx的疯狂攻击。
经过与公司领导与业务人员的交流,最终才知道,由于业务模式的变化,导致LZ负责的业务系统用户剧增。因为高并发的原因,LZ的系统现在已经频繁的不堪重负,数次被压垮,提升服务器的响应速度已经迫在眉睫。最终定制的解决方案是比较主流的解决方案,也就是将前端采用集群部署,而后台与其它业务系统的交互,则会采用独立的服务器处理。不过这个方案并不能一解燃眉之急,集群的部署和前端与后台的分割都需要不短的时间进行部署和测试,因此只能安排到这周一(其实就是今天,0.0)才开始推行。
为了避免在系统向集群转换的过程中再出现问题,LZ与各位领导商量之后就制定了一个简单的应急方案,而LZ则担任了编写应急方案的职责。最近的几天,LZ就是在忙于编写这个应急方案(除此之外,还要边应付业务人员和其它同事对系统的不满,0.0),怎一个焦头烂额可以形容。
应急方案采用的方式是,增加用户数限制以及优先级的功能。用户数限制的功能类似于火车站之前的抢登陆,简单的说,就是这个系统只能若干个(比如1000个)用户登陆,后面来的用户将被拒绝登陆。优先级的功能,则是指在人数达到一定数目时,需要将某些角色的用户踢掉,让另外一部分角色的人优先使用系统(这是因为那些被踢的角色查询的数据量较大且对系统的依赖性不高,容易给服务器造成不必要的压力)。这个功能LZ一开始觉得挺简单的,但是当LZ真写起来的时候,才发现真的没有想象中的那么简单。
最开始的难度在于用户数的精确统计,因为用户数与session数量并没有直接关系,要想统计精确的用户数,必须按照用户名去重。这一点在找到适合的监听器(LZ使用的监听器是存在于web服务器中的HttpSessionAttributeListener,但这不一定适用于所有项目)之后被解决,方案就是记录每个用户的session数量,为0则代表该用户已退出,否则代表正在使用系统(也就是会算作1个用户)。
然而最难的地方却不是用户数的精确统计,而是并发所导致的难度。因为用户数是在高并发的情况下统计的,因此必须考虑并发的情况,在代码中添加适当的同步,即要保证统计数据的正确性,又得保证足够的性能。如果因为这个数量统计而影响性能,那就与这个功能的意义背道而驰了。
这还是LZ第一次在高并发的情况下编程(以前其实也有,只是由于功能并不核心,所以从未考虑过性能,只是无脑的在方法上使用synchronized),每写一句代码,都要考虑如果有成百上千个线程同时运行会如何。这每一句代码似乎都成了美女,每一个都可能有成百上千个大爷翻牌,而且还是同时,因此到底如何同时伺候N(N>1)个大爷,自然就成了一个问题。这也算是LZ第一次在编程的过程中,真真切切的产生了并发的意识。
或许是老天开眼,也或许是LZ运气尚且说的过去,在这之前的几个星期,LZ刚买了一本关于并发编程的书籍,一直都没有看。这下可好了,刚好派上用场,因此拿起这本书不到两天,LZ就一口气读了将近一半。最终也算是临时抱佛脚,将这个应急方案给应付过去了。
由于LZ只是为了应付当前的情况,所以并没有细读,尽管读的过程中都读懂了,也与作者有深深的共鸣,但过后其实印象并不深。不过这足以让LZ度过当前的难关了,在编写这段并发代码时,LZ主要采取了以下几种小技巧(基于书中的思想)。
1、将原本存在于监听器和过滤器的属性全部提出,使得两者不再需要考虑线程安全的问题。
2、提出的属性放在单独的两个类(session有效列表和session无效列表,无效列表其实就是可能要被踢的session)当中,这两个类都是单例,并确保这两个类是线程安全的。
3、由于需要遍历一个装满session的集合实现踢人的功能,因此采用备份的方式。如此一来,在遍历session并使得session失效时,并不会锁住失效session的列表,这样可以极大的提高性能(前提是限制的用户数并不高)。
4、在监听器与过滤器使用这两个单例类时,坚决杜绝各种竞态条件或者复合操作。
5、重构现有的代码,让需要同步的地方集中在一起,减少性能的损耗。
这算是LZ从书中紧急领略的几个方式,今天LZ的应急方案已经正式上线,是否能顶住高并发还有待考量。不过LZ是领略了书中的一部分真谛才下的手,而非一时臆测,因此还是有一定把握的。(希望明早一上班不会被业务部门炮轰,0.0)
由于这次事件,LZ已经彻底爱上了这本并发编程(前几天还说爱上了深入理解那本书,有点花心啊)。这本书并不厚,才200多页,LZ用了一天半不到的时间看了80页,准备直接趁热打铁,等看完这本书之后,再继续深入理解和设计模式这两本书。因此最近的计算机系统原理系列可能要延迟一下了。
本文写的十分匆忙(今晚其实刚加过班,0.0),因此难免漏洞百出,各位猿友如果遇到过类似高并发的问题,可以畅述己见,让LZ等一届屌丝得以一窥天机。时间已经不早了,各位猿友明早见分晓吧。