Java 处理高并发、高负载类网站的优化方法

1

- 网站关注点之数据库 -

首先是数据库, 这是大多数应用所面临的首个 SPOF。尤其是 Web2.0 的应用,数据库的响应是首先要解决的。

一般来说 MySQL 是最常用的,可能最初是一个 MySQL 主机,当数据增加到 100 万以上,那么,MySQL 的效能急剧下降。常用的优化措施是 M-S(主 - 从)方式进行同步复制,将查询和操作和分别在不同的服务器上进行操作。我推荐的是 M-M-Slaves 方式,2 个主 MySQL,多个 Slaves,需要注意的是,虽然有 2 个 Master,但是同时只有 1 个是 Active,我们可以在一定时候切换。之所以用 2 个 M,是保证 M 不会又成为系统的 SPOF。

Slaves 可以进一步负载均衡,可以结合 LVS, 从而将 select 操作适当的平衡到不同的 slaves 上。

以上架构可以抗衡到一定量的负载,但是随着用户进一步增加,你的用户表数据超过 1 千万,这时那个 M 变成了 SPOF。你不能任意扩充 Slaves,否则复制同步的开销将直线上升,怎么办?我的方法是表分区,从业务层面上进行分区。最简单的,以用户数据为例。根据一定的切分方式,比如 id,切分到不同的数据库集群去。

全局数据库用于 meta 数据的查询。缺点是每次查询,会增加一次,比如你要查一个用户 nightsailer, 你首先要到全局数据库群找到 nightsailer 对应的 cluster id,然后再到指定的 cluster 找到 nightsailer 的实际数据。

每个 cluster 可以用 m-m 方式,或者 m-m-slaves 方式。这是一个可以扩展的结构,随着负载的增加,你可以简单的增加新的 mysql cluster 进去。

需要注意的是:

1、禁用全部 auto_increment 的字段

2、id 需要采用通用的算法集中分配

3、要具有比较好的方法来监控 mysql 主机的负载和服务的运行状态。如果你有 30 台以上的 mysql 数据库在跑就明白我的意思了。

4、不要使用持久性链接(不要用 pconnect), 相反,使用 sqlrelay 这种第三方的数据库链接池,或者干脆自己做,因为 php4 中 mysql 的链接池经常出问题。

2

- 系统架构之 HTML 静态化 -

其实大家都知道,效率最高、消耗最小的就是纯静态化的 html 页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。

但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统 CMS,像我们常访问的各个门户站点 的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限 管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的 CMS 是必不可少的。

除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像 Mop 的大杂烩就是使用了这样的策略,网易社区等也是如此。

同时,html 静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用 html 静态化来实现,比如论坛中的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这 部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求高并发。

网站 HTML 静态化解决方案:

当一个 Servlet 资源请求到达 WEB 服务器之后我们会填充指定的 JSP 页面来响应请求:

HTTP 请求 ---Web 服务器 ---Servlet-- 业务逻辑处理 -- 访问数据 -- 填充 JSP-- 响应请求

HTML 静态化之后:

HTTP 请求 ---Web 服务器 ---Servlet--HTML-- 响应请求

静态访求如下,Servlet:

null

生成 HTML 静态页面的类:

null

null

null

3

- 网站关注点之缓存、负载均衡、存储 -

缓存是另一个大问题,一般用 memcached 来做缓存集群,一般来说部署 10 台左右就差不多(10g 内存池)。需要注意一点,千万不能用使用 swap,最好关闭 linux 的 swap。

负载均衡 / 加速

可能上面说缓存的时候,有人第一想的是页面静态化,所谓的静态 html,这是常识,不属于要点了。页面的静态化随之带来的是静态服务的负载均衡和加速。Lighttped+Squid 是最好的方式了。

LVS <------->lighttped====>squid(s) ====lighttpd

上面是经常用的。注意,没有用 apache,除非特定的需求,否则不部署 apache,因为一般用 php-fastcgi 配合 lighttpd, 性能比 apache+mod_php 要强很多。

