极客时间已完结课程限时免费阅读

04丨网络编程原理:一个字符的互联网之旅

04丨网络编程原理:一个字符的互联网之旅-极客时间

04丨网络编程原理:一个字符的互联网之旅

讲述:李智慧

时长17:39大小16.17M

我们开发的面向普通用户的应用程序,目前看来几乎都是互联网应用程序,也就是说,用户操作的应用程序,不管是浏览器还是移动 App,核心请求都会通过互联网发送到后端的数据中心进行处理。这个数据中心可能是像微信这样的自己建设的、在多个地区部署的大规模机房,也可能是阿里云这样的云服务商提供的一个虚拟主机。
但是不管这个数据中心的大小,应用程序都需要在运行期和数据中心交互。比如我们在淘宝的搜索框随便输入一个字符“a”,就会在屏幕上看到一大堆商品。那么我们的手机是如何通过互联网完成这一操作的?这个字符如何穿越遥远的空间,从手机发送到淘宝的数据中心,在淘宝计算得到相关的结果,然后将结果再返回到我们的手机上,从而完成自己的互联网之旅呢?
虽然我们在编程的时候,很少要自己直接开发网络通信代码,服务器由 Tomcat 这样的 WEB 容器管理网络通信,服务间网络通信通过 Dubbo 这样的分布式服务框架完成网络通信。但是由于我们现在开发的应用主要是互联网应用,它们构建在网络通信基础上,网络通信的问题可能会出现在系统运行的任何时刻。了解网络通信原理,了解互联网应用如何跨越庞大的网络构建起来,对我们开发一个互联网应用系统很有帮助,对我们解决系统运行过程中各种因为网络通信而出现的各种问题更有帮助。

DNS

我们先从 DNS 说起。
构成互联网 Internet 的最基本的网络协议就是互联网协议 Internet Protocol,简称 IP 协议。IP 协议里面最重要的部分是 IP 地址,各种计算机设备之间能够互相通信,首先要能够找到彼此,IP 地址就是互联网的地址标识。手机上的淘宝 App 能够访问淘宝的数据中心,就是知道了淘宝数据中心负责请求接入的服务器的 IP 地址,然后建立网络连接,进而处理请求数据。
那么手机上的淘宝 App 如何知道数据中心服务器的 IP 地址呢?当然淘宝的工程师可以在 App 里写死这个 IP 地址,但是这样做会带来很多问题,比如影响编程的灵活性以及程序的可用性等。
事实上这个 IP 地址是通过 DNS 域名解析服务器得到的。当我们打开淘宝 App 的时候,淘宝要把 App 首页加载进来,这时候就需要连接域名服务器进行域名解析,将 xxx.taobao.com 这样的域名解析为一个 IP 地址,然后连接目标服务器。

CDN

事实上 DNS 解析出来的 IP 地址,并不一定是淘宝数据中心的 IP 地址,也可能是淘宝 CDN 服务器的 IP 地址。
CDN 是内容分发网络 Content Delivery Network 的缩写。我们能够用手机或者电脑上网,是因为运营服务商为我们提供了互联网接入服务,将我们的手机和电脑连接到互联网上。App 请求的数据最先到达的是运营服务商的机房,然后运营商通过自己建设的骨干网络和交换节点,将我们请求数据的目的地址发往互联网的任何地方。
为了提高用户请求访问的速度,也为了降低数据中心的负载压力,淘宝会在全国各地各个主要的运营服务商的接入机房中部署一些缓存服务器,缓存那些静态的图片、资源文件等,这些缓存服务器构成了淘宝的 CDN。
如果用户请求的数据数据是静态的资源,这些资源的 URL 通常以 image.taobao.com 之类的二级域名进行标识,域名解析的时候就会解析为淘宝 CDN 的 IP 地址,请求先被 CDN 处理,如果 CDN 中有需要的静态文件,就直接返回,如果没有,CDN 会将请求发送到淘宝的数据中心,CDN 从淘宝数据中心获得静态文件后,一方面缓存在自己的服务器上,一方面将数据返回给用户的 App。
而如果请求的数据是动态的,比如要搜索关键词为“a”的商品列表,请求的域名可能会是 search.taobao.com 这样的二级域名,就会直接被 DNS 解析为淘宝的数据中心的服务器 IP 地址,App 请求发送到数据中心处理。

