高性能硬件上的程序部署
15万PV/天 左右的在线文档类型网站 更换硬件系统,心硬件为4个CPU,16GB物理内存,操作系统64位CentOS5.4 Resin作为Web服务器。
两种方式:
1,选用64位JDK来使用大内存
2,使用若干个32位虚拟机建立逻辑集群来利用硬件资源
第一种方案
给Java虚拟机分配超大堆的前提,是有把握把应用程序的Full GC 频率控制得足够低,至少低到不会影响用户使用如:十几小时乃至一天出现一次Full GC。这样可以在深夜执行定时任务的方式出发Full GC 甚至自动重启服务器来保持内存可用空间在一个稳定的水平
而控制Full GC 频率的关键,是看应用中绝大多数对象能否符合“朝生夕灭”原则,即大多数对象生存时间补偿,尤其是不能有批量的,长生存时间的对象产生,这样才能保证老年代空间的稳定。
缺点:
- 内存回收导致长时间停顿
- 现阶段,64位JDK的性能测试结果普遍低于32位JDK。
- 需要保证程序足够稳定,因为这种应用要是产生堆溢出几乎就无法产生堆转储快照(因为要产生十几GB乃至更大的Dump文件),哪怕产生了快照也几乎无法进行分析。
- 相同程序在64位JDK消耗的内存一般比32位JDK大,这是由于指针膨胀,以及数据类型对齐补白等因素导致的。
第二种方案
使用若干个32位虚拟机建立逻辑集群来利用硬件资源
具体做法是在一台物理机器上启动多个应用服务器进程,每个服务器进程分配不同端口,然后在前端搭建一个负载均衡器,以反向代理的方式来分配访问请求。读者不需要太过在意均衡器转发所消耗的性能,即使使用64位JDK,许多应用也不止有一台服务器,因此在许多应用中前端的均衡器总是要存在的。
考虑到在一台物理机器上建立逻辑集群的目的仅仅是为了尽可能利用硬件资源,并不需要关心状态保留、热转移之类的高可用性需求,也不需要保证每个虚拟机进程有绝对准确的均衡负载,因此**使用无Session复制的亲合式集群是一个相当不错的选择。**我们仅仅需要保障集群具备亲合性,也就是均衡器按一定的规则算法(一般根据SessionID分配)将一个固定的用户请求永远分配到固定的一个集群节点进行处理即可,这样程序开发阶段就基本不用为集群环境做什么特别的考虑了。
当然,很少有没有缺点的方案,如果读者计划使用逻辑集群的方式来部署程序,可能会遇到下面一些问题:
- 尽量避免节点竞争全局的资源,最典型的就是磁盘竞争,各个节点如果同时访问某个磁盘文件的话(尤其是并发写操作容易出现问题),很容易导致IO异常。
- 很难最高效率地利用某些资源池,譬如连接池,一般都是在各个节点建立自己独立的连接池,这样有可能导致一些节点池满了而另外一些节点仍有较多空余。尽管可以使用集中式的JNDI,但这个有一定复杂性并且可能带来额外的性能开销。
- 各个节点仍然不可避免地受到32位的内存限制,在32位Windows平台中每个进程只能使用2GB的内存,考虑到堆以外的内存开销,堆一般最多只能开到1.5GB。在某些Linux或UNIX 系统(如Solaris)中,可以提升到3GB乃至接近4GB的内存,但32位中仍然受最高4GB(232)内存的限制。
- 大量使用本地缓存(如大量使用HashMap作为K/V缓存)的应用,在逻辑集群中会造成较大的内存浪费,因为每个逻辑节点上都有一份缓存,这时候可以考虑把本地缓存改为集中式缓存。
介绍完这两种部署方式,再重新回到这个案例之中,最后的部署方案调整为:
建立5个32 位JDK的逻辑集群,每个进程按2GB内存计算(其中堆固定为1.5GB),占用了10GB内存。另外建立一个Apache服务作为前端均衡代理访问门户。考虑到用户对响应速度比较关心,并且文档服务的主要压力集中在磁盘和内存访问,CPU资源敏感度较低,因此改为CMS收集器进行垃圾回收。部署方式调整后,服务再没有出现长时间停顿,速度比硬件升级前有较大提升