1 深入Web请求过程
1.1 B/S网络架构概述
- B/S 网络架构从前端到后端都得到了简化,都基于统一的应用层协议HTTP来交互数据,与大多数传统C/S互联网应用程 序采用的长连接的交互模式不同,HTTP采用无状态的短连接的通信方式,通常情况下,一次请求就完成了一次数据交互,通常也对应一个业务逻辑,然后这次通信连接就断开了。采用这种方式是为了能够同时服务更多的用户,因为当前互联网应用每天都会处理上亿的用户请求,不可能每个用户访问一次后就一直保持这个连接。
- 当一个用户在浏览器里输入www.taobao这个URL时,将会发生很多操作。首先它会请求DNS把这个域名解析成对应的IP地址,然后根据这个IP地址在互联网上找到对应的服务器,向这个服务器发起一个get请求,由这个服务器决定返回默认的数据资源给访问的用户。在服务器端实际上还有很复杂的业务逻辑:服务器可能有很多台,到底指定哪台服务器来处理请求,这需要一个负载均衡设备来平均分配所有用户的请求;还有请求的数据是存储在分布式缓存里还是一个静态文件中,或是在数据库里;当数据返回浏览器时,浏览器解析数据发现还有一些静态资源(如CSS、JS或者图片)时又会发起另外的HTTP请求,而这些请求很可能会在CDN上,那么CDN服务器又会处理这个用户的请求,大体上一个用户请求会涉及这么多的操作。每一个细节都会影响这个请求是否会成功。
- 一些固定不变的原则需要遵守:
- 互联网上所有资源都要用一个URL来表示。
- 必须基于HTTP与服务端交互。
- 数据展示必须在浏览器中进行。
- 图1-1 CDN架构图
1.2 如何发起一个请求
- 发起一个HTTP请求的过程就是建立一个Socket通信的过程。
- 下面是一个基本的HttpClient的调用实例:
HttpClient httpClient = createHttpClient();
PostMethod postMethod;
String domainName = Switcher.domain;
postMethod = new PostMethod(domainName);
postMethod.addRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=gbk");
for (FileterDate filterDate : filterDatas) {
postMethod.addParameter("ip", filterData.ip);
postMethod.addParameter("count", String.valueOf(filterData.count));
}
try {
httpClient.executeMethod(postMethod);
postMethod.getResponseBodyAsString();
} catch (Exception e) {
logger.error(e);
}
- Linux中的curl命令,通过curl + URL就可以简单地发起一个HTTP请求。
1.3 HTTP解析
- 要理解HTTP,最重要的是熟悉HTTP Header, 它控制着用户浏览器的渲染行为和服务器的执行逻辑。
- 常见的HTTP请求头
请求头 | 说明 |
---|---|
Accept-Charset | 用于指定客户端接受的字符集 |
Accept-Encoding | 用于指定可接受的内容编码,如Accept-Encoding:gzip.deflate |
Accept-Language | 用于指定一种自然语言,如Accept-Language:zh-cn |
Host | 用于指定被请求资源的Internet主机和端口号,如Host:www.taobao.com |
User-Agent | 客户端将它的操作系统、浏览器和其他属性告诉服务器 |
Connection | 保持连接是否保持,如Connection:Keep-Alive |
- 常见的HTTP响应头
响应头 | 说明 |
---|---|
Server | 使用的服务器名称,如Server:Apache/1.3.6 |
Content-Type | 用来指明发送给接收者的实体正文的媒体类型,如Content-Type:text/html;charset=GBK |
Content-Encoding | 与请求报头Accept-Encoding对应,告诉浏览器服务器采用什么压缩编码 |
Content-Language | 描述了资源所用的自然语言,与Accept-Language对应 |
Content-Length | 指明实体正文的长度,用以字节方式存储的十进制数字来表示 |
Keep-Alive | 保持连接的时间,如Keep-Alive:timeout=5, max=120 |
- 常见的HTTP状态码
状态码 | 说明 |
---|---|
200 | 客户端请求成功 |
302 | 临时跳转,跳转的地址通过location指定 |
400 | 客户端请求有语法错误,不能被服务器识别 |
403 | 服务器收到请求,但是拒绝提供服务 |
404 | 请求的资源不存在 |
500 | 服务器发生不可预测的错误 |
1.3.1 查看HTTP信息的工具
- 在Firefox浏览器下,使用最多的是Firbug
- Chrome浏览器通过F12键打开调试工具
1.3.2 浏览器缓存机制
- 在我们浏览一个页面时发现有异常的情况下,通常考虑的就是是不是浏览器做了缓存,所以一般的做法就是按 Ctrl+F5组合键重新请求一下这个页面,重新请求的页面肯定是最新的页面。
- 为什么重新请求就一定能够请求到没有缓存的页面?首先是浏览器端,如果是按Ctrl+F5组合键刷新页面,那么浏览器会直接向目标URL发送请求,而不会使用浏览器缓存的数据;其次即使请求发送到服务端,也有可能访问到的是缓存的数据,比如,在我们的应用服务器的前端部署一个缓存服务器,如Varnish代理,那么Varnish也可能直接使用缓存数据。所以为了保证用户能够看到最新的数据,必须通过HTTP来控制。
1. Cache-Control/Pragma
- 这个HTTP Head字段用于指定所有缓存机制在整个请求/响应链中必须服从的指令,如果知道该页面是否为缓冲,不仅可以控制浏览器,还可以控制和HTTP相关的缓存或代理服务器。
- Cache-Control:no-cache。
- HTTP Head字段的可选值
可选值 | 说明 |
---|---|
Public | 所有内容都将被缓存,在响应头中设置 |
Private | 内容只缓存到私有缓存中,在响应头中设置 |
no-cache | 所有内容都不会被缓存,在请求头和响应头中设置 |
no-store | 所有内容都不会被缓存或Internet临时文件中,在响应头中设置 |
must-revalidation/proxy-revalidation | 如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证,在请求头重设置 |
max-age=xxx | 缓存的内容将在xxx秒后失效,这个选项只在HTTP 1.1中可用,和Last-Modified一起使用时优先级较高,在响应头中设置 |
- Cache-Control请求字段被各个浏览器支持得较好,而且它的优先级也比较高,它和其他一些请求字段同时出现 时,Cache-Control会覆盖其他字段。
- Pragma字段的作用和Cache-Control有点类似,它也是HTTP头中包含一个特殊的指令,使相关的服务器遵守该指 令,最常用的就是Pragma:no-cache,它和Cache-Control:no-cache的作用是一样的。
2. Expires
- Expires通常的使用格式是Expires:Sat, 25 Feb 2012 12:22:17 GMT, 后面跟着一个日期和时间,超过这个时间值后,缓存的内 容将失效,也就是浏览器在发出请求之前检查这个页面的这个字段,看该页面是否已经过期了,过期了就重新向服务器发起请求。
3. Last-Modified/Etag
- Last-Modified字段一般用于表示一个服务器上的资源的最后修改时间,资源可以是静态或者动态内容,通过这 个最后修改时间可以判断当前请求的资源是否是最新的。
- 一般服务端在响应头中返回一个Last-Modified字段,告诉浏览器这个页面的最后修改时间,如Last-Modified:Sat,25 Feb 2012 12:55:04 GMT,浏览器再次请求时在请求头中增加一个If-Modified-Since:Sat,25 Feb 2012 12:55:04 GMT字段,询问当前缓存的页面是否是最新的,如果是最新的就返回304状态吗,告诉浏览器是最新的,服务器也不会传输新的数据。
- 与Last-Modified字段有类似功能的还有一个Etag字段,这个字段的作用是让服务器给每个网页分配一个唯一的 编号,然后通过这个编号来区分当前这个页面是否是最新的。这种方式比使用Last-Modified更加灵活,但是在后端的Web服务器有多台比较难处理,因为每个Web服务器都要记住网站的所有资源,否则浏览器返回这个编号就没有意义了。
1.4 DNS域名解析
1.4.1 DNS域名解析过程
-
图1-10是DNS域名解析的主要请求过程示例图
-
当一个用户在浏览器中输入www.abc.com时,DNS解析将会有将近10个步骤,这个过程大体描述如下。
-
当用户在浏览器输入域名并按下回车键后,第1步,浏览器会检查缓存中有没有这个域名对应的解析过的IP地址,如果缓存中有,这个解析过程就将结束。域名被 缓存的时间限制可以通过TTL属性来设置。如果缓存时间太长,一旦域名被解析的IP有变化,会导致被客户端缓存的域名无法解析到变化后的IP地址,以致该域名不能正常解析,这段时间内有可能会有一部分用户无法访问网站。如果时间设置太短,会导致用户每次访问网站都要重新解析一次域名。
-
第2步,如果用户的浏览器缓存中没有,浏览器会查找操作系统缓存中是否有这个域名对应的DNS解析结果。其实操作系统也会有一个域名解析的过程,在Window中可以通过C:\Windows\System32\drivers\etc\hosts文件来设置,你可以将任何域名解析到任何能够访问的IP地址。如果你在这里指定了一个域名对应的IP地址,那么浏览器会首先使用这个IP地址。例如,我们在测试时可以将一个域名解析到一台测试服务器上,这样不用修改任何代码就能测试到单独服务器上的代码的业务逻辑是否正确。正式因为有这种本地DNS解析的规程,所以黑客就有可能通过修改你的域名解析来把特定的域名解析到它指定的IP地址上,导致这些域名被劫持。
- 在Linux中这个配置文件是/etc/hosts。
-
前面这两个步骤是在本机完成的,所以图1-10没表示出来。如果在本机中仍然无法完成域名的解析,就会真正请求域名服务器来解析这个域名了。
-
第3步,在我们的网络配置中都会有“DNS服务器地址”,操作系统会把这个域名发送给这里设置的LDNS(本地DNS服务器),也就是本地区的域名服务器。这个DNS通常都提供给你本地互联网接入的一个DNS解析 服务,例如你是在学校接入互联网,那么你的DNS服务器肯定在你的学校,如果你是在一个小区接入互联网的,那这个DNS就是提供给你接入互联网的应用提供商,即电信或联通,也就是SPA,那么这个DNS通常也会在你所在城市的某个角落,通常不会很远。
- 在windows下可以通过ipconfig查询这个地址。
- 在Linux下通过cat /etc/resolv.conf
-
这个专门的域名解析服务器性能都会很好,它们一般都会缓存域名解析结果,当然缓存时间受到域名的失效时间控制的,一般缓存空间不是影响域名失效的主要因素。大约80%的域名解析都到这里就已经完成了,所以LDNS主要承担了域名的解析工作。
-
第4步,如果LDNS仍然没有命中,就直接到Root Server域名服务器请求解析。
-
第5步,根域名服务器返回给本地域名服务器一个所查询域的主域名服务器(gTLD Server)地址。gTLD是国际顶级域名服务,如.com、.cn、.org等,全球只有13台左右。
-
第6步,本地域名服务器(local DNS Server)再向上一步返回的gTLD服务器发送请求。
-
第7步,接受请求的gTLD服务器查找并返回此域名对应的Name Server域名服务器的地址,这个Name Server通常就是你注册的域名服务器,例如你在某个域名提供商申请的域名,那么这个域名解析 任务就由这个域名提供商的服务器来完成。
-
第8步,Name Server域名服务器会查询存储的域名和IP的映射关系表,在正常情况下都根据域名得到目标IP记录,连同一个TTL值返回DNS Server域名。
-
第9步,返回该域名对应的IP和TTL值,Local DNS Server会缓存这个域名和IP的对应关系,缓存的时候由TTL值控制。
-
第10步,把解析的结果返回给用户,用户根据TTL值缓存在本地系统缓存中,域名解析过程结束。
-
在实际的DNS解析过程中,可能还不止这个10个步骤,如Name Server也可能有多级,或者有一个GTM来负载均衡控制,这都有可能会影响域名解析的过程。
1.4.2 跟踪域名解析过程
- 在Linux和Windows下都可以用nslookup命令来查询域名的解析结果。
- 在Linux系统中还可以用dig命令来查询DNS的解析过程。
- 还可通过增加+trace参数跟踪这个域名的解析过程
dig www.taobao.com +trace
1.4.3 清楚缓存的域名
- 主要在两个地方缓存结果,一个是Local DNS Server,另外一个是用户的本地机器。这两个缓存都是TTL值和本机缓存大小控制的,但是最大缓存时间是TTL值,基本上Local DNS Server的缓存时间就是TTL控制的,很难人工介入,但是我们的本机缓存可以通过如下方式清除。
- 在Windows下可以执行ipconfig /fulshdns命令来刷新缓存
- 在Linux下可以通过/etc/inin.d/nscd restart来清除缓存。
- 在java应用中JVM也会缓存DNS的解析结果,这个缓存是在InetAddress类中完成的,而且这个缓存时间还比较特殊,它有两种缓存策略:一种是正确解析结果缓存,另一种是失败的解析结果缓存。这两个缓存时间由两个配置项分别是networkaddress.cache.ttl和networkaddress.cache.negative.ttl,它们的默认值分别是-1(永不失效)和10(缓存10秒)。
- 要修改这两个值同样有几种方式,分别是:直接修改java.security文件中的默认值、在java的启动参数中增加-Dsun.net.inetaddr.ttl=xxx来修改默认值、通过InetAddress类动态修改。
- 在这里还要特别强调一下,如果我们需要用InetAddress类解析域名,必须是单例模式,不然会有严重的性能问题,如果每次都创建InetAddress实例,则每次都要进行一次完整的域名解析,非常耗时。
1.4.4 几种域名解析方式
- 域名解析记录主要分为A记录、MX记录、CNAME记录、NS记录和TXT记录。
- A记录,A代表的是Address,用来指定域名对应的IP地址,如将item.taobao.com指定到115.238.23.xxx,将switch.taobao.com指定到121.14.24.xxx。A记录可以将多个域名解析到一个IP地址,但是不能将一个域名解析到多个IP地址。
- MX记录,表示的是Mail Exchange,就是可以将某个域名下的邮件服务器指向自己的Mail Server,如taobao.com域名的A记录IP地址是115.238.25.xxx,如果将MX记录设置为115.238.25.xxx所在的服务器,而正常通过Web请求的话仍然解析到A记录的IP地址。
- CNAME记录,全称是Canonical Name(别名解析)。所谓的别名解析就是可以为一个域名设置一个或者多个别名。如将taobao.com解析到xulingbo.net,将srcfan.com也解析到xilingbo.net。其中xulingbo.net分别是taobao.com和srcfan.com的别名。跟踪域名解析的”www.taobao.com. 1542 IN CNAME www.gslb.taobao.com“就是CNAME解析。
- NS记录,为某个域名指定DNS解析服务器,也就是这个域名有指定的IP地址的DNS服务器去解析,"gslb.taobao.com. 86400 IN NS gslbns2.taobao.com."就是NS解析。
- TXT记录,为某个主机名或域名设置说明,如可以为xulingbo.net设置TXT记录为”巨山的博客“这样的说明。
1.5 CDN工作机制
- CDN也就是内容分布网络(Content Delivery Network),它是构筑在现有Internet上的一种先进的流量分配网络。其目的是通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的网络”边缘“,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。有别于镜像,它比镜像更智能,可以做这样一个比喻:CDN = 镜像(Mirror) + 缓存(cache) + 整体负载均衡(GSLB)。因而,CDN可以明显提高Internet中信息流动的效率。
- 目前CDN都以缓存网站中的静态数据为主,如CSS、JS、图片和静态页面等数据。用户在从主站服务器请求到动态内容后,再从CDN上下载这些静态数据,从而加速网页数据内容的下载速度,如淘宝有90%以上的数据都是由CDN来提供的。
- 通常来说CDN要达到以下几个目标。
- 可扩展。性能可扩展性:应对新增的大量数据、用户和事务的扩展能力。成本可扩展性:用低廉的运营成本提供动态的服务能力和高质量的内容分发。
- 安全性。强调提供物理设备、网络、软件、数据和服务过程的安全性,减少因为DDos攻击或者其他恶意行为造成商业网站的业务中断。
- 可靠性、响应和执行。服务可用性指能够处理可能的故障和用户体验下降的问题,通过负载均衡及时提供网络的容错机制。
1.5.1 CDN架构
-
通常的CDN架构如图:
-
一个用户访问某个静态文件(如CSS文件),这个静态文件的域名假如是cdn.taobao.com,那么首先要向Local DNS服务器发起请求,一般经过迭代解析后回到这个域名的注册服务器去解析,一般每个公司都会有一个DNS解析服务器。这时这个DNS解析服务器通常会把它重新CNAME解析到另一个域名,而这个域名最终会被指向CDN全局中的DNS负载均衡服务器,再由这个GTM来最终分配是哪个地方的访问用户,返回给离这个访问用户最近的CDN节点。
-
拿到DNS解析结果,用户就直接去这个CDN节点访问这个静态文件了,如果这个节点中所请求的文件不存在,就会再回到源站去获取这个文件,然后再返回给用户。
1.5.2 负载均衡
-
负载均衡就是对工作任务进行平衡、分摊到多个操作单元上执行,如图片服务器、应用服务器等,共同完成工作任务。它可以提高服务器响应速度及利用效率,避免软件或者硬件模式出现 单点失效,解决网络拥塞问题,实现地理位置无关性,为用户提供较一致的访问质量。
-
通常有三种负载均衡架构,分别是链路负载均衡、集群负载均衡和操作系统负载均衡。所谓的链路负载均衡也就是前面提到的通过DNS解析成不同的IP,然后用户根据这个IP来访问不同的目标服务器,如图1-17所示。
-
负载均衡是由DNS的解析来完成的,用户最终访问哪个Web Server是由DNS Server来控制的,在这里就是由Global DNS Server来动态解析域名服务。这种DNS解析的优点是用户直接访问目标服务器,而不需要经过其他的代理服务器,通常访问速度会更快。但是也有缺点,由于DNS在用户本地和Local DNS Server都有缓存,一旦某台Web Server挂掉,就很难及时更新用户的域名解析结构。如果用户的域名没有及时更新,那么用户将 无法访问这个域名,带来的后果非常严重。
-
集群负载均衡是另外一种常见的负载均衡方式,它一般分为硬件负载均衡和软件负载均衡。硬件负载均衡一般使用一台专门的硬件设备来转发请求。如图1-18所示。
-
硬件负载均衡的关键就是这台价格非常昂贵的设备,如F5,通常为了安全需要一主一备。它的优点是很显然就是性能非常好,缺点就是非常贵,一般公司是用不起的,还有就是当访问量陡 然增加超出服务极限时,不能进行动态扩容。
-
软件负载均衡是使用最普遍的一种负载方式,它的特点是使用成本非常低,直接使用廉价的PC就可以搭建。缺点就是一般一次访问请求要经过多次代理服务器,会增加网络延时。
-
图1-19中上面的两台是LVS,使用四层负载均衡,也就是在网络层利用IP地址进行地址转发。下面的三台使用HAProxy进行七层负载,也就是可以根据访问用户的HTTP请求头来进行负载均衡,如可以根据不同的URL来将请求头转发到特定机器或者根据用户的Cookie信息来指定访问的机器。
-
最后一种是操作系统负载均衡,就是利用操作系统级别的软中断或者硬件中断来达到负载均衡,如可以设置多队列网卡等来实现。
-
这几种负载均衡不仅在CDN的集群中能使用,而且在Web服务器或者分布式数据集群中同样也能使用,但是在这些地方后两种使用得要多一点。
1.5.3 CDN动态加速
-
CDN的动态加速技术也是当前比较流行的一种优化技术,它的技术原理就是在CDN的DNS解析中通过动态的链路探测来寻找回源最好的一条路径,然后通过DNS的调度将所有请求调度到选定的这条路径上回源,从而加速用户访问的效率。如图1-20所示。
-
由于CDN节点是遍布全国的,所以用户接入一个CDN节点后,可以选择一条从离用户最近的CDN节点到源站链路最好的路径让用户走。一个简单的原则就是在每个CDN节点上从源站下载一个一定大小的文件,看哪个链路的总耗时最短,这样可以构成一个链路列表,然后绑定到DNS解析上,更新到CDN的Local DNS。当然是否走这个链路并不一定根据耗时这个唯一条件,有时也要考虑网络成本,例如走某个节点虽然可以节省10ms,但是网络带宽的成本却增加了很多,还有其他网络链路的安全等因素也要综合考虑。