HTTP

不管发送到 CDN 还是数据中心,App 请求都会以 HTTP 协议发送。
HTTP 是一个应用层协议,当我们进行网络通信编程的时候,通常需要关注两方面的内容,一方面是应用层的通信协议,主要是我们通信的数据如何编码,既能使网络传输过去的数据携带必要的信息,又使通信的两方都能正确识别这些数据,即通信双方应用程序需要约定一个数据编码协议。另一方面就是网络底层通信协议,即如何为网络上需要通信的两个节点建立连接完成数据传输,目前互联网应用中最主要的就是 TCP 协议。
在 TCP 传输层协议层面,就是保证建立通信两方的稳定通信连接,将一方的数据以 bit 流的方式源源不断地发送到另一方,至于这些数据代表什么意思,哪里是两次请求的分界点,TCP 协议统统不管,需要应用层面自己解决。如果我们基于 TCP 协议自己开发应用程序,就必须解决这些问题。而互联网应用需要在全球范围为用户提供服务,将全球的应用和全球的用户联系在一起,需要一个统一的应用层协议,这个协议就是 HTTP 协议。
这张图是 HTTP 的请求头的例子,包括请求方法和请求头参数。请求方法主要有 GET、POST,这是我们最常用的两种,此外还有 DELETE、PUT、HEAD、TRACE 等几种方法;请求头参数包括缓存控制 Cache-Control、响应过期时间 Expires、Cookie 等等。
HTTP 请求如果是 GET 方法,那么就只有请求头;如果是 POST 方法,在请求头之后还有一个 body 部分,包含请求提交的内容,HTTP 会在请求头的 Content-Length 参数声明 body 的长度。
这是 HTTP 响应头的例子,响应头和请求头一样包含各种参数,而 status 状态码声明响应状态,状态码是 200,表示响应正常。
响应状态码是 3XX,表示请求被重定向,常用的 302,表示请求被临时重定向到新的 URL,响应头中包含新的临时 URL,客户端收到响应后,重新请求这个新的 URL;状态码是 4XX,表示客户端错误,常见的 403,表示请求未授权,被禁止访问,404 表示请求的页面不存在;状态码是 5XX,表示服务器异常,常见的 500 请求未完成,502 请求处理超时,503 服务器过载。
如果响应正常,那么在响应头之后就是响应 body,浏览器的响应 body 通常是一个 HTML 页面,App 的响应 body 通常是个 JSON 字符串。

TCP

