一个网页被显示的前生今世(二)

cover-image

###Go to 一个网页被显示的前生今世(一)

接上文

##服务器处理请求

####前台处理
当一个请求发送至服务器的时候, 应该由一个前台程序来接收这个请求并选择直接响应静态资源(静态站)还是交给后台程序进一步处理(动态网站). 这里常用的软件有Apache(阿帕奇)/Nginx, 对于大并发的应用, nginx有天然的优势.

我们来举个栗子, Apache2的默认并发连接数是200, 而nginx是直接开到65535(linux文件系统的极限). 虽然在安全性上面阿帕奇优势明显, 不过业内各种大牛(taobao/tencent)基本上都是采用直接改造nginx来满足高并发需求的.

####后台处理
动态网站的后台程序通常能够接受刚才发送的header信息, 并对数据库(通常是SQL当然还有NoSQL)进行读取啦, 添加修改删除等等操作, 并返回适当的响应.

##响应
OK, 那么我们的服务器顺利响应了正确的信息, 高兴地发出了一个HTTP/200的信号说明 Anything goes well.

报头中的Content-Type说明了返回的MIME类型, 这里是一个html文档, 这使得浏览器清楚将该响应内容以HTML的方式进行解析.Expires头表示存在时间,它允许浏览器在这个时间之前不去检查(发请求)而是放心地读取缓存, 正确的使用Expires这可以使得服务器的鸭梨大大减轻.还有一些头的信息没有列出, 大家可以参阅高性能网站建设指南(O’REILLY).

##浏览器解析HTML
好吧, 绕了一圈又说回浏览器了. 做前端的同学都懂的, 浏览器兼容性可以说是最头疼的东西.

####HTML逐步呈现
我们知道, 在IE中, MS提供了一个进度指示器来让用户知道我们的网页正在被打开. 这里的进度指示器有一个优势就是: 它让用户知道系统没有崩溃, 而是正在努力为他或她解决问题.它不仅指出了还需要等多久, 还给用户一些可以看的图形化的东西, 以使得等待不那么枯燥无味. 而通常情况下, 在这个指示器没有跑完的时候, 大部分网页就已经显示了. 所以, 我们说其实浏览器是逐步加载网页的.

当然也有特例, 我们称之为白屏, 将样式表(CSS)放在页面底部将会阻塞内容逐步呈现, 虽然实际加载页面的时间没有增加, 但是让用户感觉缓慢的页面通常会招致反感, 用户经常会不知道发生了什么而离开!

所以无论如何前端开发者都应该遵循的一个原则就是:

1
LINK标签将样式表放在HTMLhead标签中!

####其它对象
在浏览器显示HTML时,它会注意到需要获取其他地址内容的标签(img, css, js, 甚至flash控件, etc)。这时,浏览器会额外的发送获取请求来重新获得这些文件。而这些地址都要经历一个和HTML读取类似的过程。所以浏览器会在DNS中查找这些域名,发送请求,重定向等等…

####JavaScript
虽然我们的浏览器脚本不止Js一种, 但是它已经成为了各种浏览器的事实标准, 下文就用”js”来代替”浏览器脚本”这一表述.

在浏览器解析HTML的时候, 经常会碰到一个script标签, 这个时候浏览器通常会被阻塞来运行js代码, 因为js代码中有可能带有对DOM的一些操作, 如果js代码的运行跟html的解析同时进行, 会导致各种错误, 所以不管是什么js代码, 这个过程是阻塞式的.

我经常看到一些刚学js的同学在头部的代码中这样写道:

1
2
3
var ele = document.getElementById("ele");
// ...
// code here

然后跑过来问为什么代码没反应. DOM压根就没有加载完整!

随后这样写道:

1
2
3
4
5
window.onload = function(){
var ele = document.getElementById("ele");
// ...
// code here
}

我们创造js code的目的通常是执行客户端代码, 也就是说多是与用户的交互. 既然这样, 那为何不把js放在最后加载呢?

1
最佳情况: 将js代码放在文档的底部(body标签闭合之前)

当然还有一种更好的方式, async, 我们在下面讲.

##ASYNC Request(异步请求)
好了, 网页加载完成了, 是不是这个时候网页就”死了”呢? 答案是 NO!绝不!