squid 的使用可以解决文件的同步等等问题,但是需要注意,要很好的监控缓存的命中率,尽可能的提高的 90% 以上。

squid 和 lighttped 也有很多的话题要讨论,这里不赘述。

存储

存储也是一个大问题,一种是小文件的存储,比如图片这类。另一种是大文件的存储,比如搜索引擎的索引,一般单文件都超过 2g 以上。

小文件的存储最简单的方法是结合 lighttpd 来进行分布。或者干脆使用 Redhat 的 GFS,优点是应用透明,缺点是费用较高。这里指购买盘阵的问题。存储量是 2-10Tb 的项目,采用分布式存储。这里要解决文件的复制和冗余。这样每个文件有不同的冗余,这方面可以参考 google 的 gfs 的论文。

大文件的存储,可以参考 nutch 的方案,现在已经独立为 hadoop 子项目。(可以 google it)

** 其他:** 此外,passport 等也是考虑的,不过都属于比较简单的了。

4

- 网站系统架构之图片服务器分离 -

对于 Web 服务器来说,不管是 Apache、IIS 还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他 们都有独立的图片服务器,甚至很多台图片服务器。

这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用 服务器和图片服务器上,可以进行不同的配置优化,比如 apache 在配置 ContentType 的时候可以尽量少支持,尽可能少的 LoadModule, 保证更高的系统消耗和执行效率。

利用 Apache 实现图片服务器的分离

** 缘由:** 起步阶段的应用,都可能部署在一台服务器上(费用上的原因) 。

第一个优先分离的,肯定是数据库和应用服务器。

第二个分离的,会是什么呢?各有各的考虑,这里重点考虑的是节约带宽,服务器性能再好,带宽再高,并发来了,也容易撑不住。因此,我这篇文章的重点在这里。这里重点是介绍实践,不一定符合所有情况,供看者参考。

环境介绍:

WEB 应用服务器:4CPU 双核 2G, 内存 4G
部署:Win2003/Apache Http Server 2.1/Tomcat6

数据库服务器:4CPU 双核 2G, 内存 4G

部署:Win2003/MSSQL2000

步骤

步骤一:

增加 2 台配置为:2CPU 双核 2G,内存 2G 普通服务器,做资源服务器
部署:Tomcat6,跑了一个图片上传的简单应用,(记得指定 web.xml 的),并指定域名为 res1.***.com,res2.***.com,采用 ajp 协议

步骤二:

修改 Apache httpd.conf 配置 。
原来应用的文件上传功能网址为:
1、/fileupload.html
2、/otherupload.html
在 httpd.conf 中增加如下配置

null

步骤三:

修改业务逻辑。
所有上传文件在数据库中均采用全 url 的方式保存,例如产品图片路径存成:http://res1.***.com/upload/20090101/product120302005.jpg。

现在,可以高枕无忧了,带宽不够时,增加个几十台图片服务器,只需要稍微修改一下 apache 的配置文件,即可。

5

- 系统架构之数据库集群和库表散列 -

大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。

在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase 等都有很好的方案,常用的 MySQL 提供的 Master/Slave 也是类似的方案,您使用了什么样的 DB,就参考相应的解决方案来实施即可。

上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用 DB 类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。

我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者 功能进行更小的数据库散列,比如用户表,按照用户 ID 进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。

sohu 的论坛就是采用了这样的 架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和 ID 进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系 统随时增加一台低成本的数据库进来补充系统性能。

01

集群软件的分类:

一般来讲,集群软件根据侧重的方向和试图解决的问题,分为三大类:高性能集群(High performance cluster,HPC)、负载均衡集群(Load balance cluster, LBC),高可用性集群(High availability cluster,HAC)。

高性能集群(High performance cluster,HPC),它是利用一个集群中的多台机器共同完成同一件任务,使得完成任务的速度和可靠性都远远高于单机运行的效果。弥补了单机性能上的不足。该集群在天气预报、环境监控等数据量大,计算复杂的环境中应用比较多;