应用程序使用操作系统的 socket 接口进行网络编程,socket 里封装了 TCP 协议。应用程序通过 socket 接口使用 TCP 协议完成网络编程,socket 或者 TCP 在应用程序看就是一个底层通信协议,事实上,TCP 仅仅是一个传输层协议,在传输层协议之下,还有网络层协议,网络层协议之下还有数据链路层协议,数据链路层协议之下还有物理层协议。
传输层协议 TCP 和网络层协议 IP 共同构成 TCP/IP 协议栈,成为互联网应用开发最主要的通信协议。OSI 开放系统互联模型将网络协议定义了 7 层,TCP/IP 协议栈将 OSI 顶部三层协议应用层、表示层、会话层合并为一个应用层,HTTP 协议就是 TCP/IP 协议栈中的应用层协议。
物理层负责数据的物理传输,计算机输入输出的只能是 0 1 这样的二进制数据,但是在真正的通信线路里有光纤、电缆、无线各种设备。光信号和电信号,以及无线电磁信号在物理上是完全不同的,如何让这些不同的设备能够理解、处理相同的二进制数据,这就是物理层要解决的问题。
数据链路层就是将数据进行封装后交给物理层进行传输,主要就是将数据封装成数据帧,以帧为单位通过物理层进行通信,有了帧,就可以在帧上进行数据校验,进行流量控制。数据链路层会定义帧的大小,这个大小也被称为最大传输单元。
像 HTTP 要在传输的数据上添加一个 HTTP 头一样,数据链路层也会将封装好的帧添加一个帧头,帧头里记录的一个重要信息就是发送者和接受者的 mac 地址。mac 地址是网卡的设备标识符,是唯一的,数据帧通过这个信息确保数据送达到正确的目标机器。
前面已经提到,网络层 IP 协议使得互联网应用根据 IP 地址就能访问到淘宝的数据中心,请求离开 App 后,到达运营服务商的交换机,交换机会根据这个 IP 地址进行路由转发,可能中间会经过很多个转发节点,最后数据到达淘宝的服务器。
网络层的数据需要交给链路层进行处理,而链路层帧的大小定义了最大传输单元,网络层的 IP 数据包必须要小于最大传输单元才能进行网络传输,这个数据包也有一个 IP 头,主要包括的就是发送者和接受者的 IP 地址。
IP 协议不是一个可靠的通信协议,并不会确保数据一定送达。要保证通信的稳定可靠,需要传输层协议 TCP。TCP 协议在传输正式数据前,会先建立连接,这就是著名的 TCP 三次握手。
App 和服务器之间发送三次报文才会建立一个 TCP 连接,报文中的 SYN 表示请求建立连接,ACK 表示确认。App 先发送 SYN=1,Seq=X 的报文,表示请求建立连接,X 是一个随机数;淘宝服务器收到这个报文后,应答 SYN=1,ACK=X+1,Seq=Y 的报文,表示同意建立连接;App 收到这个报文后,检查 ACK 的值为自己发送的 Seq 值 +1,确认建立连接,并发送 ACK=Y+1 的报文给服务器;服务器收到这个报文后检查 ACK 值为自己发送的 Seq 值 +1,确认建立连接。至此,App 和服务器建立起 TCP 连接,就可以进行数据传输了。
TCP 也会在数据包上添加 TCP 头,TCP 头除了包含一些用于校验数据正确性和控制数据流量的信息外,还包含通信端口信息,一台机器可能同时有很多进程在进行网络通信。如何使数据到达服务器后能发送给正确的进程去处理,就需要靠通信端口进行标识了。HTTP 默认端口是 80,当然我们可以在启动 HTTP 应用服务器进程的时候,随便定义一个数字作为 HTTP 应用服务器进程的监听端口,但是 App 在请求的时候,必须在 URL 中包含这个端口,才能在构建的 TCP 包中记录这个端口,也才能在到达服务器后,被正确的 HTTP 服务器进程处理。
如果我们以 POST 方法提交一个搜索请求给淘宝服务器,那么最终在数据链路层构建出来的数据帧大概是这个样子,这里假设 IP 数据包的大小没有超过链路层的最大传输单元。
App 要发送的数据只是 key="a"这样一个 JSON 字符串,每一层协议都会在上一层协议基础上添加一个头部信息,最后封装成一个链路层的数据帧在网络上传输,发送给淘宝的服务器。淘宝的服务器在收到这个数据帧后,在通信协议的每一层进行校验检查,确保数据准确后,将头部信息删除,再交给自己的上一层协议处理。HTTP 应用服务器在最上层,负责 HTTP 协议的处理,最后将 key="a"这个 JSON 字符串交给淘宝工程师开发的应用程序处理。

LB(负载均衡)

HTTP 请求到达淘宝数据中心的时候,事实上也并不是直接发送给搜索服务器处理。因为对于淘宝这样日活用户数亿的互联网应用而言,每时每刻都有大量的搜索请求到达数据中心,为了使这些海量的搜索请求都能得到及时处理,淘宝会部署一个由数千台服务器组成的搜索服务器集群,共同为这些高并发的请求提供服务。
因此,搜索请求到达数据中心的时候,首先到达的是搜索服务器集群的负载均衡服务器,也就是说,DNS 解析出来的是负载均衡服务器的 IP 地址。然后,由负载均衡服务器将请求分发到搜索服务器集群中的某台服务器上。
负载均衡服务器的实现手段有很多种,淘宝这样规模的应用,通常使用 Linux 内核支持的链路层负载均衡。
这种负载均衡模式也叫直接路由模式,在负载均衡服务器的 Linux 操作系统内核拿到数据包后,直接修改数据帧中的 mac 地址,将其修改为搜索服务器集群中某个服务器的 mac 地址,然后将数据重新发送回服务器集群所在的局域网,这个数据帧就会被某个真实的搜索服务器接收到。
负载均衡服务器和集群内的搜索服务器配置相同的虚拟 IP 地址,也就是说,在网络通信的 IP 层面,负载均衡服务器变更 mac 地址的操作是透明的,不影响 TCP/IP 的通信连接。所以真实的搜索服务器处理完搜索请求,发送应答响应的时候,就会直接发送回请求的 App 手机,不会再经过负载均衡服务器。