前端的同学都听说过一个名词 AJAX. OK, 引用一句话, 在Web 2.0伟大精神的指引下,页面显示完成后客户端仍与服务器端保持着联系.

继续举栗子, 当你在百度的搜索框中输入一个关键词的时候, 是不是会跳出一个实时变化的候选热门词表, 这些内容当然是从百度的服务器中实时获取的!

我们可以简单的使用Chrome开发者工具的Network窗口来监视这些ajax请求, 如果在windows下还可以用fiddler这个工具(以前Ricter逗比介绍用的).

当然, 如果我们用异步模式来加载一些并不是立刻被需要的又很臃肿的js脚本, 整个世界将会变得很nice.

还有一点就是轮询, 它的工作方式注定了它的效率不会很高, 长连接好像是一个折衷的方法, 当被轮询时服务器没有新消息它就不理会客户端, 反之在没有超时的情况下, 新消息就会做为响应返回给客户端, 这很适合网络聊天室之类的应用, 虽然大家都用QQ了谁还会用网络聊天室呢, 也许网页游戏喜欢用这个。

最近研究Nodejs的时候发现了另外一种实时性很好的技术, websocket, 它的标准更迭貌似很快, 我用了一个叫做socket.io的库来实现它发现很好用, 不知道有没有懂的大牛来讲解一下ws的实现>.o

##Summury
本文从前端的角度简单的讲了讲自己对网络应用的模式理解, 不过我也算是个初学者, 这里难免有出错的地方欢迎客官指正哈. 也希望对那些不甚明白的同学有所帮助.

EOF.

一个网页被显示的前生今世(一)

cover-image

作为一个前端开发人员, 首先对于浏览器, 以及各种WEB服务的工作机理要有一个清楚的认识. 从你在地址栏输入网址的那一刻起到网页显示在你的显示器上, 这期中到底发生了什么, 本文将浅显地讨论这方面的机理.

##先来讲点题外话.URI(URL)

####什么是URI
就中文翻译通用资源标识符(URI)来讲, 或者我更喜欢叫它统一资源定位器(URL), 似乎很难把它与网址挂上钩. 实际上, 网址就是一种基本的URL. 不知道有多少人思考过, 我们经常输入的网址格式一般都是www.xxxx.com, 或者再加上协议名 http https 端口以及路径什么的 或者再加上账号密码!如下图:
URL

第一部分:协议名(以单个冒号结束)
第二部分:用户信息 也就是账号密码!(登陆ftp时常用)
第三部分:主机名(也就是域名)
第四部分:端口
第五部分:查询,这里有个bug。。。 应该是?号后的内容才是查询!
第六部分:片段ID(是不会发送到服务器的!)

浏览器解析URL之后, 采用相应的协议和地址来向服务器发送信息.

当然浏览器对残缺的URL也具有一定的解析(矫正把..)能力, 比如 http:www.baidu.com 也是可以解析的..

我们发散一下思维, 对于一些协议, 比如mongodb:// ssh://也可以用user:password@domain的方式来发送认证.

####PS:至于URI与URL的区别, 据说业内还在争论, 所以暂且就看做一样好了…

##DNS查询
浏览器开始向对应的地址发送请求了, 第一步是通过访问的域名找出DNS地址.

PS: 如202.202.43.125这样奇葩的IP网址就不用了…

DNS查找过程如下:

  • 浏览器缓存 – 浏览器会缓存DNS记录一段时间。但是,操作系统没有告诉浏览器储存DNS记录的时间,这样不同浏览器会储存个自固定的一个时间(2分钟到30分钟不等)。

  • 系统缓存 – 如果在浏览器缓存里没有找到需要的记录,浏览器会操作系统发出一个调用, 系统从它的DNS缓存中取出对应的缓存, 例如Windows下可以通过 ipconfig /dnsflush 命令来清除这个缓存。如果系统缓存中也没有记录, 系统还会在hosts文件中查找这个记录。

  • 路由器缓存 – 接着,前面的查询请求发向路由器,它一般会有自己的DNS缓存。
    ISP DNS 缓存 – 接下来要check的就是ISP缓存DNS的服务器。在这一般都能找到相应的缓存记录。

  • 递归搜索 – 你的ISP的DNS服务器从跟域名服务器开始进行递归搜索,从.com顶级域名服务器到Facebook的域名服务器。一般DNS服务器的缓存中会有.com域名服务器中的域名,所以到顶级服务器的匹配过程不是那么必要了。递归的顶层是全球只有7台的DNS根服务器, 前几天的国内根服务器故障影响的余波可是还没消散呢→_→

