Web 是如何工作的

第一部分:Web是如何工作的

Web组成部分的定义

如果你想知道万维网的秘密,以下是一些你必须理解的重要术语:

客户端:就是一个运行在电脑上,用来连接因特网的程序,比如谷歌浏览器或火狐浏览器。它的主要角色是接受用户的输入,并按用户输入去请求另一台称之为web服务器的电脑。尽管我们通常使用浏览器去访问网页,但是你可以把你的整台电脑当作客户端-服务器模型中的“客户端”。每一台客户端电脑都有唯一标识的地址——IP地址,这样其它电脑就能根据IP地址去识别它。

服务器:连接互联网的机器,它也有一个IP地址。一台服务器是等待其它机器(比如说客户端)的请求,并响应它们。不像你拥有IP地址的电脑(比如说客户端),服务器是安装和运行着特殊的服务器软件,这些软件来告诉服务器如何去回应来自你浏览器的请求。Web服务器的主要功能是存储、处理和传送网页至客户端。服务器有很多种类型,比如Web服务器、数据库服务器、文件服务器和应用程序服务器等等。(在这篇文章中,我们谈论的是Web服务器。)

IP地址:网际协议地址,在TCP/IP网络中,它是一台设备(电脑、服务器、打印机、路由器等)的数字标识。在互联网中的每一台电脑都有一个IP地址,它用来识别与其他电脑的通信。IP地址是由四组数字,通过点来隔开的字符串(比如224.155.65.2)。这也称之为“逻辑地址”。为了定位网络中的设备,通过TCP/IP协议软件把逻辑IP地址转换成一个物理地址,这个物理地址是烧制在你的硬件中的。

ISP:网络服务提供商。ISP是一个在客户端和服务器之间的中间商。对于典型的家庭,ISP通常是“电缆公司”。当你的浏览器接受去www.github.com的请求时,它是不知道去哪里查找www.github.com的,所以这是ISP的工作,ISP通过DNS查找你试图访问的站点所对应的IP地址。

DNS:域名系统。DNS是一个分布式数据库,用来跟进互联网上电脑域名与它们相对应的IP地址记录。在目前不用去操心“分布式数据库”是如何工作的,只要知道DNS的存在使得用户输入的www.github.com可以用一个IP地址代替。

域名:用来标识一个或多个IP地址。用户使用域名(比如www.github.com)去互联网上浏览一个网站。但你在浏览器里敲入域名,DNS查找其对应的IP地址。

TCP/IP:传输控制协议/因特网互联协议。TCP/IP是使用最广泛的通信协议。“协议”只是一系列规则做什么的标准,TCP/IP是用来作为网络间传送数据的标准协议。

端口号:是一个16位的整型数字,用来标识服务器上具体的端口,端口通常是与IP地址关联起来的。它作为识别服务器上特定进程的一种方式,将网络请求转发到服务器上。

主机:连接网络的电脑——它可以是客户端、服务器或者任意类型的设备。每一台主机都有一个唯一的IP地址。就拿www.google.com这个网站来说,主机可以是Web服务器,用来给该网站提供网页服务。主机与服务器通常容易引起混淆,但是注意它们两个是不同的东西。服务器是主机中的一种——它们是一种特指的机器。另一方面,一台主机可能涉及整个组织,该组织提供一个托管服务来维护多个Web服务器。按这个意义来说,你可以一台主机上运行一个服务。

HTTP:超文本传输协议。该协议是浏览器和Web服务器在互联网间通信用的。

URL:统一资源定位符。URL是用来标识一个特定的Web资源。就拿https://github.com/someone这个简单的例子来说,这个URL规定了使用的协议(“https”),主机名(github.com)和文件名(某人的个人资料页面)。一个用户可以拿这个URL,通过HTTP从一个域名为github的主机上获得这个网页资源。

从代码到网页的旅程

现在我们对这旅程中主要名词有了一定的了解,让我们通过github搜索来看下,我们是如何通过在地址栏里输入一个URL到达对应的网页的:

1、在你的浏览器里输入URL