小结

事实上,这个搜索字符“a”的互联网之旅到这里还没有结束。淘宝搜索服务器程序在收到这个搜索请求的时候,首先在本地缓存中查找是否有对应的搜索结果。如果没有,会将这个搜索请求,也就是这个字符发送给一个分布式缓存集群查找是否有对应的搜索结果。如果还没有,才会将这个请求发送给一个更大规模的搜索引擎集群去查找。
这些分布式缓存集群或者搜索引擎集群都需要通过 RPC 远程过程调用的方式进行调用请求,也就是需要通过网络进行服务调用,这些网络服务也都是基于 TCP 协议进行编程的。
对于互联网应用,用户请求数据离开手机通过各种网络通信,最后到达数据中心的应用服务器进行最后的计算、处理,中间会经过许多环节,事实上,这些环节就构成了互联网系统的整体架构,所以通过网络通信,可以将整个互联网应用系统串起来,对理解互联网系统的技术架构很有帮助,在程序开发、运行过程中遇到各种网络相关问题,也可以快速分析问题原因,快速解决问题。

思考题

负载均衡就是将不同的网络请求数据分发到多台服务器上,每台服务器承担一部分请求负载压力,多台服务器共同承担外部并发请求的压力,除了文中提到的这种负载均衡实现方案,你还了解哪些方案呢?
欢迎你在评论区写下你的思考,也欢迎把这篇文章分享给你的朋友或者同事,一起交流一下。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 16

提建议

上一篇
03丨Java虚拟机原理:JVM为什么被称为机器(machine)?
下一篇
05丨文件系统原理:如何用1分钟遍历一个100TB的文件?
unpreview
 写留言

