引言

在前端优化的第一部分中,主要讲解了对静态资源的优化措施,包括图片压缩、CSS Sprites 技术、GZIP 压缩等。本文作为系列的第二部分,将深入讲解前端优化中关键的 Flush 机制、动静分离、HTTP 持久连接、HTTP 协议的灵活应用以及 CDN 等技术。结合这些技术策略,旨在进一步提升 Java Web 应用程序的整体性能。

Flush 机制的使用

在 Web 技术中,Flush 机制并非新概念。其核心思想是无需等待网页内容全部加载完毕再一次性写回客户端,而是可以分部分逐次返回。对于内容较大的网页,一次性写回全部内容会导致用户长时间面对空白页面,体验较差。Flush 机制允许开发人员将网页内容按文档流顺序逐步返回给客户端,使用户感知到系统正在工作,从而提升等待过程中的用户体验。这是一个经典的优化手段,至今仍在广泛使用。

在 Java Web 技术中,实现 Flush 非常简单。只需调用 HttpServletResponse.getWriter() 输出流的 flush() 方法,即可将已经加载完成的内容写回给客户端。

然而,是否每个网页都需要使用该技术?并不建议。将网页内容加载完毕后一次性返回客户端也有其优势。网络传输存在最大传输单元(MTU),内容加载完毕后一次性输出可以最大程度地利用传输带宽,减少分块和传输次数。换言之,Flush 机制可能会略微增加用户等待时间和浏览器渲染时间。但对于大型网页而言,牺牲这点效率来换取用户体验的提升,通常是值得的。

动静分离

所谓的动静分离,就是将 Web 应用程序中的静态内容和动态内容分别部署在不同的 Web 服务器上,针对性地处理不同类型的请求,从而达到性能提升的目的。本文基于 Java Web 环境讲解优化,主流服务器软件为 Tomcat。需要注意的是,Tomcat 在高并发和静态资源处理能力上相对较弱。既然选择了 Java Web,我们就应通过架构优化来弥补不足。动静分离便是其中一种有效方法:将静态资源的处理任务交给更擅长的软件,而让 Tomcat 专注于处理 JSP/Servlet 动态请求。

服务器软件选择

对于静态资源处理,可以选择 Nginx。这是一款高性能的 HTTP 和反向代理服务器,支持高并发,对静态资源的处理能力较强。事实上,动静分离的方案多种多样:有人采用 Apache+Tomcat 组合,也有人使用 Tomcat+Tomcat 组合(部署于不同主机或域名)。其中 Apache+Tomcat 方案与 Nginx 方案原理相似,均基于反向代理。相比之下,Nginx 配置动静分离更为简洁,而 Apache 则需要配置 mod_proxy 模块。

Apache 配置示例

在 Apache 中,mod_proxy 模块负责反向代理的实现。核心配置内容如清单 1所示(该配置源自实际项目经验)。

清单 1. 动静分离的 Apache 核心配置

<Proxy balancer://proxy>
    BalancerMember http://192.168.1.178:8080 loadfactor=1
    BalancerMember http://192.168.1.145:8080 loadfactor=1
</Proxy>

NameVirtualHost *:80
<VirtualHost *:80>
    ServerAdmin service@xuanli365.com
    ServerName www.xuanli365.com
    DocumentRoot /www
    DirectoryIndex index.shtml
    <Directory /www>
        AllowOverride All
        AddType text/html .shtml
        AddType application/x-rar .rar
        AddHandler server-parsed .shtml
        Options +IncludesNOEXEC
    </Directory>
    RewriteEngine on
    ProxyRequests Off
    ProxyPass /static/!
    ProxyPass / balancer://proxy/
    ProxyPassReverse / balancer://proxy/
    ProxyPreserveHost on
</VirtualHost>

根据 Apache 官方对 mod_proxy 模块的介绍,ProxyPass 属性可以将远端服务器映射到本地服务器的 URL 空间中,实现地址映射。在清单 1的配置中:

  • 当访问路径不在 /static/ 下时(! 表示非),请求转发给后端服务器(即 Tomcat)。
  • 当访问路径 /static/ 下时,直接访问本机。

