鱼喃

听!布鲁布鲁,大鱼又在那叨叨了

在浏览器地址栏输入一个URL后发生了什么

这个算是面试必问问题之一了,可以借此考察候选人对HTTP、网络方面的知识了解深度。一般面经上都会这么说:首先DNS解析,然后经历TCP三次握手,构造HTTP报文,获得响应之后浏览器渲染。不过如果真的接触过之后就会发现,面经上说的这些真的是浅尝辄止。

以下以我的博客https://blog.newnius.com为例,然后结合接触过的技术来尽可能详细的说一下背后的故事吧,某些细节上可能会有遗漏和瑕疵。

为了叙述简单流畅,流程中的所有缓存全部忽略了。一般而言,几乎所有的步骤中都有缓存的存在。

DNS解析

大家都知道,在互联网上两个节点间想要通信需要靠ip地址来定位,在早期的时候各大网站确实是直接使用ip地址的,但是ip地址有一个很大的缺点就是没有规律,很难记,为了便于记忆设计了域名这么个东西。(当然了,还有其他原因,比如说服务器迁移、分布式等等,这里就忽略了)虽然域名多了以后还是会记不住,门户网站也因此兴起,不过这是后话了。

有了域名之后,用户记忆更加方便了,但是路由它只认ip,所以需要有一个域名到ip的转换服务,这也就是DNS解析的由来。

在输入浏览器之后,浏览器首先区检查hosts文件里是否设置了对应的解析,如果没有,就向DNS服务器询问(DHCP的时候网关会提供),DNS服务器是有层级的,一般来说,直接对用户提供服务的DNS服务器不会直接负责域名的解析,而是会将请求转发到上游DNS服务器,一直到最终具体负责解析的DNS服务器。(这种多级的拓扑结构可以通过缓存的优化来很好的降低流量负载)

那么DNS服务器如何知道域名对应的IP呢,这块就需要提到ns服务器了,域名拥有人可以设置ns服务器用于设置域名到ip的解析,比如我目前的方案是用到了AWS的route53服务。DNS服务器就是要询问这个NS服务器子域名blog对应的ip地址。

但是DNS服务器它如何知道我的NS服务器是哪一个呢?其实这块也是一个分层的设计,DNS服务器首先询问根服务器,负责解析com域的顶级域名服务器有哪些?然后选择其中的一个询问负责newnius.com域名解析的次级域名服务器是哪一个?(其实这里就是R53的服务器了)

知道具体的NS服务器地址之后就可以查询blog子域名的ip地址了,这里为了兼顾互联网和特色网的速度我设置了geodns,即国内会被解析到阿里云的服务器上,而国外会被解析到GitHub pages上,如果是在国内访问的话,一般会拿到国内阿里云的地址。

拿到IP地址之后,根据调用递归逐级返回,这样我们的主机就能知道域名对应的IP是什么了。

是不是挺复杂的?

其实DNS这块能说的东西还有很多,比如DNS协议是基于udp的,主要目的是为了快,但是随着国内近年来的dns污染,目前DoH(dns over https)也开始流行起来了。

HTTP 页面请求

拿到IP地址之后,通过TCP三次握手,协商窗口大小等等建立TCP连接,然后构造一个 HTTP GET 请求,主要包括主机名、地址等等信息,经由TCP通道发送到网站服务器。因为博客是静态的,所以直接用的Nginx,请求会落到Nginx上,然后根据配置找到文件所在的路径,读取文件,返回一个HTML文件。

关于TCP三次握手四次挥手这里就不展开说了,可以参考之前的文章 TCP 通信之三次握手与四次挥手

不过在收到GET请求之后其实发生了更多的事,我的网站是设置了https的,而且非https会被重定向到https协议上,一般而言,浏览器会默认请求http协议,所以服务器会首先返回一个301重定向请求让浏览器知道这是一个https协议的站点,请通过https协议来访问。浏览器拿到301请求之后,会首先建立https连接,也就是ssl四次握手。在握手中拿到网站的证书,然后向域名签发机构确定证书的有效性,其他的握手过程主要是为了确定生成密钥所需的参数。握手完成之后再次构造HTTP GET请求,不过这次内容就是加密的了。

https这块也涉及到了很多的内容,比如证书包括了什么,为什么要有域名签发机构这么个东西,加密算法为什么是非对称加密?