精选留言(34)

  • Zend
    2019-11-25
    还有IP隧道模式,负载均衡服务器把收到的数据包,封装一个新的IP头标记,发给应用服务器,应用服务器收到数据包后,还原数据包,直接返回客户端,无需经过负载均衡服务器,缓解负载均衡服务器压力; NAT模式:是把客户端发来的数据包的IP头的目的地址,在负载均衡器上换成其中一台应用服务器的IP地址,并发至此应用服务器来处理,应用服务器处理完成后把数据交给经过负载均衡器,负载均衡器再把数据包的原IP地址改为自己的IP,将目的地址改为客户端IP地址,无论是进来的流量,还是出去的流量,都必须经过负载均衡器。
    展开
    共 2 条评论
    38
  • 分清云淡
    2019-12-25
    居然留言有这么多人不知道 Linux 内核负载均衡(也就是 LVS),LVS是第一个国产合并到Linux Kernel的功能 。批评一下作者不直接说LVS,让很多人误解了。 https://plantegg.github.io/2019/06/20/%E5%B0%B1%E6%98%AF%E8%A6%81%E4%BD%A0%E6%87%82%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1--lvs%E5%92%8C%E8%BD%AC%E5%8F%91%E6%A8%A1%E5%BC%8F/
    共 1 条评论
    22
  • Paul Shan
    2019-12-05
    网络系统像蛋糕一样多层协议,反映了如何将一个复杂系统拆解成一个一个独立的子系统分而治之。 HTTP协议解决的是有明确目的的通信,例如Get就是从服务端获取某些资源,Put是往服务器修改数据,Post是往服务器添加数据。 TCP 协议解决的是二进制意义上的稳定通信,通过握手协议建立连接,收发双方都不断的重复读和写操作。 IP协议解决的是一个地址通信,IP是主机的地址,类似于一户人家的门牌号码。IP协议不是一个稳定的协议,发出就不管了。IP地址不太好记,所以用DNS来解析,就像我们熟人之间,说某某人的家就好了,不用再说具体门牌。 数据链路层,解决的是物理连接。每个网卡都有一个地址,这个地址像经纬度坐标,不像IP层的ip那样容易修改。 物理层,解决的是物理信号到二进制信号的转化。
    展开
    共 1 条评论
    20
  • a、
    2019-11-25
    DNS的负载均衡,应用层的nginx、apache,传输层的F5.
    12
  • escray
    2020-09-22
    一篇文章概述网络编程原理。 我感觉即使不是互联网应用,单位内部局域网上的应用其实也符合“互联网之旅”中的描述(局域网内可能不需要 CDN,但是 DNS 还是可以有的)。 应用层(OSI 顶部的应用层、表示层、会话层)的 HTTP 协议,传输层的 TCP 协议,网络层的 IP 协议,数据链路层的 ARP 协议……能把这几个协议将清楚,那么一个网络包的旅程也就基本清楚了。 关于负载均衡,推荐隔壁专栏《从零开始学架构》中的《20 | 高性能负载均衡:分类及架构》和《21 | 高性能负载均衡:算法》。 对于思考题,其实负载均衡大概有三类,DNS、软件和硬件。 DNS 负载均衡一般由域名商提供,主要是按照访问的 IP 地址来决定访问不同地理位置的机房,相对简单、成本低,但是定制化功能较差和分配策略较少。 软件负载均衡比较常用,主要有 Nginx 和 LVS,7 层的 Nginx 大概是 5 万/秒,4 层的 LVS 是十万级。优点是简单、便宜、灵活,缺点是性能不如硬件负载均衡,也没有安全防护功能。 硬件负载均衡(F5 和 A10)除了贵,基本没有别的缺点,功能强大、性能高、稳定,还有安全防护功能。 比较典型的负载均衡架构是由 DNS 支撑地理级别的负载均衡,然后由 F5 支持集群级别的负载均衡,最后由 Nginx 或者 LVS 来实现机器级别的负载均衡。
    展开
    11
  • realwuxing
    2019-11-26
    李老师,想问下 Linux 内核支持的链路层负载均衡,web服务器与负载均衡服务器的IP地址都是一样的,这样不会有影响吗?在同一个局域网内,IP为什么也可以相同,谢谢!
    共 7 条评论
    5
  • 观弈道人
    2019-11-25
    李老师,想问下,负载均衡服务器想做到高可用,该怎么处理?谢谢
    共 5 条评论
    4
  • geek_arong2048
    2021-07-10
    负载均衡实现方式: 1、应用层面:LVS、Nginx、DNS等 2、协议层面:4层、7层负载均衡 3、算法层面:轮询、加权轮询、一致性哈希、随机等
    4
  • 探索无止境
    2019-11-25
    老师您好,客户端在发送请求的时候,是如何知道服务器的mac地址的?这个有点想不明白

    作者回复: 具体这块我没看过,但是应该有很多地方可以在正式通信前就可以得到服务器的mac地址,比如在TCP握手的时候,ACK应答包的链路层数据帧里包含mac地址。 准确答案参考下面其他同学的评论~

    共 12 条评论
    3
  • 老男孩
    2019-11-26
    坚持学习打卡。老师说的使用linux内核的方式负载均衡,我还是第一次听,涨姿势了。一般都是像array,f5这种硬件负载均衡器,还有nginx也可以做负载均衡,但一般用nginx做反向代理的,比如后端服务端口8080,但前端访问是80,nginx的端口。负载均衡器的也可以部署多台,服务器之间不断发送心跳包,监测对方是否宕机。这样能保证负责均衡器不出现单点问题。
    2
  • 蝴蝶
    2022-02-12
    负载均衡嘛,可以在客户端做,那就是客户端负载均衡了,哈哈.靠近客户端的叫网关,靠近服务端的叫代理,兄弟们,觉得有道理吗?
    1
  • 米兰的小铁匠
    2021-06-25
    思考总结:本篇主要网络基础,一次网络请求的步骤 1、DNS:客户端通过DNS解析服务器的地址域名(可能是CND的服务),然后连接服务器 2、CDN:CDN是运行商机房,CDN再请求后台服务器(可能是负载均衡的服务)。一般静态资源会部署到CDN上(如秒杀了),极大减少服务器压力。 3、接着对发送的内容说明,Http、tcp协议等,并对网络通讯的5层协议依次描述(应用层、传输层、网络层、数据链路层、物理层) 4、最后简单说明了下负载均衡
    展开
    1
  • 杯莫停
    2020-08-12
    TCP通信三次握手: SYN 和ACK是报文标识位,前者表示请求建立一个新的连接,后者表示确认序列号有效 seq:字节流传输的标识序列号,32位,通常都是从0开始的。 ack:回复确认的序列号,一般都是对方请求的序列号+1,表示上次请求消耗了一个序列号,下次请求序列号+1开始。 1.客户端 标识位:SYN=1表示有意愿和服务器建立一个新的连接 seq=x表示把字节流初始序列号告诉服务器,让服务器知道本次通信的序列号是x 2.服务器回复客户端请求 ACK=1表示客户端发送的序列号有效 SYN=1表示也有意愿和客户端建立一个通信连接。 seq=y表示把自己的报文字节流初始标记x发送给客户端,待客户端确认。 ack=x+1表示确认了客户端的初始序列号,+1表示通信建立后序列号就从x+1开始。 3.客户端回复服务器 ACK=1,表示服务器的序列号有效 seq=x+1,表示第二次跟服务器通信,从上次通信的序列号+1 ack=y+1,表示确认了服务器的初始序列号,并告诉对方下次通信序列号从y+1开始 通信建立之初,这三次繁复的报文交换是为了建立可靠的通信环境。而且客户端和服务器是个双向通信的,所以双方都需要确认对方的通信关系。另外我觉得,将这个过程翻译成“三次握手”是有问题的。但没办法,这种翻译已经成了业内的“协议”了。
    展开
    1
  • 俊杰
    2019-11-25
    曾经接触过的,七层负载均衡用Nginx,硬件层面的负载均衡用F5
    1
  • 摊牌
    2022-12-30 来自陕西
    引用:“负载均衡服务器和集群内的搜索服务器配置相同的虚拟 IP 地址,也就是说,在网络通信的 IP 层面,负载均衡服务器变更 mac 地址的操作是透明的,不影响 TCP/IP 的通信连接。所以真实的搜索服务器处理完搜索请求,发送应答响应的时候,就会直接发送回请求的 App 手机,不会再经过负载均衡服务器” 不太理解上面这段话,如果负载均衡服务器和集群内的搜索服务器配置相同的虚拟 IP 地址的话,那么app的请求会不会可能直接发到搜索服务集群,而不经过负载均衡服务器,导致负载均衡失效? 希望老师帮忙解答一下,谢谢!
    展开
  • 算法成瘾者
    2022-05-19
    我的几点理解偏差纠正: ① 是先经过DNS 解析出IP,再到CDN缓存有无得判断。只是DNS解析出来的IP可能是CDN 的IP或者是 服务器的IP而已
  • 蝴蝶
    2022-02-12
    想起来,还有硬件负载均衡,F5 A10.还有系统级别的,如 LVS,软件级别的 nginx haproxy.还有更高层的 DNS 级别负载均衡.
  • 蝴蝶
    2022-02-12
    对的哦,还有 DNS 的负载均衡,有个什么 GSLB
  • Rock
    2021-04-07
    401是未授权啊Unauthorized 500是Internal Server Error 这么明显的错误 多误导人啊

    作者回复: 参考维基百科:https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81 401 Unauthorized是未认证的意思;403 Forbidden是未授权的意思。 500 服务器内部错误可以理解为原因,请求未完成可以认为是结果,文中表述重点放在结果上。

  • meijing0114
    2020-11-27
    Nginx应该是最常用的负载均衡方式;硬件F5现在在互联网公司已经非常少见了;现在的负载均衡往往会附带很多对后端服务的感知;