B/S架构带来了两方面的好处:
- 客户端使用统一的浏览器,有效地屏蔽了不同服务提供商提供给用户使用服务的差异性。最重要的一点是,浏览器的交互特性使得用户使用它非常简便,且用户行为的可继承性非常强。
- 服务端基于统一的HTTP协议,简化了开发模式,使得服务器开发者可以采用相对规范的开发模式,这样可以大大节省开发成本。
##1.1 B/S网络架构概述## B/S网络架构从前端到后端都得到了简化,都基于统一的应用层协议HTTP来交互数据,HTTP协议采用无状态的短链接的通信方式,通常情况下,一次请求就完成了一次数据交互,通常也对应一个业务逻辑,然后这次通信连接就断开了。采用这种方式是为了能够同时服务更多的用户,因为当前互联网应用每天都会处理上亿的用户请求,不可能每个用户访问一次后就一直保持住这个连接。
当一个用户在浏览器里输入www.taobao.com这个URL时,将会发生如下操作:
- 首先,浏览器会请求DNS把这个域名解析成对应的IP地址。
- 然后,根据这个IP地址在互联网上找到对应的服务器,向这个服务器发起一个get请求,由这个服务器决定返回默认的数据资源给访问的用户。
- 在服务器端实际上还有复杂的业务逻辑:服务器可能有多台,到底指定哪台服务器处理请求,这需要一个负载均衡设备来平均分配所有用户的请求;
- 还有请求的数据是存储在分布式缓存里还是一个静态文件中,或是在数据库里;
- 当数据返回浏览器时,浏览器解析数据发现还有一些静态资源(如:css,js或者图片)时又会发起另外的HTTP请求,而这些请求可能会在CDN上,那么CDN服务器又会处理这个用户的请求;
不管网络架构如何变化,但是始终有一些固定不变的原则需要遵守:
- 互联网上所有资源都要用一个URL来表示。URL就是统一资源定位符。
- 必须基于HTTP协议与服务端交互。
- 数据展示必须在浏览器中进行。
##1.2 如何发起一个请求## 如何发起一个HTTP请求和如何建立一个Socket连接区别不大,只不过outputStream.write写的二进制字节数据格式要符合HTTP协议。
浏览器在建立Socket连接之前,必须根据地址栏里输入的URL的域名,DNS解析出IP地址,再根据这个IP地址和默认80端口与远程服务器建立Socket连接,然后浏览器根据这个URL组装成一个get类型的HTTP请求头,通过outputStream.write发送到目标服务器,服务器等待inputStream.read返回数据,最后断开这个连接。
一句话,发起一个HTTP请求的过程就是建立一个Socket通信的过程。
- 借助开源的HttpClient工具包,模拟浏览器来发起HTTP请求。当然如果你对HTTP协议的数据结构非常熟悉,完全可以自己再实现另外一个HttpClient,甚至可以自己写个简单的浏览器。
- Linux中的curl命令,简单使用:
(1)curl > /dev/null
(2)curl -I 查看这次访问的HTTP协议的头信息
(3)curl 增加HTTP头,可通过-HI选项实现
因缺少Cookie信息,所以上面的访问返回302状态码,必须增加Cookie才能正确访问该连接,如下所示:
##1.3 HTTP协议解析## B/S网络架构的核心是HTTP协议,最重要的就是要熟悉HTTP协议中的HTTP Header,HTTP Header控制着互联网上成千上万的用户的数据传输。最关键的是,它控制着用户浏览器的渲染行为和服务器的执行逻辑。
表1-1 常见的HTTP请求头
表1-2 常见的HTTP响应头
表1-3 常见的HTTP状态码
###1.3.2 浏览器缓存机制### 当我们使用Ctrl+F5组合键刷新一个页面时,首先是在浏览器端,会直接向目标URL发送请求,而不会使用浏览器缓存的数据;其次即使请求发送到服务端,也有可能访问到的是缓存的数据。所以在HTTP的请求头中会增加一些请求头,它告诉服务端我们要获取最新的数据而非缓存。最重要的是在请求头中增加了两个请求项Pragma:no-cache和Cache-Control:no-cache。
1. Cache-Control/Pragma 这个HTTP Head字段用于指定所有缓存机制在整个请求/响应链中必须服从的指令,如果知道该页面是否为缓存,不仅可以控制浏览器,还可以控制和HTTP协议相关的缓存或代理服务器。
表1-4 Http Head字段的可选值
Cache-Control请求字段被各个浏览器支持的较好,而且它的优先级也比较高,它和其他一些请求字段(如Expires)同时出现时,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)或者动态的内容(如Servlet提供了一个getLastModified方法用于检查某个动态内容是否已经更新),通过这个最后修改时间可以判断当前请求的资源是否是最新的。
一般服务器端在响应头中返回一个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域名解析过程### 当用户在浏览器中输入域名并按下回车后,DNS解析过程大体如下:
- 浏览器缓存检查(本机):浏览器会检查缓存中有没有这个域名对应的解析过的IP地址,如果缓存中有,这个解析过程就将结束。浏览器缓存域名也是有限制的,不仅浏览器缓存大小有限制,而且缓存的时间也有限制,通常情况下为几分钟到几小时不等,域名被缓存的时间限制可以通过TTL属性来设置。这个缓存时间太长和太短都不好,如果缓存时间太长,一旦域名被解析到的IP有变化,会导致被客户端缓存的域名无法解析到变化后的IP地址,以致该域名不能正常解析,这段时间内有可能会有一部分用户无法访问网站。如果时间设置太短,会导致用户每次访问网站都要重新解析一次域名。
- 操作系统缓存检查(本机)+hosts解析(本机):如果用户的浏览器缓存中没有,浏览器会查找操作系统缓存中是否有这个域名对应的DNS解析结果。其实操作系统也会有一个域名解析的过程,在Windows中可以通过
C:\Windows\System32\drivers\etc\hosts
文件来设置,你可以将任何域名解析到任何能够访问的IP地址。如果你在这里指定了一个域名对应的IP地址,那么浏览器会首先使用这个IP地址。在Linux中这个配置文件是/etc/hosts,修改这个文件可以达到同样的目的,当解析到这个配置文件中的某个域名时,操作系统会在缓存中缓存这个解析结果,缓存的时间同样是受这个域名的失效时间和缓存的空间大小控制的。 - 本地区域名服务器解析(LDNS):在我们的网络配置中都会有“DNS服务器地址”这一项,这个地址就用于解决前面所说的如果两个过程无法解析时要怎么办,操作系统会把这个域名发送给这里设置的LDNS,也就是本地区的域名服务器。这个DNS通常都提供给你本地互联网接入的一个DNS解析服务,例如你是在学校接入互联网,那么你的DNS服务器肯定在你的学校,如果你是在一个小区接入互联网的,那这个DNS就是提 供给你接入互联网的应用提供商,即电信或者联通,也就是通常所说的SPA,那么这个DNS通常也会在你所在城市的某个角落,通常不会很远。这个专门的域名解析服务器性能都会很好,它们一般都会缓存域名解析结果,当然缓存时间是受域名的失效时间控制的,一般缓存空间不是影响域名失效的主要因素。大约80%的域名解析都到这里就已经完成了,所以LDNS主要承担了域名的解析工作。
- 根域名服务器解析(Root Server):如果LDNS仍然没有命中,就直接到Root Server域名服务器请求解析。
- 根域名服务器返回给本地域名服务器一个所查询域的主域名服务器(gTLD Server)地址。gTLD是国际顶级域名服务器,如.com、.cn、.org等,全球只有13台左右。
- 本地域名服务器(Local DNS Server)再向上一步返回的gTLD服务器发送请求。
- 接受请求的gTLD服务器查找并返回此域名对应的Name Server域名服务器的地址,这个Name Server通常就是你注册的域名服务器,例如你在某个域名服务提供商申请的域名,那么这个域名解析任务就由这个域名提供商的服务器来完成。
- Name Server域名服务器会查询存储的域名和IP的映射关系表,正常情况下都根据域名得到目标IP记录,连同一个TTL值返回给DNS Server域名服务器。
- 返回该域名对应的IP和TTL值,Local DNS Server会缓存这个域名和IP的对应关系,缓存的时间由TTL值控制。
- 把解析的结果返回给用户,用户根据TTL值缓存在本地系统缓存中,域名解析过程结束。
在实际的DNS解析过程中,可能还不止这10个步骤,如Name Server也可能有多级,或者有一个GTM来负载均衡控制,这都有可能会影响域名解析的过程。 ###1.4.2 跟踪域名解析过程### 在Linux系统中还可以使用dig命名来查询DNS的解析过程,如下所示:dig +cmd +trace www.taobao.com
上面清楚地显示了整个域名是如何发起和解析的,从根域名(.)到gTLD Server(.com.)再到Name Server (taobao.com.)的整个过程都显示出来了。还可以看出DNS的服务器有多个备份,可以从任何一台查询到解析结果。 ###1.4.3 清除缓存的域名### 我们知道DNS域名解析后会缓存解析结果,其中主要在两个地方缓存结果,一个是Local DNS Server,另外一个是用户的本地机器。这两个缓存都是TTL值和本机缓存大小控制的,但是最大缓存时间是TTL值,基本上Local DNS Server的缓存时间就是TTL控制的,很难人工介入,但是我们的本机缓存可以通过如下方式清除。
在Linux下可以通过/etc/init.d/nscd restart来清除缓存。如下:
JVM缓存DBS解析结果:在Java应用中JVM也会缓存DNS的解析结果,这个缓存是在InetAddress
类中完成的,而且这个缓存时间还比较特殊,它有两种缓存策略:一种是正确解析结果缓存,另一种是失败的解析结果缓存。这两个缓存时间由两个配置项控制,配置项是在%JAVA_ HOME%\lib\security\java.security
文件中配置的。两个配置项分别是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.241,将switch.taobao.com指定到121.14.24.241。A记录可以将多个域名解析到一个IP地址,但是不能将一个域名解析到多个IP地址。
MX记录,表示的是Mail Exchange,就是可以将某个域名下的邮件服务器指向自己的Mail
CNAME记录,全称是Canonical Name(别名解析),所谓的别名解析就是可以为一个域名设置一个或者多个别名。如将taobao.com解析到xulingbo.net,将srcfan.com也解析到xulingbo.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要达到以下几个目标:
- 可扩展( Scalability):性能可扩展性:应对新增的大量数据、用户和事务的扩展能力;成本可扩展性:用低廉的运营成本提供动态的服务能力和高质量的内容分发。
- 安全性( Security):强调提供物理设备、网络、软件、数据和服务过程的安全性,(趋势)减少因为DDoS攻击或者其他恶意行为造成商业网站的业务中断。
- 可靠性、响应和执行(Reliability、Responsiveness和Performance):服务可用性,能够处理可能的故障和用户体验的下降,通过负载均衡及时提供网络的容错机制。 ###1.5.1 CDN架构###
如图所示,一个用户访问某个静态文件(如CSS文件),这个静态文件的域名假如是cdn.taobao.com,那么首先要向Local DNS服务器发起请求,一般经过迭代解析后回到这个域名的注册服务器去解析,一般每个公司都会有一个DNS解析服务器。这时这个DNS解析服务器通常会把它重新CNAME解析到另外一个域名,而这个域名最终会被指向CDN全局中的DNS负载均衡服务器,再由这个GTM来最终分配是哪个地方的访问用户,返回给离这个访问用户最近的CDN节点。
拿到DNS解析结果,用户就直接去这个CDN节点访问这个静态文件了,如果这个节点中所请求的文件不存在,就会再回到源站去获取这个文件,然后再返回给用户。 ###1.5.2 负载均衡### 负载均衡(Load Balance)就是对工作任务进行平衡、分摊到多个操作单元上执行,如:图片服务器、应用服务器等,共同完成工作任务。它可以提高服务器响应速度及利用效率,避免软件或者硬件模块出现单点失效,解决网络拥塞问题,实现地理位置无关性,为用户提供较一致的访问质量。
通常有三种负载均衡架构,分别是链路负载均衡、集群负载均衡和操作系统负载均衡。所谓链路负载均衡也就是前面提到的通过DNS解析成不同的IP,然后用户根据这个IP来访问不同的目标服务器。如图:
链路负载均衡是由DNS的解析来完成的,用户最终访问哪个Web Server是由DNS Server来控制的,在这里就是由Global DNS Server来动态解析域名服务。这种DNS解析的优点是用户会直接访问目标服务器,而不需要经过其他的代理服务器,通常访问速度会更快。但是也有缺点,由于DNS在用户本地和Local DNS Server都有缓存,一旦某台Web Server挂掉,那么很难及时更新用户的域名解析结构。如果用户的域名没有及时更新,那么用户将无法访问这个域名,带来的后果非常严重。
集群负载均衡是另外一种常见的负载均衡方式,它一般分为硬件负载均衡和软件负载均衡。硬件负载均衡一般使用一台专门硬仵设备来转发请求,如图:
(1)硬件负载均衡的关键就是这台价格非常昂贵的设备,如F5,通常为了安全需要一主一备。它的优点很显然就是性能非常好,缺点就是非常贵,一般公司是用不起的,还有就是当访问量陡然增大超出服务极限时,不能进行动态扩容。
(2)软件负载均衡是使用最普遍的一种负载方式,它的特点是使用成本非常低,直接使用廉价的PC就可以搭建。缺点就是一般一次访问请求要经过多次代理服务器,会增加网络延时。如图:
上面两台是LVS,使用四层负载均衡,也就是在网络层利用IP地址进行地址转发。下面三台使用HAProxy进行七层负载,也就是可以根据访问用户的HTTP请求头来进行负载均衡,如可以根据不同的URL来将请求转发到特定机器或者根据用户的Cookie信息来指定访问的机器。
操作系统负载均衡,就是利用操作系统级别的软中断或者硬件中断来达到负载均衡,如可以设置多队列网卡等来实现。
下一节: