互联网的网络架构由C/S架构转变为B/S架构
B/S架构的好处:
- 客户端使用统一的浏览器(Browser)。不需要特殊的配置和网络连接就可以使用所有服务提供商提供的服务。同时也方便了用户的使用。
- 服务端(Server)基于统一的HTTP。C/S架构使用自定义应用层协议,B/S架构使用统一的HTTP协议。使用统一的HTTP协议减少了服务提供商的开发成本。基于HTTP的服务器(如Apache、IIS、Nginx、Tomcat、Jboss)和开发服务的通用框架有很多,使得服务开发者开发成本降低。
1 B/S网络架构
大多数C/S互联网应用程序使用长连接交互模式,而B/S采用应用层的HTTP来交互数据,HTTP采用无状态的短连接方式,即通常情况下,一次请求就完成了一次数据交互,也就是完成了一个业务逻辑,完成之后这次通信连接就断开了。
采用短连接的目的是能够同时服务更多用户,互联网应用每天会处理上亿次用户请求,不可能每个用户访问一次后一直保持这个连接。
用户在浏览器中输入 www.taobao.com 这个URL之后,发生了哪些操作?
首先浏览器请求DNS服务器将域名解析为对应的IP地址;
然后浏览器根据IP地址找到互联网上对应的服务器,向服务器发送一个get请求;
服务器端的服务器可能有很多台,负载均衡(Load Balance)服务器收到get请求后来平均分配所有的用户请求,指定到底哪台服务器来处理请求;
指定的服务器收到请求后根据请求的数据所在的位置(可能存储在分布式缓存中、静态文件中或是数据库中)返回数据资源给用户;
浏览器收到返回的数据后,浏览器解析数据,如果发现数据中有一些静态资源(如CSS、JS或图片),则浏览器又会发起另外的HTTP请求,这些请求很可能会在CDN(content delivery network,内容分发网络,通过在网络各处放置节点服务器,实时的根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上,使得用户可以就近取得所需的内容,解决网络拥挤状况,提高网站响应速度。)上,CDN服务器再处理这个用户请求。
CDN架构图:
不管上面的网络结构如何变化,一些始终遵循的固定原则:
- 互联网上所有资源都要用一个URL表示。
- 必须基于HTTP与服务端交互。不管访问的数据是在国内的还是在国外,是文本数据还是流媒体(传输过程中使用了流技术的多媒体数据,流技术即把连续的影像和声音信息经过压缩处理后放到网站服务器让用户一边下载一边观看收听,不需要将整个压缩文件都下载下来后才可以使用的网络传输技术),都必须按照统一的HTTP请求的方式,这样服务器端才能正确得知请求的是什么。
- 数据的展示必须在浏览器中进行。当用户获取到请求的数据资源后,必须在浏览器上最终解析展示。
2 如何发起一个HTTP请求
发起一个HTTP请求的过程就是建立一个Socket通信的过程。
3 HTTP解析
HTTP是B/S架构的核心。要理解HTTP,最重要的是要熟悉HTTP中的HTTP Header。
什么是HTTP协议?
HTTP(hypertext transfer protocol,超文本传输协议)协议定义了浏览器向WWW服务器请求数据以及服务器向浏览器返回数据的方法。HTTP协议使用了面向连接的TCP作为运输层协议,保证数据的可靠传输,HTTP不考虑数据在传输过程中被丢弃后又怎样重传。HTTP协议本身是无连接的。即虽然HTTP使用了TCP连接,但是通信的双方在交换HTTP报文之前不需要先建立HTTP连接。HTTP协议是无状态的。即同一个用户第二次访问同一个服务器上的资源时,服务器的响应与第一次被访问时的相同(假定请求的数据在服务器上没有更新)。因为服务器不需要记录曾经请求数据的用户信息,也不需记录为该用户返回过几次数据,所以HTTP的无状态特点简化了服务器的设计,使得服务器更容易支持大量并发的HTTP请求。
当浏览器向服务器发送get请求时,HTTP协议规定首先要建立浏览器与服务器之间的TCP连接。这样的连接过程需要三次握手,当三次握手的前两部分完成后(即经过一个RTT(round-trip time,往返时间,表示从发送方发送数据开始,到发送方收到来自接收方的确认总共经历的时间,其间接收方收到数据后立刻发送确认)时间后),浏览器就把HTTP请求报文作为三次握手的第三个报文的数据发送给服务器。服务器收到HTTP请求之后,就把所请求的文档作为响应报文返回给客户。
请求一个WWW资源所需的时间是该资源的传输时间(与资源的大小成正比)加上两倍的RTT(一个RTT用于建立TCP连接,另一个RTT用于请求和接受资源)。这里TCP建立连接的三次握手的第三个报文段中捎带了浏览器对WWW资源的请求。
请求服务器资源所需时间:
HTTP报文结构
HTTP有两类报文:请求报文与响应报文
HTTP是面向文本的(text-oriented),报文中的每一个字段都是一些ASCII码串,因此各个字段的长度都是不确定的。
HTTP请求报文和响应报文都是由三部分组成(两种报文的区别是报文的开始行不同):
(1)开始行(start-line),用于区分是请求报文还是响应报文。请求报文中的开始行叫做请求行(request line),响应报文中的开始行叫做状态行(status line)。开始行的三个字段之间都以空格分隔开,最后的CR与LF分别代表回车与换行。
(2)首部行(message-header),用来说明浏览器、服务器和报文主体的一些信息。首部可以有好几行,也可以不使用。每一个首部行中有首部字段名和对应的值,每一行以回车和换行结束。整个首部行结束时,还有一空行将首部行与实体主体分开。
(3)报文主体(message-body),请求报文中一般不使用这个字段,在响应报文中也可能不使用
HTTP Message:1
2
3
4
5generic-message = start-line
*(message-header CRLF)
CRLF
[ message-body ]
start-line = Request-Line | Status-Line
HTTP请求报文(request message)结构与响应报文(response message)结构:
HTTP请求报文的请求行由三项内容组成:方法(操作)、请求资源的URL、以及HTTP的版本。
HTTP请求报文的一些方法:
方法(操作) | 意义 |
---|---|
OPTION | 请求一些选项的信息 |
GET | 请求读取由URL所标志的信息 |
HEAD | 请求由读取URL所标志的信息的首部 |
POST | 为服务器添加信息 |
PUT | 在指明的URL下存储下一个资源 |
DELETE | 删除指明的URL所标志的资源 |
TRACE | 用来进行环回测试的请求报文 |
CONNECT | 用于代理服务器 |
常见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响应报文的状态行包括三项内容,即HTTP的版本,状态码,以及解释状态码的简单短语。
状态码(status-code)由三位数字组成,分为5大类共33种(见RFC2616):
1xx:表示通知信息,如请求已经收到或正在处理
2xx:表示成功(Successful)
3xx:表示重定向(Redirection),如要完成请求还必须采取进一步的行动
4xx:表示客户端错误(client error),如400客户端请求有语法错误,不能被服务器识别
5xx:表示服务端错误(server error),如500服务器内部错误,服务器发生了意料之外的错误情况导致服务器不能满足客户端发来的请求
常见的HTTP状态码:
状态码 | 意义 |
---|---|
200 | 客户端请求成功 |
301 | Moved permanently(请求的资源被永久转移),跳转的地址通过Location指定 |
302 | 临时跳转(请求的资源被临时转移),跳转的地址通过Location指定 |
400 | 客户端有语法错误,不能被服务器识别 |
403 | 服务器收到请求,但是拒绝提供服务 |
404 | 请求的资源不存在 |
500 | 服务器发生不可预期的错误 |
常见的HTTP响应头:
响应头 | 意义 |
---|---|
Server | 使用的服务器名称,如Server:Apache/1.3.6(Unix) |
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 |
3.2 浏览器缓存机制
当我们浏览一个页面发现有异常情况时,通常考虑的就是是不是浏览器做了缓存,一般的做法是使用ctrl+F5组合键来重新请求该页面,重新请求的最新的页面。ctrl+F5组合键可以保证浏览器会直接向目标URL发送请求而不使用浏览器缓存的数据;同时当我们使用ctrl+F5来刷新页面时,HTTP的请求头中会增加一些请求头,这些请求头用于告诉服务端我们要获取最新的数据而不是缓存,这样可以避免请求发送到服务端时访问到的是服务端缓存的数据(例如应用服务器的前端部署了一个缓存服务器,如Varnish代理,Varnish可能直接使用缓存数据)
使用ctrl+F5组合键后HTTP请求头会发生什么改变
使用ctrl+F5组合键后HTTP请求头中会增加两个请求项:Pragma:no-cache和Cache-Control:no-cache。
未使用ctrl F5组合键的HTTP请求头:
使用了ctrl F5组合键的HTTP请求头:
3.2.1 Cache-control和Pragma两项配置项的作用
该两项HTTP Head字段用于指定所有缓存机制在整个请求/响应链中必须服从的指令。
HTTP head字段的可选值:
可选值 | 意义 |
---|---|
Public | 所有内容都将被缓存,在响应头中设置 |
Private | 内容只缓存到私有缓存中,在响应头中设置 |
no-cache | 所有内容都不会被缓存,在请求头中和响应头中设置 |
no-store | 所有内容都不会被缓存到缓存或者Internet临时文件中,在响应头中设置 |
must-revalidation/proxy-revalidation | 如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证,在请求头中设置 |
max-age=xxx | 缓存的内容将在xxx秒后失效,这个选项只在HTTP1.1中可用,和Last-Modified一起使用时优先级较高,在响应头中设置 |
Cache-Control请求字段被各个浏览器支持得较好,且它的优先级较高,当Cache-Control和其他请求字段(如Expires)同时出现时,Cache-Control会覆盖其他字段。
Pragma字段的作用和Cache-Control类似,也是在HTTP头中包含的一个特殊的指令,对服务器的行为做一个规约,最常用的是Pragma:no-cache,与Cache-Control:no-cache的作用一样。
3.2.2 Expires
Expires通常使用的格式是Expires后面跟一个日期和时间,如Expires:Sat,25 Feb 2012 12:22:17 GMT,表示的意义是超过这个时间,缓存的内容就会失效。浏览器在发出请求之前会检查该页面的这个字段,如果缓存的内容已经失效,即该页面已经过期,就会重新向服务器发起请求。
3.2.3 Last-Modified/Etag
Last-Modified 字段一般用于表示一个服务器上的资源的最后修改时间。服务器在响应头中返回一个Last-Modified字段,告诉该浏览器的最后修改时间,如Last-Modified:Sat,25 Feb 2012 12:55:04 GMT,浏览器再次发送请求时在请求头中添加字段If-Modified-Since:SatSat,25 Feb 2012 12:55:04,询问当前缓存的页面是否是最新的,如果是最新的返回304状态码,告诉浏览器缓存的页面是最新的,服务器也不会返回最新的数据。
Etag字段的功能与Last-Modified字段类似,该字段的作用是让服务器给每一个页面分配一个唯一的编号,通过这个编号来确定缓存的页面是否是最新的。该方式比Last-Modified更加灵活,缺点是当后端的Web服务器有多台时,需要每个Web服务器都记住网站的所有资源,否则浏览器返回的这个编号就失去了存在的意义。
4 DNS域名解析
互联网都是通过域名来发布和请求资源,但是不同计算机之间是通过IP地址来建立网络连接。DNS解析就是将URL中的域名需要解析成IP地址。
4.1 DNS域名解析过程:
当一个用户在浏览器中输入网站域名并按下回车键后,DNS解析有近10个步骤:
第一步:浏览器检查缓存中是否有该域名解析过的IP地址,如果有,则DNS解析过程结束。浏览器中缓存的域名对应的IP地址的大小和时间都是有限制的,通常时间在几分钟到几小时不等,可以通过TTL属性(time to live,记录域名解析结果在DNS服务器上的保留时间)对浏览器中缓存的域名对应的IP地址的存在时间进行设置。缓存时间不宜太长或者太短,如果太长,一旦域名被解析到的IP地址有变化,浏览器中缓存的IP地址即失效,会导致该域名不能正常解析,用户无法正常访问网站;如果缓存的时间太短,会导致用户每次访问网站都要重新解析一次域名,缓存的意义不再。
第二步:如果浏览器中没有该域名对应的解析过的IP地址缓存,则浏览器会检查操作系统的hosts文件。操作系统中的hosts文件缓存了一些域名对应的DNS解析结果。如果hosts文件中指定了一个域名的IP地址,那么浏览器会首先使用这个IP地址。这样的做法带来的好处:我们在网站测试过程中可以把域名解析到一台测试服务器上,这样我们不需要修改任何代码就能测试到单独服务器上代码的业务逻辑是否正确;这样的做法带来的坏处:本地DNS解析的过程可能被黑客利用,黑客可以通过修改本地的域名解析来把特定的域名解析到他指定的IP地址,导致域名劫持。
Windows系统中该本地配置文件的地址在C:\Windows\System32\drivers\etc\hosts,Linux系统中该文件的地址在/etc/hosts,当操作系统解析到这个配置文件中的域名时,操作系统会在缓存中缓存这个解析结果,缓存的时间受该域名的失效时间和缓存的空间大小控制。
第一步和第二步的域名解析过程都是在本机完成的,在本机中无法完成的域名解析,就会请求域名服务器来解析该域名。
第三步:当上面两步无法完成域名解析时,操作系统会把该域名发送给本地区域名服务器,即LDNS,网络配置中的“DNS服务器地址”这一项就设置了LDNS的地址。LDNS在本地互联网中,如果是学校中接入互联网,则所属的LDNS服务器肯定在学校里,如果是在小区中接入电信或移动(SPA,service provider )互联网,则所属的LDNS服务器通常会在城市中不远处的某处。LDNS服务器缓存了域名解析的结果,缓存的时间受域名的失效时间控制,缓存空间不足是域名解析不成功的主要因素。LDNS的性能通常会很好,它承担了域名解析的主要工作,80%的域名解析到这里就完成了。Windows下可以通过ipconfig/all查看LDNS服务器的地址,Linux下可以通过cat /etc/resolv.conf查看。
Windows中查看DNS server地址:
第四步:如果LDNS仍然没有命中请求的域名,则直接到Root Server根域名服务器请求解析。
第五步:根域名服务器返回给本地域名服务器一个所查询域的主域名服务器(gTLD Server, generic top-level domain,通用顶级域)地址。gTLD是国际顶级域名服务器,如.com、.cn、.org等。
第六步:本地区域名服务器LDNS再向上一步返回的gTLD服务器发送请求。
第七步:接受请求的gTLD服务器查找并返回此域名对应的Name Server域名服务器的地址,这个Name Server通常就是注册的域名服务器,例如在某个域名服务提供商申请的域名,则该域名解析任务就由这个域名提供商的服务器来完成。
第八步: Name Server域名服务器会查询存储的域名和IP的映射关系表,在正常的情况下都根据域名得到的IP地址,连通一个TTL值返回给DNS Server域名服务器。
第九步:返回该域名对应的IP和TTL值,LDNS服务器会缓存这个域名和IP地址的对应关系,缓存的时间由TTL控制。
第十步:把解析的结果返回给用户,用户根据TTL值将结果缓存在本地的系统缓存中,域名解析过程结束。
在实际的DNS解析过程中,可能会超过上述10个步骤,如Name Server可能有多级,或者有一个GTM(global traffic manager,全球流量管理器,一种负载均衡组件)来负载均衡控制,这都可能影响域名解析的过程。
DNS域名解析过程:
4.2 跟踪域名解析过程
在Linux和Windows下都可以使用nslookup命令来查询域名解析结果;在Linux下还可以使用dig 域名来查询DNS的解析过程。
Windows命令行下使用nslookup来查看域名解析结果:
4.3清除缓存的域名
DNS域名解析后会在Local DNS Server和本机上缓存解析结果,这两个缓存都是由TTL值和缓存空间的大小控制的,最大缓存时间即TTL值。Local DNS Server的缓存时间就是TTL值,很难人工介入来进行更改。本机缓存可以进行人工清除。
本机缓存的清除方法:
在Windows的命令行中使用ipconfig/flushdns命令来刷新缓存。
在Linux下可以使用/etc/init.d/nscd restart来清除缓存。
在Java应用中,JVM也会缓存DNS的解析结果,这个缓存是在InetAddress类中完成的,这个缓存时间较为特殊,有两种缓存策略:一种是正确解析结果缓存,另一种是失败的解析结果缓存。这两个缓存时间由两个配置项控制,配置项是在%JAVA_HOME%\lib\security\java.security文件中配置的。两个配置项分别是networkaddress.cache.ttl和networkaddress.cache.negative.ttl,它们的默认值分别是-1(永不失效)和10(缓存10秒)。
要修改这两个值有如下几种方式,分别是:直接修改java.security文件中的默认值;在Java的启动参数(运行程序是加上JVM参数)中增加-Dsun.net.inetaddr.ttl=xxx来修改默认值;通过InetAddress类动态修改。
需要特别注意的是,如果我们使用InetAddress类解析域名,必须是单例模式,不然会有严重的性能问题,如果每次都创建InetAddress实例,则每次都要进行一次完整的域名解析,非常耗时。
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记录:MX代表Mail Exchange,就是可以将某个域名下的邮件服务器指向自己的Mail Server,如taobao.com域名的A记录IP地址是115.238.25.xxx,如果将MX记录设置为115.238.25.xxx,即xxx@taobao.com的邮件路由,DNS会将邮件发送到115.238.25.xxx所在的服务器,同时正常通过Web请求的话仍然能解析到A记录的IP地址。
- CNAME记录:全程是Canonical name(别名解析)。所谓的别名解析就是可以为一个域名设置一个或多个别名。即将域名指向另一个域名,再通过另一个域名提供IP地址。
- NS记录:域名服务器记录。意义是为某个域名指定DNS解析服务器,也就是这个域名由指定的IP地址的DNS服务器去解析。如果需要把子域名交给其他DNS服务商解析,就需要添加NS记录。
- TXT记录:为某个主机名或者域名设置说明,该项可以填写任何东西,长度限制255。
5 CDN工作机制
CDN即content delivery network内容分布网络,是构筑在现有Internet上的一种先进的流量分配网络,其目的是通过在现有的Internet中添加一层新的网络架构,将网站的内容发布到最接近用户的网络“边缘”,使得用户可以就近取得所需的内容,提高用户访问网站时网站的响应速度。CDN=镜像(mirror)+缓存(cache)+整体负载均衡(GSLB),可以明显提高Internet中信息流动的效率。
目前CDN都以缓存网站中的静态数据为主,如CSS、JS、图片和静态页面等数据。用户在从主站服务器请求到动态的内容后,再从CDN上下载这些静态数据,从而加速网页数据内容的下载速度,如淘宝有90%以上的数据都是由CDN来提供的。
通常来说CDN要达到以下几个目标:
- 可扩展(Scalability)。性能可扩展性:应对新增的大量数据、用户和事务的扩展能力。成本可扩展性:用低廉的运营成本提供动态的服务能力和高质量的内容分发。
- 安全性(Security)。强调提供物理设备、网络、软件、数据和服务过程中的安全性,减少因为DDoS攻击(distributed denial of service,分布式拒绝服务攻击,将多个计算机联合起来作为攻击平台以成倍提高拒绝服务攻击的威力。拒绝服务攻击即使目标主机停止提供服务,常见方法有迫使被攻击的服务器缓冲区满,不接受新的请求(带宽攻击)或使用IP欺骗,影响网站的正常访问)或者其他恶意行为造成商业网站的业务中断。
- 可靠性、响应和执行(reliability、responsiveness和performance)。服务可用性指能够处理可能的故障和用户体验下降的问题,通过负载均衡及时提供网络的容错机制。
5.1 CDN架构
一个用户访问某个静态文件(如CSS文件),这个静态文件的域名假如为cdn.taobao.com,那么首先要向Local DNS服务器发起请求,一般经过迭代解析之后回到这个域名的注册服务器去解析,一般每个公司都会有一个DNS解析服务器。这时这个DNS解析器通常会把它重新CNAME解析到另一个域名,而这个域名最终会被指向CDN全局中的DNS负载均衡服务器(CDN Server的负载均衡),在由这个GTM来最终分配是哪个地方的访问用户,返回给离这个地方最近的CDN节点。
拿到这个DNS解析结果,用户就会直接去这个CDN节点访问该静态文件,如果该节点中请求的文件不存在,则会再到源站去获取该文件,然后再返回给用户。
5.2 负载均衡
负载均衡(Load Balance)指将工作任务进行平衡,将其分摊到多个操作单元上执行,如图片服务器、应用服务器等,共同完成工作任务。可以提高服务器响应速度及利用效率;避免软件或者硬件模块出现单点失效;解决网络拥塞问题;实现地理位置无关性;为用户提供稳定的网站访问质量。
三种常见的负载均衡架构:链路负载均衡、集群负载均衡和操作系统负载均衡。这几种负载均衡不仅在CDN架构中能使用,在Web服务或者分布式数据集群中同样也能使用。
- 链路负载均衡:通过DNS解析成不同的IP,用户根据IP来访问不同的目标服务器。此时负载均衡是由DNS的解析来完成的,用户最终访问哪个Web Server是由DNS Server来控制的,在这里就是由Global DNS Server来动态解析域名服务。这种DNS解析的优点是用户会直接访问目标服务器而不要经过其他的代理服务器,通常访问速度会更快;缺点是由于DNS在用户本地和LDNS服务器都有缓存,一旦某台Web Server挂掉,很难及时更新用户的域名解析结果,导致用户无法访问该域名的网站。
- 集群负载均衡:分为硬件负载均衡和软件负载均衡。
硬件负载均衡采用一台专门的硬件设备来转发请求,硬件负载均衡的关键就在于这太价格非常昂贵的请求分发设备,如F5,为了安全通常需要一主一备。硬件负载均衡的优点是性能非常好,缺点是请求分发设备价格非常贵,一般公司用不起,还有就是当访问量突然爆发式增长超出服务极限时,不能进行动态扩容。
软件负载均衡是最为广泛使用的一种负载均衡方式,特点是使用成本非常低,直接使用廉价的PC就可以搭建,缺点是一般一次访问请求要经过多次代理服务器,会增加网络响应时间。- 操作系统负载均衡:就是利用操作系统级别的软件中断或者硬件中断来达到负载均衡,如可以设置多队列网卡等实现。
5.3 CDN动态加速
CDN动态加速的原理是在CDN的DNS解析中通过动态的链路探测来寻找最好的一条路径,然后通过DNS的调度将所有请求调度到选定的这条路径上回源,从而提高用户的访问速度,是当前较为流行的一种优化技术。
由于CDN节点是遍布全国,所以用户接入一个CDN节点后,可以选择一条从离用户最近的CDN节点到源站链路最好的路径让用户走。一条简单的方法是在每个CDN节点上从源站下载一个一定大小的文件,看哪条链路总耗时最短,这样可以构成一个链路链表,然后绑定到DNS解析上,更新到CDN的Local DNS。当然选取最佳链路不能只看总耗时,也要考虑增加了的网络带宽的成本以及网络链路安全的因素。