例如,访问 www.xuanli365.com/static/css/index.css 时,实际处理请求的是 Apache 服务器;而访问 www.xuanli365.com/index.jsp 时,Apache 会将请求转发到后端的 Tomcat 服务器(如 http://192.168.1.178:8080/index.jsp)。这就实现了动静分离。此外,该配置中也包含了简单的负载均衡(通过 loadfactor 因子)。

大型网站案例分析

打开任意大型门户网站(如腾讯网站),查看图片资源地址,可能会发现类似以下域名:

  • http://mat1.gtimg.com/www/iskin960/qqcomlogo.png
  • http://img1.gtimg.com/v/pics/hv1/95/225/832/54158270.jpg

可见该网站存放图片资源使用了多个域名。使用 Linux 的 host 命令查看这两个域名的 IP 地址,结果如图 1所示。

图 1. 某网站的动静分离

图 1. 某网站的动静分离

通过查看 IP 地址可以发现,这些图片很可能存放在不同的主机上(注:一个主机可以拥有多个 IP)。图片内容和网页的动态内容不在同一 IP 下,这也是动静分离的体现。使用多个域名可以增加浏览器的并发下载数,从而提高下载效率。

本文演示架构

本文采用另一种策略对动静分离进行演示,大致结构如图 2所示。

图 2. 本文设计的动静分离结构

图 2. 本文设计的动静分离结构

在该架构中:

  • A 主机:存放静态资源,安装 Nginx。
  • B 主机:存放动态程序,安装 Tomcat。

配置 Nginx 后,当请求的是 html、jpg 等静态资源时,直接访问 A 主机上的静态资源目录;当用户提出动态资源请求时,则将请求转发到后端的 B 服务器,交由 Tomcat 处理,再由 Nginx 将结果返回给客户端。

可能会有疑问:动态请求要先访问 A,A 转发访问 B,再由 B 返回结果给 A,A 最后又将结果返回给客户端,是否多余?初看的确如此,但这种结构至少有两点好处:

  1. 为负载均衡做准备:随着系统发展,单台 B 主机处理动态请求可能不足。基于图 2 的结构,可以直接扩展 B1、B2 等节点,只需修改 Nginx 配置即可实现负载均衡。
  2. 便于程序开发与维护:基于该结构,程序代码中只需写相对路径(如 <img src="a.jpg">),无需关心具体资源服务器的地址。若静态资源扩展到其他服务器,只需调整 Nginx 配置,无需修改代码。

Nginx 配置示例

按照图 2 所示的架构图,安装好 Nginx 和 Tomcat 后,对 nginx.conf 进行配置。与本文该部分相关的配置如清单 2所示。

清单 2. 动静分离的 Nginx 配置

# 转发的服务器,upstream 为负载均衡做准备
upstream tomcat_server {
    server 192.168.1.117:8080;
}

server {
    listen       9090;
    server_name  localhost;
    index        index.html index.htm index.jsp;
    charset      koi8-r;

    # 静态资源存放目录
    root  /home/wq243221863/Desktop/ROOT;

    access_log  logs/host.access.log  main;

    # 动态请求的转发
    location ~ .*.jsp$ {
        proxy_pass http://tomcat_server;
        proxy_set_header Host $host;
    }
     
    # 静态请求直接读取
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|css)$ {
        expires      30d;
    }
    ……
}

清单 2配置简洁明了:动态请求(以 .jsp 结尾)发到 B 主机(192.168.1.117:8080),静态请求(gif|jpg 等)则直接访问定义的 root 目录。

测试环境搭建

接下来在 Tomcat 中新建 Web 项目,添加一个 test.jsp 文件,目录结构如图 3所示。

图 3. B 上的测试项目结构

图 3. B 上的测试项目结构

定义一张测试用的静态图片,放置在 A 主机的桌面 ROOT/seperate 目录下。结构如图 4所示。

图 4. A 上的静态资源文件夹结构

图 4. A 上的静态资源文件夹结构

注意:这里的 separate 目录名是与 B 的项目文件夹同名的。

查看图 3 中 test.jsp 的源码,如清单 3所示。

清单 3. test.jsp 源码

<%@ page language="java" contentType="text/html; charset=UTF-8"
   pageEncoding="UTF-8"%>
<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>动静分离的测试</title>
</head>
<body>
     <div>这是动态脚本处理的结果</div><br>
   <% // 这是一段测试的动态脚本
   Date now=new Date();
   SimpleDateFormat f=new SimpleDateFormat("现在是"+"yyyy 年 MM 月 dd 日 E kk 点 mm 分");
   %>
   <%=f.format(now)%>
   <br><br>
   <div>这是静态资源的请求结果</div><br><img alt="静态资源" src="jquery.gif">
</body>
</html>

清单 3是一个非常简单的 JSP 页面,主要使用 img 标签来访问 jquery.gif。已知 test.jsp 在 B 服务器上,而 jquery.gif 在 A 服务器上。用于访问 jquery.gif 的代码里不需要指定 A 的地址,而是直接使用相对路径,就好像该图片也在 B 上一样,这就是本结构的优点。在 A 上访问 test.jsp 文件,结果如图 5所示。

图 5. test.jsp 的结果

图 5. test.jsp 的结果

配置顺利,完全按照预期实现了动静分离。

性能测试对比

初步完成动静分离配置后,我们需要验证其性能提升效果。将 Tomcat 服务器也迁移到 A 服务器上,同时将 jquery.gif 拷贝一份到 separate 项目目录下,图 3 的结构变为图 6所示。

图 6. 拷贝 jquery.gif 的 separate 项目

图 6. 拷贝 jquery.gif 的 separate 项目

将 Tomcat 端口设置为 8080,Nginx 端口依然是 9090。现在:

  • 访问 http://localhost:9090/separate/test.jsp(未使用动静分离,静态资源由 Tomcat 处理)。
  • 访问 http://localhost:8080/separate/test.jsp(使用了动静分离,静态资源由 Nginx 处理)。
  • 注:原文此处端口描述可能存在笔误,根据上下文逻辑,9090 应为 Nginx 代理端口,8080 为 Tomcat 直连端口。测试时分别对比 Nginx 代理下的静态资源访问与 Tomcat 直连的静态资源访问。

使用 Apache 的 AB 压力测试工具,对相关 URL 分别进行压力和吞吐率测试。

1. 静态资源测试

首先,对静态资源(jquery.gif)的处理结果如清单 4所示。

清单 4. 静态资源的 AB 测试

测试脚本:ab -c 100 -n 1000 http://localhost:{port}/seperate/jquery.gif