负载均衡集群(Load balance cluster, LBC),它是利用一个集群中的多台单机,完成许多并行的小的工作。一般情况下,如果一个应用使用的人多了,那么用户请求的响应时间就会增大,机器的性能也会受到影响,如果使用负载均衡集群,那么集群中任意一台机器都能响应用户的请求,这样集群就会在用户发出服务请求之后,选择当时负载最小,能够提供最好的服务的这台机器来接受请求并相应,这样就可用用集群来增加系统的可用性和稳定性。这类集群在网站中使用较多;

高可用性集群(High availability cluster,HAC),它是利用集群中系统 的冗余,当系统中某台机器发生损坏的时候,其他后备的机器可以迅速的接替它来启动服务,等待故障机的维修和返回。最大限度的保证集群中服务的可用性。这类系统一般在银行,电信服务这类对系统可靠性有高的要求的领域有着广泛的应用。

2

数据库集群的现状

数据库集群是将计算机集群技术引入到数据库中来实现的,尽管各厂商宣称自己的架构如何的完美,但是始终不能改变 Oracle 当先,大家追逐的事实,在集群的解决方案上 Oracle RAC 还是领先于包括微软在内的其它数据库厂商,它能满足客户高可用性、高性能、数据库负载均衡和方便扩展的需求。

Oracle’s Real Application Cluster (RAC)

Microsoft SQL Cluster Server (MSCS)

IBM’s DB2 UDB High Availability Cluster(UDB)

Sybase ASE High Availability Cluster (ASE)

MySQL High Availability Cluster (MySQL CS)

3

基于 IO 的第三方 HA(高可用性) 集群

当前主要的数据库集群技术有以上六大类,有数据库厂商自己开发的;也有第三方的集群公司开发的;还有数据库厂商与第三方集群公司合作开发的,各类集群实现的功能及架构也不尽相同。

RAC(Real Application Cluster,真正应用集群)是 Oracle9i 数据库中采用的一项新技术,也是 Oracle 数据库支持网格计算环境的核心技术。它的出现解决了传统数据库应用中面临的一个重要问题:高性能、高可伸缩性与低价格之间的矛盾。在很长一段时间里,甲骨文都以其实时应用集群技术 (Real Application Cluster,RAC) 统治着集群数据库市场。

6

- 网站的系统架构之缓存 -

缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。

架构方面的缓存,对 Apache 比较熟悉的人都能知道 Apache 提供了自己的缓存模块,也可以使用外加的 Squid 模块进行缓存,这两种方式均可以有效的提高 Apache 的访问响应能力。

网站程序开发方面的缓存,Linux 上提供的 Memory Cache 是常用的缓存接口,可以在 web 开发中使用,比如用 Java 开发的时候就可以调用 MemoryCache 对一些数据进行缓存和通讯共享,一些大 型社区使用了这样的架构。

另外,在使用 web 语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP 有 Pear 的 Cache 模块,Java 就更多了,.net 不是很熟悉,相信也肯定有。

Java 开源缓存框架

JBossCache/TreeCache 是一个复制的事务处理缓存,它允许你缓存企业级应用数据来更好的改善性能。缓存数据被自动复制,让你轻松进行 Jboss 服务器之间的集群工作。JBossCache 能够通过 Jboss 应用服务或其他 J2EE 容器来运行一个 Mbean 服务,当然,它也能独立运行。

JBossCache 包括两个模块:TreeCache 和 TreeCacheAOP。 TreeCache -- 是一个树形结构复制的事务处理缓存。 TreeCacheAOP -- 是一个“面向对象”缓存,它使用 AOP 来动态管理 POJO。

OSCache 标记库由 OpenSymphony 设计,它是一种开创性的 JSP 定制标记应用,提供了在现有 JSP 页面之内实现快速内存缓冲的功能。OSCache 是个一个广泛采用的高性能的 J2EE 缓存框架,OSCache 能用于任何 Java 应用程序的普通的缓存解决方案。

