无缘无故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的标志位为位码,有六种:
SYNSynchronous Sequence Numbers 同步序列编号ACKAcknowledgement 确认码SEQSequence Numbers 序列码FINFinish 结束码
来看下三次握手:
- 第一次握手:建立连接时,客户端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里面拉取或推送,都变快了。