2、浏览器解析这个URL所包含的信息。这个URL包含了使用的协议(“https”),主机名(github.com)以及资源(“/”)。在这个例子中,“.com”后面没有任何东西去指向一个特指的资源,所以浏览器就知道只检索主页。

3、浏览器与ISP通信,通过DNS查找主机名为www.github.com的Web服务器所对应的IP地址。DNS服务将先联系根域名服务器,该服务器将查找https://www.github.com,并取得“.com”顶级域名的域名服务器对应的IP地址,然后将这个IP地址返回给你的DNS服务。DNS服务对“.com”做扩展查询https://www.github.com对应的IP地址。

4、一旦ISP接受到目标服务器对应的IP地址,它就会把这个IP地址发送给你的浏览器。

5、你的浏览器将拿这个IP地址和URL中的端口号(HTTP协议默认端口号是80,HTTPS默认端口号是443),打开一个TCP套接字连接。这时,你的浏览器和服务器就建立了连接。

6、你的浏览器发送一个HTTP请求至Web服务器,去获取www.github.com的主页。

7、Web服务器接收请求,并查找HTML页面。如果该页面存在,该Web服务器准备响应并把它发回给你的浏览器。如果服务器找不到你请求的页面,它将发送一个404错误消息,404表示“页面未找到”

8、你的浏览器把它接收到的HTML页面从头到尾扫描一遍,并去寻找其它相关的资源,如图片、CSS文件、JavaScript脚本文件等等。

9、在网页中涉及的其它资源,服务器会重复上面的步骤去发起HTTP请求每个相应的资源。

10、一旦浏览器加载完HTML涉及到的所有资源,页面最终会加载在浏览器窗体里,并关闭与服务器的连接。

跨越互联网的深渊

一件值得注意的是,当你发起一个请求时,你请求获取的信息是如何传送的。当你发起一个请求,这些信息是拆分成许多小块的,我们称之为包,每一个包包含TCP头部,这个头部里包含了源主机和目的主机的端口号,以及IP头部包含源主机和目的主机的IP地址。然后这个包通过以太网、WiFi和蜂窝网络传输,并允许在任何路径上进行传输,并在到达最终目的地时获取尽可能多的跳转。

(事实上我们并不关心包是如何到达目的地的,我们关心的是这些包能够安全地传送到目的主机),一旦这些包到达目的主机,它们将再次组合起来,当成一个整体进行传送。

那么所有这些包是怎么知道它们应如何到达目的主机,而不丢失任何包的呢?

答案就是TCP/IP。

TCP/IP是两部分组成的,它的功能是作为因特网的基本“控制系统”。IP表示因特网互联协议,它的主要任务是通过每个包的IP头部(例如IP地址)去发送和寻找路径传送到其它电脑。第二部分是传输控制协议(TCP),它的任务是拆分信息或者是把文件拆成小包,通过TCP头部把包路由至目的电脑相应的应用上,如果包发生丢失时,就重新发送,一旦当他们都到达另一端时,就按正确的顺序组装起来。

描绘最终的图形

等等,任务还未完成。现在你的浏览器已经取得了网站首页的全部资料(HTML、CSS、JavaScript、图片等等),它还得经过几步把这些资源以人可读的网页的方式呈现。

你的浏览器有一个渲染引擎,它就是负责展示内容的。这个渲染引擎以小块的方式接受所有资源的内容,然后通过HTML解析算法告诉浏览器如何解析这些资源。

一旦解析完成,它会生成一个由DOM元素组成的结构树,DOM代表文档对象模型,它是在HTML文档中如何呈现所有对象位置的一种约定。每个文档中的对象或者说节点,可以使用脚本语言,如JavaScript,去操作它们。(W3C 文档对象模型 (DOM) 是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。DOM的本质是建立网页与脚本语言或程序语言沟通的桥梁。)

构建了DOM树之后,将对样式表进行解析,以了解如何对每个节点进行样式化。使用上面的信息,浏览器从上至下遍历DOM节点,并计算每个节点的CSS样式、位置、坐标等等。

一旦浏览器取得了DOM节点和它们的样式,它最终根据你的屏幕准备绘制页面。最终页面就是你之前在网络上看到的页面一样。