CDN

这块其实应该放在请求前面,不过为了流畅叙述,放在这里。一般来说,流量比较大的站点都会有多级分流机制,一个是在机房内的负载均衡,另外一个就是CDN(Content Delivery Network内容分发网络)服务了。

CDN这块一个是动静分离,把静态内容如js脚本、css文件分发到离用户更近的节点上,这样一来降低了源站的负载,还提升了用户的访问速度;从网络层面上来看,CDN有时也能优化,一个是网络路由的优化,另一个是连接速度的优化,因为HTTP是通过TCP协议的,有一个窗口大小调整的过程,采用CDN可以提前建立一个大管道,这样用户跟CDN边缘节点建立连接的时候能更快的达到最佳速度;除此之外,CDN还能起到保护源站的作用。

页面渲染

一般来说,网页内容除了HTML文件之外还有其他的一些资源,如js、css、图片等等。

在浏览器接收到HTML文件之后,一是区解析其中的DOM结构,另外就是马不停蹄的去获取其他的资源,一边渲染页面,一边拉取其他资源,也是通过HTTP协议的。

因为不是搞前端,所以浏览器渲染这块不是很熟。

HTTP 1.0 vs HTTP 1.1 vs HTTP 2.0

之前我们只说通过HTTP协议去请求文件,但是这中间又是具体怎么做的呢?是每个请求都建立一个连接还是共用?是并发请求还是串行?

在HTTP 1.0中,因为互联网早期网络容量小,不能长时间占用,所以连接需要尽快释放,一般会尽可能多的减少文件数量,这种情况下如果一个页面有多个资源,频繁的创建连接会带来比较大的延迟;在HTTP 1.1中改进了这个策略,连接可以复用了,在上一个请求用完之后不会马上释放,而是等待一段时间,看还有没有其他的请求。但是1.1有一个缺点就是每次只能处理一个请求,无法做到多路复用;在HTTP 2.0中,再次改进了这么一个协议,支持了多路复用,这样又能更快的压榨性能了。

除了以上这些差异,各个版本之间的差异还有很多,下表是一个简略的对比。

http 1.0 http 1.1 http 2.0
单次 连接复用(长连接,connection: keep-alive) 多路复用
断点续传、Host头 .

如果以上这些你都知道,那么你知道 HTTP 3.0 吗?

HTTP 3.0是Google提出来的一个协议,主要是为了解决TCP拥塞控制的缺陷的(此前Google还提出了bbr拥塞控制算法,科学上网的人一般都会设置这个,可以提升网速)。因为TCP需要三次握手,需要拥塞控制,但是默认的拥塞控制算法略显保守,而直接去修改网络上各种中间设备的拥塞控制算法不太现实,所以Google就提出了基于UDP的QUIC算法,尽可能多的去压榨网络带宽。至于它有没有用,前几天看到发哥发朋友圈表示怀疑。就我而言的话,Google的机房遍历全球各地,各种优化也是络绎不绝,所以还是相信这个协议会有更好的性能,而且从理论上来说也的确如此。不过放到实际的网络环境下的话,会有一些差别,就比如说在国内的网络环境下,UDP普遍被QOS,所以可能性能还不及TCP。但是这个属于特色问题了。

死亡拷问

想写的其实还有很多,每一个小点都能继续发散,不过囿于时间,所以就只写了这些。以下还有一些有趣的问题。

你知道根服务器一共有多少台吗?分别位于哪里?由什么机构负责呢?为什么没有进行扩容?

为啥那些砖家们天天逼逼着要有中国人自己的根服务器呢?为啥很难实现呢?

你知道如果服务器想要主动推送消息,有哪些做法吗?

HTTP主要由哪两部分组成?有分别有些什么呢?

为什么要有HTTPS协议?为什么他能保证安全呢?是所有的内容都加密了吗?

HTTP是有状态的还是无状态的?如何去跟踪一个会话呢?

你知道有哪些提升网站访问速度的方法吗?

为什么HTTPS证书会存在有效期的概念?

为什么需要引入域名签发机构这种东西?

你还知道哪些状态吗?

为什么采用Nginx来做前面的负载均衡?

知道中间人攻击吗

参考

DNS over HTTPS

ssl为什么会让http安全

根域名服务器

HTTP 状态码及其含义