9090 端口,也就是 Nginx 的测试结果:
Concurrency Level:      100
Time taken for tests:   0.441 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      4497000 bytes
HTML transferred:       4213000 bytes
Requests per second:    2267.92 [#/sec] (mean)
Time per request:       44.093 [ms] (mean)
Time per request:       0.441 [ms] (mean, across all concurrent requests)
Transfer rate:          9959.82 [Kbytes/sec] received

8080 端口,也就是 Tomcat 的测试结果:
Concurrency Level:      100
Time taken for tests:   1.869 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      4460000 bytes
HTML transferred:       4213000 bytes
Requests per second:    535.12 [#/sec] (mean)
Time per request:       186.875 [ms] (mean)
Time per request:       1.869 [ms] (mean, across all concurrent requests)
Transfer rate:          2330.69 [Kbytes/sec] received

清单 4的测试脚本代表同时处理 100 个请求并下载 1000 次 jquery.gif 文件。关注 Requests per second(吞吐率)指标可以看出 Nginx 实现动静分离的优势:动静分离每秒可以处理 2267 个请求,而不使用则只可以处理 535 个请求。由此可见,动静分离后效率提升显著。

2. 动态脚本测试

动态请求的转发会导致动态脚本的处理效率降低吗?降低多少?再用 AB 工具对 test.jsp 进行测试,结果如清单 5所示。

清单 5. 动态脚本的 AB 测试

测试脚本:ab -c 1000 -n 1000 http://localhost:{port}/seperate/test.jsp

9090 端口,也就是 Nginx 的测试结果:
Concurrency Level:      100
Time taken for tests:   0.420 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      709000 bytes
HTML transferred:       469000 bytes
Requests per second:    2380.97 [#/sec] (mean)
Time per request:       42.000 [ms] (mean)
Time per request:       0.420 [ms] (mean, across all concurrent requests)
Transfer rate:          1648.54 [Kbytes/sec] received

8080 端口,也就是 Tomcat 的测试结果:
Concurrency Level:      100
Time taken for tests:   0.376 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      714000 bytes
HTML transferred:       469000 bytes
Requests per second:    2660.06 [#/sec] (mean)
Time per request:       37.593 [ms] (mean)
Time per request:       0.376 [ms] (mean, across all concurrent requests)
Transfer rate:          1854.77 [Kbytes/sec] received

经过多次测试,得出清单 5的较为稳定的结果。可以看到在使用 Nginx 实现动静分离以后,动态请求的吞吐率的确会有所下降(因为多了一层转发)。然而,对于网站整体性能来说,静态资源的高吞吐率,以及未来可以实现的负载均衡、可扩展性、高可用性等优势,该牺牲是值得的。

任何技术都是有利有弊,动静分离也不例外。选择了动静分离,就意味着选择了更为复杂的系统架构,维护成本在一定程度上会增加。但鉴于其带来的性能提升,这仍是很多系统架构师会选择的一种解决方案。

HTTP 持久连接

持久连接(Keep-Alive)也叫做长连接,它是一种 TCP 连接方式。连接会被浏览器和服务器所缓存,在下次连接同一服务器时,缓存的连接被重新使用。由于 HTTP 的无状态性,传统的 HTTP 通信往往是“一次性”的。持久连接减少了创建连接的开销,提高了性能。HTTP/1.1 已经支持长连接,大部分浏览器和服务器也提供了支持。

要想发起长连接,服务器和浏览器必须共同合作。一方面浏览器要保持连接,另一方面服务器也不会断开连接。也就是说,双方需要进行协商,而协商机制依赖于 HTTP 协议。协商结构如图 7所示。

图 7. 长连接协商

图 7. 长连接协商

  • 浏览器在请求头部添加 Connection: Keep-Alive,告诉服务器“我支持长连接”。
  • 倘若服务器支持,则在响应头部添加 Connection: Keep-Alive,确认建立长连接。
  • 服务器还可以通过 Keep-Alive: timeout=10, max=100 头部告诉浏览器超时时间和最大请求数。

Tomcat 配置

在 Tomcat 里允许配置长连接。编辑 conf/server.xml 文件,配置 Connector 节点。该节点负责控制浏览器与 Tomcat 的连接,其中与长连接直接相关的有两个属性:

  • keepAliveTimeout:表示在 Connector 关闭连接前,为另外一个请求 Keep Alive 所等待的毫秒数。默认值和 connectionTimeout 一样。
  • maxKeepAliveRequests:表示 HTTP/1.0 Keep Alive 和 HTTP/1.1 Keep Alive / Pipeline 的最大请求数目。如果设置为 1,将会禁用 Keep Alive 和 Pipeline;如果设置为小于 0 的数,Keep Alive 的最大请求数将没有限制。

也就是说在 Tomcat 里,默认长连接是打开的。当我们想关闭长连接时,只要将 maxKeepAliveRequests 设置为 1 即可。

配置验证

首先将 maxKeepAliveRequests 设置为 20,keepAliveTimeout 为 10000,通过 Firefox 查看请求头部(访问上面提到的 test.jsp)。结果如图 8所示。

图 8. 服务器打开长连接

图 8. 服务器打开长连接

接下来,将 maxKeepAliveRequests 设置为 1,并且重启服务器,再次请求网页后查看的结果如图 9所示。

图 9. 服务器关闭长连接

图 9. 服务器关闭长连接

对比可以发现,Tomcat 关闭长连接后,在服务器的请求响应中明确标识了:Connection: close,告诉浏览器服务器并不支持长连接。

性能测试

长连接究竟可以带来怎样的性能提升?我们用数据说话。依然使用 AB 工具,它可以使用 -k 参数模拟浏览器使用 HTTP 的 Keep-Alive 特性。对 http://localhost:8080/seperate/jquery.gif 进行测试,结果如清单 6所示。

清单 6. AB 测试长连接

测试脚本:ab -k -c 1000 -n 10000 http://localhost:8080/seperate/jquery.gif

关闭长连接时:
Concurrency Level:      1000
Time taken for tests:   5.067 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    0
Total transferred:      44600000 bytes
HTML transferred:       42130000 bytes
Requests per second:    1973.64 [#/sec] (mean)
Time per request:       506.678 [ms] (mean)
Time per request:       0.507 [ms] (mean, across all concurrent requests)
Transfer rate:          8596.13 [Kbytes/sec] received
  
打开长连接时,maxKeepAliveRequests 设置为 50:
Concurrency Level:      1000
Time taken for tests:   1.671 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    10000
Total transferred:      44650000 bytes
HTML transferred:       42130000 bytes
Requests per second:    5983.77 [#/sec] (mean)
Time per request:       167.119 [ms] (mean)
Time per request:       0.167 [ms] (mean, across all concurrent requests)
Transfer rate:          26091.33 [Kbytes/sec] received

结果令人惊讶。使用长连接和不使用长连接的性能对比,对于 Tomcat 配置的 maxKeepAliveRequests 为 50 来说,性能提升了将近 5 倍。可见服务器默认打开长连接是有原因的。

HTTP 协议的合理使用

很多程序员将精力专注在了技术实现上,认为性能的高低完全取决于代码的实现,却忽略了已经成型的规范、协议和工具。最典型的就是在 Web 开发上,部分开发人员没有意识到 HTTP 协议的重要性。其实,HTTP 协议可以提供另一条性能优化之路。通过简单地在 JSP 的 request 对象中添加响应头部,往往可以迅速提升程序性能。本系列文章的宗旨也在于让程序员编最少的代码,提升最大的性能。

本文提出一个需求:在前面部分提到的 test.jsp 中,它的一部分功能是显示服务器的当前时间。现在我们希望这个动态网页允许被浏览器缓存。这似乎有点不合理,但在很多场景下,虽然是动态网页,却只执行一次(例如网页的主菜单存入数据库,不希望每次加载菜单都去读数据库)。浏览器缓存带来的性能提升众人皆知,而缓存过期时间、缓存删除、什么页面可以缓存等,都可以由程序员控制。只要熟悉 HTTP 协议,就可以轻松控制浏览器。

访问上面提及的 test.jsp,用 Firebug 查看请求情况,发现每次请求都会重新到服务器下载内容。这不难理解,因为 test.jsp 是动态内容,每次服务器必须执行后才可以返回结果。图 10是访问当前的 test.jsp 的头部情况。

图 10. 修改 test.jsp 前的访问头部情况

图 10. 修改 test.jsp 前的访问头部情况

现在我们往 test.jsp 添加清单 7的内容。

清单 7. 在 test.jsp 的首部添加的代码

<%
SimpleDateFormat f2=new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
String ims = request.getHeader("If-Modified-Since");
if (ims != null)
{
try
{
Date dt = f2.parse(ims.substring(0, ims.length()-4));
if (dt.after(new Date(2009, 1, 1)))
{
response.setStatus(304);
return;
}
} catch(Exception e)
{
 
}
}
response.setHeader("Last-Modified", f2.format(new Date(2010, 5, 5)) + " GMT");
%>

上述代码的意图是:服务器获得浏览器请求头部中的 If-Modified-Since 时间。这个时间是浏览器询问服务器,它所请求的资源是否过期。如果没过期就返回 304 状态码,告诉浏览器直接使用本地的缓存。

修改完 test.jsp 代码后,使用鼠标激活浏览器地址栏,按下回车刷新页面。这次的结果如图 11所示。

图 11. 修改 test.jsp 后的首次访问

图 11. 修改 test.jsp 后的首次访问

可以看到图 11图 10的请求报头没有区别,而在服务器的响应中,图 11增加了 Last-Modified 头部,这个头部告诉浏览器可以将此页面缓存。

按下 F5(必须是 F5 刷新),F5 会强制 Firefox 加载服务器内容,并且发出 If-Modified-Since 头部。得到的报头结果如图 12所示。

图 12. 修改 test.jsp 后的再次访问

图 12. 修改 test.jsp 后的再次访问

可以看到,图 12的底部已经提示所有内容都来自缓存。浏览器的请求头部多出了 If-Modified-Since,以此询问服务器从缓存时间起,服务器是否对资源进行了修改。服务器判断后发现没有对此资源(test.jsp)修改,就返回 304 状态码,告诉浏览器可以使用缓存。

我们在上面的实验中,用到了 HTTP 协议的相关知识,其中涉及了 If-Modified-SinceLast-Modified、304 状态码等。事实上与缓存相关的 HTTP 头部还有许多,诸如过期设置的头部等。熟悉了 HTTP 头部,就如同学会了如何与用户的浏览器交谈,也可以利用协议提升您的程序性能。

那么对于 test.jsp 这个小网页来说,基于缓存的方案提升了多少性能呢?我们用 AB 给您答案。AB 是个很强大的工具,它提供了 -H 参数,允许测试人员手动添加 HTTP 请求头部。测试结果如清单 8所示。

清单 8. AB 测试 HTTP 缓存

测试脚本:ab -c 1000 – n 10000 – H ' If-Modified-Since: Sun, 05 Jun 3910 00:00:00 GMT ' http://localhost:8080/seperate/test.jsp

未修改 test.jsp 前 :
Document Path:          /seperate/test.jsp
Document Length:        362 bytes
Concurrency Level:      1000
Time taken for tests:   10.467 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      6080000 bytes
HTML transferred:       3630000 bytes
Requests per second:    955.42 [#/sec] (mean)
Time per request:       1046.665 [ms] (mean)
Time per request:       1.047 [ms] (mean, across all concurrent requests)
Transfer rate:          567.28 [Kbytes/sec] received

修改 test.jsp 后:
Document Path:          /seperate/test.jsp
Document Length:        0 bytes
Concurrency Level:      1000
Time taken for tests:   3.535 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Non-2xx responses:      10000
Total transferred:      1950000 bytes
HTML transferred:       0 bytes
Requests per second:    2829.20 [#/sec] (mean)
Time per request:       353.457 [ms] (mean)
Time per request:       0.353 [ms] (mean, across all concurrent requests)
Transfer rate:          538.76 [Kbytes/sec] received

分别对比 Document LengthRequests per second 以及 Transfer rate 这三个指标。可以发现没使用缓存的 Document Length(下载内容的长度)是 362 字节,而使用了缓存的长度为 0。在吞吐率方面,使用缓存是不使用缓存的 3 倍左右。同时在传输率方面,缓存的传输率比没缓存的小。这些都是用到了客户端缓存的缘故。

CDN 的使用

CDN(Content Delivery Network,内容分发网络)也是笔者最近才了解和接触到的技术。在淘宝的前端技术报告上、在一个好朋友的创新工场创业之路上,我都听到了这个词,因此有必要对此技术了解一下。

所谓的 CDN,就是一种内容分发网络。它采用智能路由和流量管理技术,及时发现能够给访问者提供最快响应的加速节点,并将访问者的请求导向到该加速节点,由该加速节点提供内容服务。利用内容分发与复制机制,CDN 客户不需要改动原来的网站结构,只需修改少量的 DNS 配置,就可以加速网络的响应速度。

当用户访问了使用 CDN 服务的网站时,DNS 域名服务器通过 CNAME 方式将最终域名请求重定向到 CDN 系统中的智能 DNS 负载均衡系统。智能 DNS 负载均衡系统通过一组预先定义好的策略(如内容类型、地理区域、网络负载状况等),将当时能够最快响应用户的节点地址提供给用户,使用户可以得到快速的服务。同时,它还与分布在不同地点的所有 CDN 节点保持通信,搜集各节点的健康状态,确保不将用户的请求分配到任何一个已经不可用的节点上。而我们的 CDN 还具有在网络拥塞和失效情况下,能拥有自适应调整路由的能力。

由于笔者对 CDN 没有亲身实践,不便多加讲解,但是各大网站都在一定程度使用到了 CDN,淘宝的前端技术演讲中就提及了 CDN,可见 CDN 的威力不一般。

图 13. 淘宝的 CDN 前端优化

图 12. 淘宝的 CDN 前端优化

因此 CDN 也是不得不提的一项技术。国内有免费提供 CDN 服务的网站:http://www.webluker.com/,它需要您有备案的域名,感兴趣的您可以去试试。

小结

本文总结了 HTTP 长连接、动静分离、HTTP 协议等等。在您需要的时候,可以查看本文的内容,相信按照本文的方法,可以辅助您进行前端的高性能优化。笔者将继续写后续的部分,包括数据库的优化、负载均衡、反向代理等。由于笔者水平有限,如有错误,请联系我批评指正。

接下来在第三部分文章中,我将介绍服务器端缓存、静态化与伪静态化、分布式缓存等,并且将它们应用到 Java Web 的开发中。使用这些技术可以帮助提高 Java Web 应用程序的性能。

    • *

相关主题

说明:本文部分内容源自 IBM DeveloperWorks 早期文章,部分外部链接可能已失效或内容过时(如 Tomcat 版本配置、CDN 服务提供商等)。文中涉及的技术原理(如动静分离、HTTP 缓存、Keep-Alive)依然适用,但具体配置参数请参考当前使用的软件版本官方文档。