##向web服务器发送HTTP请求
浏览器向我们的服务器发送了一个GET请求!
我们使用Chrome的开发者工具来看一下这个请求头(header)

HTTP HEADER

我们可以看到Request Method暴露了它是一个GET方式的请求.而Request URL定义了要读取的URL

浏览器定义的User-Agent头表明了请求发起者的身份, 这使得服务器能够根据不同的系统环境,浏览器来响应不同的内容. Connection头要求服务器为了后边的请求不要关闭TCP连接。 另外AcceptAccept-Encoding头标明了浏览器希望接受的响应类型.

请求中也包含了浏览器存储的该域的cookies。可能你已经知道,在不同页面请求当中,cookies是与跟踪一个网站状态相匹配的键值。这样cookies会存储登录用户名,服务器分配的密码和一些用户设置等。Cookies会以特殊文本的形式存储在本地,并且每次请求时发送给服务器。

##301重定向
观察仔细的同学会发现, 如果我们在浏览器的地址栏输入baidu.com, 打开的网页总是www.baidu.com, 难道是浏览器的智能解析吗? 其实不然, 在这里服务器捣了一次鬼. 它发送了一个301重定向响应, 这样浏览器就会访问“http://www.baidu.com/” 而非“http://baidu.com/”。

HTTP/1.1 301 Moved Permanently

####为什么需要301重定向呢?

为什么服务器一定要重定向而不是直接发会用户想看的网页内容呢?这个问题有好多有意思的答案。
其中一个原因跟搜索引擎排名SEO有关。你看, 如果一个页面有两个地址,就像 http://www.baidu.com/http://baidu.com/ ,搜索引擎会认为它们是两个网站,结果造成每一个的搜索链接都减少从而降低排名。而搜索引擎知道301永久重定向是什么意思,这样就会把访问带www的和不带www的地址归到同一个网站排名下。

####前端的童鞋需要注意什么
让我们回忆一下如何写一个超级链接? 超简单 是不是! 其实这里有一个隐含的301重定向. 我们在写a标签(锚)的时候经常会这样写<a href="http://www.baidu.com">Baidu.</a>

服务器会自动地将 http://www.baidu.com 跳转到 http://www.baidu.com/ , 不要说看不出区别而跳转总是需要时间的, 在这些无关紧要的地方浪费时间实在是太可耻了是不是→_→

多次301的坏处总是大于好处的. 它不仅会导致打开网站的性能变差, 而且它的滥用会导致搜索引擎对网站的降权. 因为当一个页面有好几个名字时,它可能会在缓存里出现好几次。

##服务器处理请求
Web服务器端由apache/nginx…将各种请求交给响应的后台程序来处理….

##未完待续

###> Go to 一个网页被显示的前生今世(二)

没什么好总结的寒假的寒假总结

好吧, 那我还是来装作总结一下把.

##看的书

  • 犀牛 看样子ECMA核心要经常翻才行

  • 正则 (进度10%, 还无法实战…)

  • Nodejs/Npm(这里指各种module的)/MongoDB的文档 (这也算?算吧)…

  • 好像没了0.0 够少了把…

##写的东西

ok, 那没看什么大概都去写什么了把, 看看github的contributions还算有点

  • NB-Git: 这是一个用nodejs写的blog, 还不是特别完善

  • The Good Parts: 这个是用PHP写的老套路博客系统, 没后台, 但是对这个style风格还算满意

  • CoverFlow: 模仿Apple的CoverFlow效果的图库页面

还有什么..去我的git主页上找找把…

##Others

另外还折腾了个VPS, 学了半个月的Linux, 大概大半个暑假是花在ssh上面了, 真是悲剧- -

大概就是这样把…