OSCache 有以下特点:缓存任何对象,你可以不受限制的缓存部分 jsp 页面或 HTTP 请求,任何 java 对象都可以缓存。 拥有全面的 API--OSCache API 给你全面的程序来控制所有的 OSCache 特性。 永久缓存 -- 缓存能随意的写入硬盘,因此允许昂贵的创建(expensive-to-create)数据来保持缓存,甚至能让应用重启。 支持集群 -- 集群缓存数据能被单个的进行参数配置,不需要修改代码。 缓存记录的过期 -- 你可以有最大限度的控制缓存对象的过期,包括可插入式的刷新策略(如果默认性能不需要时)。

JCACHE 是一种即将公布的标准规范(JSR 107),说明了一种对 Java 对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机(spooling)、失效、各 JVM 的一致性等。它可被用于缓存 JSP 内最经常读取的数据,如产品目录和价格列表。利用 JCACHE,多数查询的反应时间会因为有缓存的数据而加快(内部测试表明反应时间大约快 15 倍)。

Ehcache 出自 Hibernate,在 Hibernate 中使用它作为数据缓存的解决方案。

Java Caching System JCS 是 Jakarta 的项目 Turbine 的子项目。它是一个复合式的缓冲工具。可以将对象缓冲到内存、硬盘。具有缓冲对象时间过期设定。还可以通过 JCS 构建具有缓冲的分布式构架,以实现高性能的应用。 对于一些需要频繁访问而每访问一次都非常消耗资源的对象,可以临时存放在缓冲区中,这样可以提高服务的性能。而 JCS 正是一个很好的缓冲工具。缓冲工具对于读操作远远多于写操作的应用性能提高非常显著。

SwarmCache 是一个简单而功能强大的分布式缓存机制。它使用 IP 组播来有效地在缓存的实例之间进行通信。它是快速提高集群式 Web 应用程序的性能的理想选择。

ShiftOne Object Cache 这个 Java 库提供了基本的对象缓存能力。实现的策略有先进先出(FIFO),最近使用(LRU),最不常使用(LFU)。所有的策略可以最大化元素的大小,最大化其生存时间。

WhirlyCache 是一个快速的、可配置的、存在于内存中的对象的缓存。它能够通过缓存对象来加快网站或应用程序的速度,否则就必须通过查询数据库或其他代价较高的处理程序来建立。

Jofti 可对在缓存层中 (支持 EHCache,JBossCache 和 OSCache) 的对象或在支持 Map 接口的存储结构中的对象进行索引与搜索。这个框架还为对象在索引中的增删改提供透明的功能同样也为搜索提供易于使用的查询功能。

cache4j 是一个有简单 API 与实现快速的 Java 对象缓存。它的特性包括:在内存中进行缓存,设计用于多线程环境,两种实现:同步与阻塞,多种缓存清除策略:LFU, LRU, FIFO,可使用强引用 (strong reference) 与软引用 (soft reference) 存储对象。

Open Terracotta 一个 JVM 级的开源群集框架,提供:HTTP Session 复制,分布式缓存,POJO 群集,跨越群集的 JVM 来实现分布式应用程序协调 (采用代码注入的方式,所以你不需要修改任何)。

sccache SHOP.COM 使用的对象缓存系统。sccache 是一个 in-process cache 和二级、共享缓存。它将缓存对象存储到磁盘上。支持关联 Key,任意大小的 Key 和任意大小的数据。能够自动进行垃圾收集。

Shoal 是一个基于 Java 可扩展的动态集群框架,能够为构建容错、可靠和可用的 Java 应用程序提供了基础架构支持。这个框架还可以集成到不希望绑定到特定通信协议,但需要集群和分布式系统支持的任何 Java 产品中。Shoal 是 GlassFish 和 JonAS 应用服务器的集群引擎。

Simple-Spring-Memcached,它封装了对 MemCached 的调用,使 MemCached 的客户端开发变得超乎寻常的简单。

null