目录
WEB请求过程(http解析,浏览器缓存机制,域名解析,cdn分发)
概述
HTTP解析
HTTP请求头
HTTP响应头
HTTP状态码
浏览器缓存机制
DNS域名解析
DNS解析过程
跟踪域名解析过程的命令
清除缓存的域名
CDN(Content Delivery Network)
CDN架构
负载均衡
CDN动态加速
大型互联网架构案例
架构目标
典型实现
DNS
CDN
LB
WEB APP
SOA
MQ
CACHE
STORAGE
其他
参考
发起一个http请求的过程就是建立一个socket通信的过程。
我们可以模仿浏览器发起http请求,譬如用httpclient工具包,curl命令等方式。
curl "http://www.baidu.com" 返回页面数据
curl -I "http://www.baidu.com" -I查看http响应头的信息
curl -I "http://www.baidu.com" -H "Cookie=......; Accept-Charset=...." -H添加请求头的信息
http header控制着互联网上成千上万的用户的数据的传输。最关键的是,它控制着用户浏览器的渲染行为和服务器的执行逻辑。
firefox可用firebug查看,httpfox工具,fidder工具,chrome&ie自带调试工具等都可以查看。
Accept-Language: zh-cn,zh;q=0.5
Accept-Language表示浏览器所支持的语言类型,分别是中文和简体中文,优先支持简体中文。
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Accept-Charset告诉 Web 服务器,浏览器可以接受哪些字符编码,分别是 GB2312、utf-8 和任意字符,优先顺序是 GB2312、utf-8、*。
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept表示浏览器支持的 MIME 类型,分别是 text/html、application/xhtml+xml、application/xml 和 */*,优先顺序是它们从左到右的排列顺序。
Accept-Encoding: gzip, deflate
Accept-Encoding表示浏览器有能力解码的编码类型,压缩编码是 gzip 和 deflate。
User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
User-Agent(用户代理),简称 UA,它是一个特殊字符串头,使得服务器能够识别客户端使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等
Host: 域名
Host表示请求的服务器网址;
Connection: Keep-Alive
Connection表示客户端与服务连接类型,Keep-Alive表示持久连接;
Server:Apache/1.3.6
使用的服务器名称
Content-Type:text/html;charset=GBK
用来指明接受者的实体正文的媒体类型
Content-Encoding:gzip
与请求报头Accept-Encooding对应,告诉浏览器服务端采用的是什么压缩编码
Content-Language:zh-cn
貌似了资源所用的自然语言,与Accept-Language对应
Content-Length:10527
指明实体正文的长度
Keep-Alive:timeout=5,max=120
保持连接的时间
200 客户端请求成功
302 临时跳转,跳转的地址通过location指定
400 客户端请求语法错误,不能被服务器识别
403 服务器收到请求,但是拒绝服务
404 请求的资源不存在
500 服务器发生不可预期的错误
浏览一个网页时发现有异常的情况下,通常考虑的是是不是浏览器做了缓存,一般的做法可以通过ctrl+F5组合键重新请求这个页面,重新请求的页面肯定是最新的页面。
Cache-Control
must-revalidation/proxy-revalidation 如果缓存失败,请求必须发送到服务器进行重新验证,请求头中设置
no-cache 所有内容都不会被缓存,请求头或者响应头中设置
public 所有内容都将被缓存,响应头中设置
private 内容只缓存到私有缓存中,响应头中设置
no-store 所有内容都不会被缓存到缓存或internet临时文件中,响应头中设置
max-age=10 缓存内容在10秒后失效,只在http1.1中可用和last-modified一起使用时优先级较高,响应头中设置
Pragma
no-cache 请求头中设置,no-cache这个名字有一点误导。设置了no-cache之后,并不是说浏览器就不再缓存数据,只是浏览器在使用缓存数据时,需要先确认一下数据是否还跟服务器保持一致。如果设置了no-cache,而ETag的实现没有反应出资源的变化,那就会导致浏览器的缓存数据一直得不到更新的情况。
html页面中写入(部分浏览器可以支持,所有缓存代理服务器都不支持,代理不解析HTML内容本身。)
Expires
Date头域表示消息发送的时间,时间的描述格式由rfc822定义
Web服务器告诉浏览器在2012-11-28 03:30:01这个时间点之前,可以使用缓存文件。发送请求的时间是2012-11-28 03:25:01,即缓存5分钟。
Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。
Cache-Control策略导向图
Last-Modified/If-Modified-Since
Last-Modified/If-Modified-Since要配合Cache-Control使用。
Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
Etag/If-None-Match(优先级比last-modified高,缓存过期后先判断有没有etag声明再判断有没有last-modified声明)
Etag/If-None-Match也要配合Cache-Control使用。
Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器觉得)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。
PS
使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:
Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。
术语解释:
根域,就是所谓的“.”,其实我们的网址www.baidu.com在配置当中应该是www.baidu.com.(最后有一点),一般我们在浏览器里输入时会省略后面的点,而这也已经成为了习惯。根域服务器我们知道有13台,但是这是错误的观点。根域服务器只是具有13个IP地址,但机器数量却不是13台,因为这些IP地址借助了任播的技术,所以我们可以在全球设立这些IP的镜像站点,你访问到的这个IP并不是唯一的那台主机。
域名后缀,国际顶级域名,有两种划分方式,一种互联网刚兴起时的按照行业性质划分的com.,net.等,一种是按国家划分的如cn.,jp.,等。每个域都会有域名服务器,也叫权威域名服务器。
顶级域或者叫做一级域,baidu.com就是一个顶级域名,而www.baidu.com却不是顶级域名,他是在baidu.com 这个域里的一叫做www的主机。
二级域,三级域,只要我买了一个顶级域,并且我搭建了自己BIND服务器(或者其他软件搭建的)注册到互联网中,那么我就可以随意在前面多加几个域了(当然长度是有限制的)。比如a.www.baidu.com,在这个网址中,www.baidu.com变成了一个二级域而不是一台主机,主机名是a。
域名服务器,能提供域名解析的服务器,上面的记录类型可以是A(address)记录,NS记录(name server),MX(mail),CNAME等。
A记录,A代表的是address,用户可以在此设置子域名并指向到自己的目标主机地址上,从而实现通过域名找到服务器。指向的目标主机地址类型只能使用IP地址,如将taobao.com域名下的item.taobao.com指定到115.238.33.11,order.taobao.com指定到112.134.22.34。A记录可以将多个域名解析到一个IP地址,但不能将一个域名解析到多个IP地址。
MX记录,表示的是Mail Exchange(邮件交换记录),用于将以该域名为结尾的电子邮件指向对应的邮件服务器以进行处理。如:用户所用的邮件是以域名mydomain.com为结尾的,则需要在管理界面中添加该域名的MX记录来处理所有以@mydomain.com结尾的邮件。
说明:MX记录可以使用主机名或IP地址;·MX记录可以通过设置优先级实现主辅服务器设置,“优先级”中的数字越小表示级别越高。也可以使用相同优先级(即随机)达到负载均衡的目的;·如果在“主机名”中填入子域名则此MX记录只对该子域名生效。
CNAME记录,全称是Canonical Name(别名解析)。可以为一个域名设置一个或者多个别名。
说明:CNAME的目标主机地址只能使用主机名,不能使用IP地址;·主机名前不能有任何其他前缀,如:http://等是不被允许的;A记录优先于CNAME记录。即如果一个主机地址同时存在A记录和CNAME记录,则CNAME记录不生效。
NS记录,解析服务器记录,为某个域名指定DNS解析服务器,也就是这个域名由指定的IP地址的DNS域名服务器去解析。例如用户希望由12.34.56.78这台服务器解析news.mydomain.com,则需要设置news.mydomain.com的NS记录。
说明:“优先级”中的数字越小表示级别越高;·“IP地址/主机名”中既可以填写IP地址,也可以填写像ns.mydomain.com这样的主机地址,但必须保证该主机地址有效。如将news.mydomain.com的NS记录指向到ns.mydomain.com,在设置NS记录的同时还需要设置ns.mydomain.com的指向,否则NS记录将无法正常解析;NS记录优先于A记录。即,如果一个主机地址同时存在NS记录和A记录,则A记录不生效。这里的NS记录只对子域名生效。
TXT记录,为某个主机名或域名设置说明,如可以为taobao.com设置TXT记录为“天下第一”这样的说明。
当用户在浏览器中输入域名并按下回车键后
第一步:浏览器会检查缓存中有没有这个域名对应的解析过的IP地址,如果有,这个解析过程就结束了,直接拿到IP进行访问。这个浏览器缓存域名是有限制的,除了缓存大小有限制,缓存的时间也有限制,通常情况下由TTL属性来设置。
第二步:如果用户浏览器缓存中没有,浏览器会查找操作系统中是否有这个域名对应的DNS解析结果。windows中c:/windows/system32/drivers/etc/hosts文件设置,linux中/etc/hosts文件中设置。当解析到这个配置文件中的某个域名时,操作系统会在缓存中缓存这个解析结果。(修改文件后不立即生效的原因)
第三步:在网络配置中都会有“DNS服务器地址”这一项,当前面两步都不能解析时,操作系统会把这个域名发送给设置的DNS服务器(简称LDNS)-local缩写,一般是本地区的域名服务器也可以是自己设置的域名服务器地址,如果命中,那解析就此结束并返回IP并标记为非权威服务器的应答。如是学校的互联网,那么你的DNS服务器肯定在你的学校,如果你是一个小区接入互联网,那这个DNS就是提供给你接入互联网的应用供应商,即电信或联通。windows中能用ipconfig查看DNS服务器地址,linux中cat /etc/resolv.conf查看DNS Server。
第四步:如果LDNS没有命中,LDNS就会向Root Server域名服务器请求解析。LDNS会从配置文件里面读取13个根域名服务器的地址(这些地址是不变的,直接在BIND的配置文件中),然后像其中一台发起请求。
第五步:根服务器拿到这个请求后,知道他是com.这个顶级域名下的,所以就会返回com.域中的NS记录,一般来说是13台主机名和IP(主域名服务器地址即gTLD-国际顶级域名服务器地址),返回给本地域名服务器即LDNS,
第六步:LDNS再向上一步返回的其中一台gTLD服务器发送请求。com.域的服务器(gTLD)发现你这请求是baidu.com这个域的,一查发现了这个域的NS(一般就是你注册的域名服务器),那就返回给你,你再去查。
第七步:LDNS接受gTLD返回的域服务器地址(即域名服务提供商的域服务器)并向其中一台再次发起请求,在baidu.com的域下面查了下有www的这台主机,就把这个IP返回给你了。
第八步:LDNS接受返回的IP和TLL值
第九步:LDNS缓存这个域名和IP的对应关系,缓存时间有TLL控制
第十步:LDNS把解析的结果返回给用户,用户根据TLL值缓存在本地系统缓存中,域名解析结束。
ps:
一般经历从根域名(.)到gTLD Server(.com.)到Name Server(taobao.com.)
DNS的服务器有多个备份,可以从任何一台查询到解析结果。
在实际的DNS解析过程中,可能还不止这10步,如Name Server可能有很多级,或者有一个GTM来负载均衡控制,这都有可能会影响域名解析过程。
nslookup 查询域名的解析结果 linux&windows
dig host +trace 查询DNS的解析过程 linux
ipconfig /flushdns 刷新缓存 windows
/etc/init.d/nscd restart 重启 linux
JVM也会缓存DNS的解析结果,这个缓存是在InetAddress类中完成的,而且这个缓存时间比较特殊,它有两种策略:一种是正确解析结果缓存,另一种是失败的解析结果缓存。这两个缓存时间由两个配置项控制,配置项在%JAVA_HOME%\lib\security\java.security文件中。两个配置项分别是networkaddress.cache.ttl和networkaddress.cache.negative.tll,默认值分别是-1(永不失效)和10(缓存10秒)。
-Dsun.net.inetaddr.tll=***可以来修改默认值,InetAddress类也可以动态修改。
CDN也就是内容分布网络(Content Delivery Network),它是构筑在现有 Internet 上的一种先进的流量分配网络。其目的是通过在现有的 Internet 中增加一层新的网络架构,将网站的内容发布到最接近用户的网络"边缘",使用户可以就近取得所需的内容,提高用户访问网站的响应速度。有别于镜像,它比镜像更智能,可以做这样一个比喻:CDN = 镜像(Mirror)+ 缓存(Cache)+ 整体负载均衡(GSLB)。因而,CDN 可以明显提高 Internet中信息流动的效率。
目前CDN都以缓存网站中的静态数据为主,如CSS、JS、图片和静态页面等数据。用户在从主站服务器请求到动态内容后再从CDN上下载这些静态数据,从而加速网页数据内容的下载速度,如淘宝有90%以上的数据都是由CDN来提供的。
一个用户访问某个静态文件(如CSS文件),这个静态文件的域名假如是cdn.taobao.com,那么首先要向Local DNS服务器发起请求,一般经过迭代解析后回到这个域名的注册服务器去解析,一般每个公司都会有一个DNS解析服务器。这时这个DNS解析服务器通常会把它重新CNAME解析到另外一个域名,重新从.(根域名)发起对这个域名的解析,而这个域名最终会被指向CDN全局中的DNS负载均衡服务器,再由这个GTM来最终分配是哪个地方的访问用户,返回给离这个访问用户最近的CDN节点。
拿到DNS解析结果,用户就直接去这个CDN节点访问这个静态文件了,如果这个节点中所请求的文件不存在,就会再回到源站去获取这个文件,然后再返回给用户。
CDN好处:
解决服务器端的“第一公里”问题
缓解甚至消除了不同运营商之间互联的瓶颈造成的影响
减轻了各省的出口带宽压力
缓解了骨干网的压力
优化了网上热点内容的分布
以上图为例,我们分析CDN的访问步骤:
step1:用户向localDNS发起请求查询输入域名对应的IP地址(若有缓存直接返回,否则去rootDNS查询);
step2:localDNS迭代向rootDNS查询,逐级迭代,rootDNS=>顶级DNS=>权限DNS;
step3:获得权限DNS后,localDNS向权限DNS发起域名解析请求;
step4:权限DNS通常会将域名CNAME【如果有有CNAME则解析CNAME对应的CDN服务,否则的话默认为普通请求,直接返回解析到的IP】到另一个域名,这个域名最终会被指向CDN网络中的智能DNS负载均衡系统;
step5:DNS负载均衡系统通过一些智能算法,将最合适的CDN节点IP地址返回给localDNS;
step6:localDNS将获得的IP地址返回给用户;
step7:用户得到节点的IP地址后,向该节点发起访问请求;
step8:CDN节点返回请求文件,如果该节点中请求的文件不存在,就会再回到源站获取这个文件,然后返回给用户。
负载均衡(Load Balance)就是对工作任务进行平衡、分摊到多个操作单元上执行,如图片服务器、应用服务器等,共同完成工作任务。它可以提高服务器响应速度及利用效率,避免软件或者硬件模块出现单点失效,解决网络拥塞问题,实现地理位置无关性,为用户提供较一致的访问质量。
通常有三种负载均衡架构,分别是链路负载均衡、集群负载均衡和操作系统负载均衡。
链路负载均衡也就是前面提到的通过DNS解析成不同的IP,然后用户根据这个IP来访问不同的目标服务器。负载均衡是由DNS的解析来完成的,用户最终访问哪个Web Server是由DNS Server来控制的,在这里就是由Global DNS Server来动态解析域名服务。这种DNS解析的优点是用户会直接访问目标服务器,而不需要经过其他的代理服务器,通常访问速度会更快。但是也有缺点,由于DNS在用户本地和Local DNS Server都有缓存,一旦某台Web Server挂掉,那么很难及时更新用户的域名解析结构。如果用户的域名没有及时更新,那么用户将无法访问这个域名,带来的后果非常严重。
集群负载均衡是另外一种常见的负载均衡方式,它一般分为硬件负载均衡和软件负载均衡。
硬件负载均衡一般使用一台专门硬件设备来转发请求,硬件负载均衡的关键就是这台价格非常昂贵的设备,如F5,通常为了安全需要一主一备。它的优点很显然就是性能非常好,缺点就是非常贵,一般公司是用不起的,还有就是当访问量陡然增大超出服务极限时,不能进行动态扩容。
软件负载均衡是使用最普遍的一种负载方式,它的特点是使用成本非常低,直接使用廉价的PC就可以搭建。缺点就是一般一次访问请求要经过多次代理服务器,会增加网络延时。两台是LVS,使用四层负载均衡,也就是在网络层利用IP地址进行地址转发。下面三台使用HAProxy进行七层负载,也就是可以根据访问用户的HTTP请求头来进行负载均衡,如可以根据不同的URL来将请求转发到特定机器或者根据用户的Cookie信息来指定访问的机器。
操作系统负载均衡,就是利用操作系统级别的软中断或者硬件中断来达到负载均衡,如可以设置多队列网卡等来实现。
CDN动态加速技术原理是在CDN的DNS解析中通过动态的链路探测来寻找回源最好的一条路径,然后通过DNS的调度将所以请求调度到选定的这条路径上回源,从而加速用户访问的效率。
由于CDN节点是遍布全国的,所有用户接入一个CDN节点后可以选择一条从离用户最近的CDN节点到原站链路最好的路径让用户走。一个简单的原则就是从源站上下载一个指定大小的文件,看哪个链路耗时最短,这样可以构成一个链路列表然后绑定到DNS解析上,更新到CDN的LDNS。
解决问题的通用思路是将分而治之(divide-and-conquer),将大问题分为若干个小问题,各个击破。在大型互联网的架构实践中,无一不体现这种思想。
下面典型的一次web交互请求示意图。
CDN(Content Delivery Network,内容分发网络)部署在网络提供商的机房里面。在用户请求网站服务时,可以从距离自己最近的网络提供商获取数据。比如视频网站和内容网站的热点内容。
如果需要自己搭建CDN系统,有3种主流方案可以选择:
LB(Load Balance,负载均衡)就是将负载(用户的请求)根据某些策略,将负载分摊给多个操作单元执行。该技术可以提供服务器的响应速度以及利用效率,避免出现单点失效。
这里回顾下前面介绍的两个小节,其实本质上把数据分类(根据数据更新频率,分为动态文件,静态文件),并把数据放在离距离用户最近的地方。另外一点就是,在DNS和CDN具体实现时,也是大量使用了负载均衡技术。
常见的负载均衡算法由:RR(Round Robin,轮询),WRR(Weighted RR,加权轮询),Random(随机),LC(Least Connection,最少连接),SH(Source Hash,源址哈希)
在常见的互联网架构中,通常使用软件负载:LVS+HAproxy+WebServer(Nginx)。在部署时,LVS,HAProxy,WebServer都会部署一个集群,用来进行负载均衡。LVS工作在第4层,在网络层利用IP地址进行转发。 HAProxy工作在第7层,根据用户的HTTP请求(比如根据URL,消息头)来进行转发。
在上述实现中,通常还会使用Keepalived+VIP(虚IP) 技术。Keepalived 提供健康检查,故障转移,提高系统的可用性。通过VIP(配置DNS 绑定域名)的形式对网站进行访问。
前端技术:遵循基本的Web前端优化经验,详见Web前端优化最佳实践及工具集锦
介绍。另外还可以使用BigPipe,动态页面静态化,无限滚动的翻页技术等技术提供更好的用户体验。另外一部分就是考虑mobile技术了,这块笔者暂时还没涉足,就不谈了。
后端技术:
网站架构的演进之路,从单一应用架构到垂直应用架构,分布式服务架构以及流动计算架构,越来越体现SOA框架的重要性。这里以优秀的开源实现dubbo为例,简单介绍下。
dubbo的功能介绍见服务治理过程,对dubbo架构详细介绍的有如何学习dubbo源代码和dubbo源代码阅读。
简而言之,就是使用了spring的schema的扩展机制,进而支持自定义dubbo标签;通过类似serviceload机制配置多个可选服务。通过jdk动态代理和Javassist,使服务调用透明化。结合ZooKeeper实现高可用元数据管理。
MQ(Message Queue,消息队列)使服务调用异步化,可以消除并发访问洪峰,提升网站响应速度。 在MQ实现中,笔者写过一篇介绍Kafka的学习笔记,详细介绍见Kafka/Metaq设计思想学习笔记,不再多言。
Cache就是将数据放到距离计算最近的地方,用来加快处理速度。通常对一定时间内的热点数据进行缓存。
在使用缓存时,需要注意缓存预热和缓存穿透问题。
一般海量数据的缓存系统不会使用Java来实现,是因为Java有额外的对象大小开销以及GC压力。所以一般是用ANSI C来实现。目前用的比较火的是Redis,更多介绍请查看Redis资料汇总
在出现NOSQL之前,一统天下的是MySQL分库分表技术。结合类似TDDL等SQL agent技术,也能够执行类似join的操作。后来,就像忽如一夜春风来,出现了很多NOSQL/分布式存储系统产品。
分布式存储系统是分布式系统中最复杂的一部分,相比较SOA,CACHE等框架,它需要解决的问题更加复杂。常见的问题如下:
当笔者阅读完《大规模分布式存储系统原理解析与架构实战》和google的两篇存储论文后,感觉里面的实现细节太多了。如果要写的话,还是后面单独列一片把。所以这里暂且略过。
还有其他方面的知识,等后面积累再多些,再重点写吧,这里仅仅是索引下,读者可以自行略过。