无缘无故github访问很缓慢,实在忍受不了龟速拉代码的感觉,来看看到底怎么回事。
为什么会访问缓慢?
我们访问的网站的地址是:github.com
。问题的表象是访问网站接受的数据很慢,那么背后到底有什么不可告人的秘密?
访问一个网站的背后流程
正好我们也回顾下前端面试的时候一个常用的问题:访问一个网站的背后是怎么进行的?(我们假设本机以前没访问过)
我们在浏览器的地址栏中输入了github.com
并回车,然后:
1. 获取域名的IP地址
浏览器尝试从本机的Host
文件(mac的话路径为/etc/Host
)中获取:github.com
对应的ip地址。
如果从Host
中获取不到IP,就需要使用DNS协议来获取IP。
具体DNS原理可以查看阮一峰的DNS原理入门,我们这里来简洁的描述下。
DNS全称(Domain Name Service)就是根据域名查出IP地址。可以想象成一本巨大的电话本。
DNS服务器根据域名的层级,进行分级查询,每一级域名都有自己的NS记录,NS记录指向该级域名的域名服务器。
2. 得到IP,建立TCP连接
为什么HTTP请求会需要建立TCP连接?
因为HTTP协议即超文本传输协议,是应用层协议,用来封装HTTP文本信息,然后需要使用TCP/IP做传输层协议将它们发送到网络上。
TCP/IP协议簇分层图:
TCP的连接需要三次握手:
首先解释下图中的名词,TCP的标志位为位码,有六种:
SYN
Synchronous Sequence Numbers 同步序列编号ACK
Acknowledgement 确认码SEQ
Sequence Numbers 序列码FIN
Finish 结束码
来看下三次握手:
- 第一次握手:建立连接时,客户端A发送连接请求报文,将SYN位置设为1,产生随机的seq码,一同发送给服务器B,服务器B由
SYN=1
知道客户端A要建立联机。 - 第二次握手:主机B收到请求后要确认联机信息,将
ack=(客户端A发送的seq)+ 1
、syn=1
、产生的随机码seq
数据发送给客户端A。 - 第三次握手:客户端A收到后检查
ack
是否正确(即第一次发送的seq
加1),以及位码syn
是否为1,若正确,客户端A会再发送ack=(服务器B的seq) + 1
。服务器B收到后会确认ack
是否为上一次的seq
加1,如果正确,就建立连接。
完成了三次握手,客户端和服务器就建立了TCP连接,就可以开始传输数据。
顺便扩展下TCP连接的断开,需要四次挥手~
TCP四次挥手:
- 第一次:从客户端A发送一个FIN码,用来关闭客户端A到服务器B的连接。
- 第二次:服务端B收到这个FIN,然后发回一个ACK,确认序号为收到的序号+1.
- 第三次:服务端B关闭与客户端A的连接,发送一个FIN给客户端A。
- 第四次:客户端A发回ACK报文确认,并将确认需要设置为收到序号+1
为什么建立连接是三次握手,关闭连接却是四次呢?
因为建立连接的时候,服务端listen状态下当收到SYN报文的请求建立连接后,它可以把ACK和SYN(ACK起应答作用,SYN起同步作用)放在一个报文里来发送。但关闭连接时,当服务端收到客户端的FIN报文通知时,它仅表示没有东西发给服务端了,并不代表服务端已将所有数据全部发送给客户端了,所以未必马上会断开连接。即可能服务端还需要发送一些数据给客户端后,再发送FIN报文给客户端表示服务端这边的同意可以关闭连接了。因此,关闭连接的时候服务端给客户端发送的ACK报文和FIN报文是分开发送的,所以会多一步。
3. 完成tcp连接,开始HTTP协议请求数据
一个HTTP请求报文由请求行、请求头部、空行和请求数据4个部分组成。服务端的web服务器(比如Nginx、Apache等)可以识别HTTP请求报文,解析请求并返回响应消息。
HTTP响应消息的格式包括:状态行、响应头、空行、消息体。每部分内容各占一行。
1 | <status-line> |
我们在终端使用curl来请求baidu来看看:
4. 客户端接收到响应消息,开始解析
浏览器根据响应体的状态码来进行对应的解析,状态码类型:
- 1xx: 指示信息,表示已接受请求,正在处理
- 2xx: 成功,表示请求已被成功处理,返回了“正确”的数据。
- 3xx: 重定向,表示需要的东西不在这里,需要去别处找。
- 4xx: 客户端错误,表示请求有语法错误或请求无法实现。
- 5xx: 服务器端错误,表示服务器未能正确的实现。
常见的几种状态码:
100 continue
这个表示临时响应,客户端应该继续请求,如果已完成,则忽略它。200 ok
表示请求成功。201 creadte
表示请求已成功,并因此创建了一个新的资源,通常是post请求之后返回的响应。300 multiple choice
被请求的资源有一系列可供选择的回馈信息,用户或浏览器能够自行选择一个首选的地址进行重定向。301 moved permanently
被请求的资源已被永久移动到新的位置。服务器返回此响应时,会自动将请求者转到新的位置。302 found
服务器目前从不同位置的网页响应请求,但请求者应继续使用原有的地址进行以后的请求。304 not modified
自从上次请求后,请求的资源未修改过,服务器返回此响应,不会返回资源内容。400 bad request
请求体的语义有误,当前服务器无法理解。401 unauthorized
需要用户身份验证403 forbidden
服务器接收到请求,但拒绝执行。404 not found
请求失败,请求希望得到的资源在服务器上没找到。500 internal server error
服务端遇到了未知的错误502 bad gateway
服务器作为网关或代理,从上游服务器收到无响应。
收到成功响应的状态码后,浏览器开始使用自己内部的工作机制,将静态的资源和html代码进行渲染。
浏览器渲染机制展开来又是一大篇,这里先不涉及了。
解决github访问慢的问题
上面扯了一大堆访问的流程,但我们还是要解决我们的问题,怎么让我们访问github的时候变快呢?
我们能做的就是本地绑定host,绕过DNS解析这个步骤。
首先获得github.com的IP地址,比较专业一点的:ipaddress.com。
进入网站后,可以看到左边有个top sites
,里面有:
1 | github.com |
点开这两个,然后复制ip,进入我们本机的hosts文件:/etc/hosts
,修改的话需要root权限。然后将我们获得的这两个域名的ip写入hosts文件:
1 | 199.232.5.194 github.global.ssl.fastly.net |
然后刷新DNS:
1 | dscacheutil –flushcache |
重新访问github,可以明显看到速度加快了,包括在shell里面拉取或推送,都变快了。