0%

mac 由github访问缓慢引发的思考

无缘无故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协议簇分层图:
tcs ip 协议簇

TCP的连接需要三次握手:
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)+ 1syn=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
2
3
4
5
6
<status-line>
<general-headers>
<response-headers>
<entity-headers>
<empty-line>
[<message-body>]

我们在终端使用curl来请求baidu来看看:

curl http://baidu.com

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
2
github.com
github.global.ssl.fastly.net

点开这两个,然后复制ip,进入我们本机的hosts文件:/etc/hosts,修改的话需要root权限。然后将我们获得的这两个域名的ip写入hosts文件:

1
2
199.232.5.194   github.global.ssl.fastly.net
192.30.253.112 github.com

然后刷新DNS:

1
dscacheutil –flushcache

重新访问github,可以明显看到速度加快了,包括在shell里面拉取或推送,都变快了。