第二部分:Web程序的结构

一个基本的Web应用程序配置

配置Web应用程序有很多种方式,也就是说,绝大部分的方式都遵循着同样的基本结构:客户端、服务端和数据库。

如何扩展一个简单的Web应用程序

上面的配置对于简单的应用程序已经够了,但是应用程序功能扩大,一台服务器没有能力去同时处理数千个来自访客的请求。

为了处理这么多请求量,我们能做的就是分发请求流量给一个后台服务器群。

在这里就使得事情变得很有趣了。你有多台服务器,每台服务器都有各自的IP地址,那么域名服务器(DNS)如何知道要把你的流量发送到哪个应用程序实例?

简单的答案是DNS不知道。管理所有独立的应用程序实例是通过一个叫负载均衡器来做到的。

负载均衡器扮演交警的角色,它把客户请求以尽量快而有效的方式传送给服务器。

由于你不能广播你所有服务器实例的IP地址,你创建一个虚拟IP,把这个IP地址公开给所有客户端,这个虚拟IP指向你的负载均衡器,所以当DNS查询你网站的IP时,它将指向负载均衡器的IP,然后负载均衡器再实时分发流量给每台后端服务器。

你可能会想负载均衡器如何知道应该把流量发送至哪台服务器,答案是:算法。

一种流行的算法是轮询,它均匀地分布所有请求给你的服务器群(所有可用的服务器)。如果你所有的服务器处理速度和内存一样的话,你通常会选择这种方式。

另一种算法是根据最少连接数来决定下一个请求发送给最少活动连接数的服务器。

根据你的需要,你可以从很多算法中选择一种来应用。

这个流程类似于下面这样的:

服务

我们通过创建服务器池和负载均衡来管理它们解决了我们的流量问题,一切都进行得不错,对吗?

……但是随着你应用程序的增多,只是增加服务器还是会导致一些问题。随着你的应用程序添加更多的功能,而且以后还会添加,这样你得一直维护这同一服务器。为了解决这个问题,我们需要一种方式来解耦服务器功能。

这就是服务出现的缘由,一个服务是另一台服务器,区别是它只与其它服务器交互,而不是直接与客户端交互的传统Web服务器。

每个服务都包含一个独立的功能,比如用户授权或者提供搜索的功能。服务允许你将单台Web服务器拆分为多个服务,每个服务执行不同的功能。

将单台服务器拆分为多个服务的主要好处是,允许你完全独立地扩展服务。

这样做的另一个优势是,允许公司开发团队各自去开发部分服务,而不是10个,100个,甚至1000个工程师开发同一台服务器的应用,如果这样很快成了项目管理的噩梦。

请注意:当你的应用程序部署在越来越多的服务器集群上时, 负载均衡、后端服务器集群,以及服务的概念变得越来越有挑战性。如会话持久化这样的事变得特别棘手——例如,如何在会话期间从客户端发送多个请求到同一个服务器的——以及如何部署你的负载均衡方案。在本文我们暂时不谈论这些高级的话题。

内容分发网络

以上所有的工作能很好的扩展流量,但是你的应用却一直集中在一个地方,当你的用户在你国家的其它地方或者其它国家访问你的网站,他们的网页加载可能要很长时间,因为这是由于客户端和服务器相隔太远造成的。毕竟我们谈论的是“万维网”,而不是“本地Web”。

解决这个流行的策略是使用内容分发网络(CDN),CDN是一个大型“代理”服务器分布式系统,它部署在多个数据中心。代理服务器只是一个扮演客户端与服务器的中间服务器。

有大量分散流量的公司可以选择付费CDN公司来分发它们的内容给终端用户。比如Akamai就是一个CDN公司,Akamai拥有成千上万台服务器,它们部署在世界各地有战略性的地理位置。

让我们来比较在有CDN和没有CDN的情况下浏览网页是如何进行的。

就像我们在第一部分谈论的传统网站,URL中的域名转换成主机服务器的IP地址。

然而,如果一个公司的网站使用了Akamai,URL中的域名会转换成Akamai公司下的服务器的IP,然后Akamai把网站内容传送给浏览这网站的使用者,这样使用者甚至不用连接该公司的服务器。

Akamai可以从其客户的服务器上获取并保存那些经常被使用的元素,比如HTML、CSS、软件下载和媒体文件。

这样做的主要目标是使得你的网站内容离使用者更近,如果用户请求的内容不用通过很远的地方去获取,那这样意味着更低的延迟,这也就降低了网页的加载时间。

第三部分:HTTP和REST

什么是HTTP?

在客户端-服务器模型中,客户端和服务器是以“请求-响应”消息方式来交互消息的:客户端发送请求,服务器响应请求。

跟踪信息的流动比听起来的复杂得多,客户端和服务器都遵循一种通用的语言及一系列规则,所以它们都知道自己需要的是什么,这种语言或者说“协议”就是HTTP。

HTTP协议定义了语法(数据格式和编码)、语义(语法的含义)以及即时(速度和顺序)。客户端和服务器之间每个HTTP请求和响应的交换可以认为是单个HTTP事务

HTTP: 主要内容

首先,HTTP是基于文本的,这意味着客户端和服务器之间的消息交换是通过比特文本来完成的,每条消息包含两个部分:一个消息首行和一个消息正文。

其次,HTTP是应用层协议,意味着它只是一个抽象层标准化主机之间是如何通信的。HTTP本身不能传送数据,它仍然得靠底层的TCP/IP协议在两台机器间获取请求和响应。

(提醒一下,TCP/IP是由两部分组成的,它的功能是作为因特网底层“控制系统”,关于TCP/IP的介绍,可以查看这一系列中的第一篇文章)

最后,你应该已经在你浏览器的地址栏见过“HTTPS”协议,你会想HTTPS是不是HTTP+“S”,简单的回答是近似,但是还是有点点不同。

普通的HTTP请求和响应是没有加密的,很容易受到各种类型的安全攻击。而HTTPS是一种更安全的通信方式,它通过加密的方式使得信息更安全,它是在原有的HTTP数据外面加了一层TLS/SSL。

SSL是一种安全协议,它允许客户端与服务器以一种安全的方式在网络中通信,它防止消息在网络传输过程中被窃听和篡改。

客户端通常使用一个特殊的端口443来标识是否需要TLS/SSL连接。一旦客户端和服务器同意使用TLS/SSL来通信,他们就通过执行所谓的“TLS握手”来协商建立一个有状态的连接,然后客户端和服务器建立一个会话秘钥,在他们彼此通信时就使用这个秘钥来加密和解密消息。

想Google和Facebook这样的大部分网站都使用HTTPS,毕竟它能使你的密码、个人信息和信用卡信息在网络上安全可靠。

HTTP:细节

我们通过访问https://www.github.com来与GitHub服务器通信。如果你使用的是谷歌浏览器或者安装了FireBug插件的火狐浏览器,那你可以通过查看“网络”页签来查看HTTP请求的详细信息。打开“网络”页签,然后在你的地址栏里输入www.github.com访问。

HTTP请求头

HTTP请求头通常包含元数据(关于数据的数据),这些元数据包含请求类型(GET、POST、PUT、DELETE),路径、状态码、内容类型、用户代理、cookie、Post主体(有时包含)等等。

用访问Github这个例子,让我们近距离地看一下头部中最重要的部分,我们以“响应头”开始:

  • 请求URL:https://github.com/ (这是我们请求的URL)
  • 请求方法:GET (在这个例子里使用的这种HTTP请求方法,相当于浏览器说:“喂,GitHub服务器,把你的主要给我”)
  • 状态码:200 OK (这是服务器告诉客户端,说你的请求结果正常的一种标准方式。状态码200意味着服务器成功地找到了资源,并正在发送给你)
  • 远程地址:192.30.252.129:443 (这个IP地址和端口号是我们访问的GitHub网站,注意它的端口#443(意思是我们使用的是HTTPS协议,而不是HTTP))
  • 内容编码:gzip (这是我们接受的资源编码,在这个例子里,GitHub服务器告诉我们,它发送回来的内容是压缩过的,GitHub可能压缩了文件,这样你下载时耗时更少。)
  • 内容类型:text/html;字符集=utf-8 (规定响应主体中的数据表示方式,包括类型及其子集,这个类型描述的是数据类型,同时这个子集指定了这个数据类型的特定格式。在我们这个例子里,我们的文本是以HTML的形式返回给我们的;第二部分指定了对于这个HTML文档使用的字符编码,大部分情况下用的是这个例子中的用的UTF-8)

头部信息还包含很多内容,它们是客户端必须传送给服务器的,这样服务器才可能知道如何响应请求。下面让我们看一下“请求头”部分:

  • 用户代理:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36 (这表示用户使用的软件,有时网站需要知道它们是如何被浏览的,所以浏览器发送这个用户代理字符串给服务器,服务器可以用来确定访问网站所用的内容)
  • 接收编码:gzip,deflate,sdch (规定浏览器将以什么编码来接收内容,我们可以看到列表中的gzip,这就是为什么GitHub服务器能够发送gzip格式的内容给我们。)
  • 接收语言:en-US;q=0.8 (描述我们想要的网页使用的语言,在这个例子里,“en”代表英语)
  • 主机:github.com (这就不言自明了吧)
  • Cookie:_octo=GH1.1.491617779.1446477115; logged_in=yes; dotcom_user=iam-peekay; _gh_sess=somethingFakesomething FakesomethingFakesomethingFakesomethingFakesomethingFakesomethingFakesomethingFake; user_session=FakesomethingFake somethingFakesomethingFakesomethingFake; ga=9389479283749823749; tz=America%2FLos_Angeles (这段文本是Web服务器存储在用户机器上,以便以后检索,这些信息是以键值对存储的,其中一个键值对是GitHub为了这个请求存储的,例如,“dotcom_user=iam-peekay”是用来告诉GitHub我的用户ID是iam-peekay)

tl;dr:这些键值对又是做什么的?

你的浏览器在任何时间访问一个网站时,它都会去查看之前这个网站在你电脑上设置的cookie.

所以,如果我访问www.github.com,我的浏览器将查找github保留在我硬盘上的cookie文件,如果找到了cookie文件,它就将把这些键值对包含在请求头里发送给服务器。

GitHub网页服务器现在就可以以不同的方式使用cookie数据,比如以用户保存的个人配置来显示内容,以及统计他们访问网站的次数。

如果浏览器没有找到cookie文件——这可能是因为之前从没有访问过这个网站或者阻止或删除了cookie——这样浏览器就不会发送任何cookie数据。

在这个例子中,GitHub服务器创建了一个新的ID键值对,同时还有任何它需要的键值对,然后通过HTTP头部发送给我的电脑,我的电脑就把这些信息保存在硬盘里。

HTTP请求方法

HTTP动词或者方法,是告诉服务器通过URL中的数据标识去做什么,URL通常是指示一个特定的资源,当客户端使用含有HTTP动词的URL时,这是告诉服务器对于这个资源需要使用什么样的操作。

例子:

当客户端发送一个请求,它将通过使用这些动词中的一个来指示是哪一类请求,其中最终要的一些动词是GET、POST、PUT和DELETE,这类动词还有其它如HEAD和OPTIONS,但是它们很少用到,所以我们在这篇文章中就不讨论它们了。

GET(安全,幂等)

GET是使用最普遍的请求方法,它的作用是从服务器上读取指定的URL。

GET请求时只读的,这意味着服务器上的数据是不应该被修改的——服务器应该只是简单的获取未修改的数据。按这种方式,GET请求被认为是安全的操作,因为请求一次或者20次,它的效果都是一样的。

此外,GET请求时幂等的,这意思就是你对同一个URL提交多次和提交一次导致的效果应该是一样的,因为一个GET请求只是请求服务器数据,而不去更改服务器上的任何数据。

如果GET请求的资源被成功找到,那么响应状态码就是200(OK),如果资源不存在就是400(NOT FOUND)。(当你访问过期的或不存在的URL时,“404页面”就被作为是错误消息)

POST(不安全,不幂等)

POST通常是用来创建新的资源,例如注册表单。但你想创建一些父资源(http://example.com/users)的子资源(如一个新用户),你就可以用POST,你提交的资源是通过URL来识别其父资源,这样服务器在处理这个新资源是就会把它与其父资源联系起来。

POST既不是安全的,也不是幂等的。这是因为你对同一个POST请求提交两次或多次,很可能导致创建了两个相同的资源。

POST请求响应状态代码201(已创建),并在头部带有指向新创建的资源的链接。

PUT(不安全,幂等)

PUT通常是用来更新资源的,它是通过URL和请求主体中的信息来识别要更新的资源。PUT也可以被用来创建新的资源。PUT请求也是不安全的操作,因为他们会修改服务器状态,然而,它是幂等的,因为你多次发送对同一个资源的PUT修改请求,它的效果和发送第一次的请求时一样的。

如果资源被成功修改,PUT请求响应状态码是200(OK),如果资源不存在则返回404(NOT FOUND)。

DELETE(不安全,幂等)

DELETE通常是用来删除资源的。DELETE请求也是幂等的,因为你删除一个资源后,即使你多次重复发送多次同一个DELETE请求,结果也是一样的:资源已删除。

如果你对同一个资源发送多次DELETE请求,你很可能会得到一个404的错误消息,因为服务器无法找到已被删除的资源。

如果成功删除,DELETE请求响应状态码是200(OK),如果删除资源不存在就返回400(NOT FOUND)。

对于上面的请求方法,如果服务器处理出错就返回500(服务器内部错误)。

什么是REST呢?

REST代表“Representational State Transfer”(表述性状态转移),这是一种针对设计应用程序的架构风格。

它的基本想法是使用“无状态”,“客户端-服务器”,“可缓存”协议来使两台机器间通信——通常这个协议就是HTTP。这是一种奇特的方式来描述REST,REST给你一系列的约束去设计应用程序,这些约束使得系统性能、扩展性、简单、修改性、可见性、可移植性和可靠性更好。

约束的全部内容很长,你可以在wiki上了解更多。这篇文章的目的主要是想更深入地探讨以下两个最重要的点:

1、接口一致性:这些约束告诉你如何去定义客户端与服务器之间的接口,让你以一种简单、可复制架构的方式去定义,也就是说:

  • 在请求里资源必须是可识别的(比如在URI用资源识别器)。一个资源(比如数据库里的数据)是定义资源表示的数据(如JSON,HTML)。资源和资源表示是分离的——客户端只与资源表示互动。
  • 客户端使用资源表示时必须要有足够的信息去操作服务器上的资源。
  • 客户端与服务器交换的每条消息必须是自我描述的,通过这些信息来决定如何处理这些消息。
  • 客户端必须使用HTTP主体内容、HTTP请求头、查询参数和URL发送状态数据。服务器必须使用HTTP主体内容、响应码和响应头部发送状态数据。
  • 注意:我们上面描述的HTTP动词使得“接口一致性”占据了很重要的部分,因为它们对那些操作的资源呈现一致性。

2、无状态:这些约束表示所有状态数据需要处理的客户端请求必须包含在请求本身里(URL,查询参数、HTTP主体,或者HTTP头部),以及服务器发送的所有状态数据需要通过响应内容本身(HTTP头部、状态码和HTTP响应主体)需要返回给客户端。

边注:状态或者应用程序状态是服务器完成请求所需要的数据。

这就是说对于每一个请求,我们会来回发送状态信息,因此服务器不需要去维护、更新和发送状态。

因为有了这种无状态系统使得应用程序更具扩展性,因为在多个请求中,服务器根本不用去操心维护同一会话状态,所有需要获取状态数据都可以在请求和响应本身取得。

参考文章:

http://www.cnblogs.com/Lau7/p/7867883.html
https://www.cnblogs.com/Lau7/p/7883132.html
https://www.cnblogs.com/Lau7/p/7886727.html

猜你喜欢

转载自www.cnblogs.com/MingsonZheng/p/9955679.html
今日推荐