前几天,产品小哥哥给我反馈了一个问题:
我们某个线上系统首页,时不时打开白屏,但是刷新一下又好了。
事出反常必有妖,这么诡异的问题,当然得排查一波啦!
随后我打开访问了一下,还真是,刷新个四五次差不多就会出现一次白屏。
计算机不会说谎,一定是哪里不对劲!
排查过程
我们这系统是使用nginx+多台业务服务器部署的架构,nginx充当代理转发,也起到负载均衡的作用。
我使用内部的地址单独访问了背后的每一台业务服务器,刷新多次,都没有出现这个问题。
而一旦使用nginx代理后的域名访问,就会出现。
直觉和经验告诉我:问题肯定出在转发这里。
思来想去,还是从前端开始入手来排查。
随后我开了两个浏览器窗口,一个正常打开,一个白屏。
祭出F12大法,准备比较一下两种情况下的差异。
先来看看浏览器的控制台窗口,果然有所发现:
点击过去查看详情,发现报错的正是要加载的首页的HTML网页内容:
网页内容被压缩了,使用浏览器的格式化工具,将其格式化成方便阅读的模式,错误位置进一步锁定在一个JS脚本这里:
我单独请求了一下这个JS文件,并没有什么异常。
再拿这个HTML网页内容和那个能正常打开的窗口里的HTML内容比较一下,发现并没有什么两样。
真是怪了!
正在迷惑之际,控制台窗口的网络连接信息发现了线索:
两个浏览器窗口请求同一个JS文件,正常那个是200,白屏那个是302!
为什么会有302的出现?
我将域名替换成内部业务服务器的地址,绕开nginx,单独向每一台服务器请求这个JS文件。
果然!
有一台给我返回了302!
而且不管怎么刷新,它总是返回302,其他几台都能正常返回。
接着,我登录了这台服务器,检查对应路径下的JS文件,确实有一个文件,但名字却不同:
注意文件名中间那一串十六进制数字,跟前面请求的东西不是同一个。
另外几台机器我也检查了,没有问题,名字跟请求的一致。
咱也不是专业的前端,只知道这个名字是VUE打包后生成的,每一次打包都会不同。
看来这一台出问题的服务器上使用的前端资源包版本跟其他几台不一样。
只要将这台服务器的前端资源更新,问题就可解决。
为什么白屏?
接下来就是来解释一个问题:为什么单独请求每一台服务器能正常打开页面,而经过nginx转发后会出现白屏的现象?
要回答这个问题,先得来理解一下浏览器渲染一个页面的基本过程。
当输入一个页面地址后,浏览器首先取回这个地址背后的HTML网页。
浏览器收到后,在解析HTML网页的时候,会发现网页中又引入了JS、CSS、图片等这些资源文件,于是又去请求它们。
注意,这里就有一个问题:
请求HTML网页的动作和请求这些资源的动作,是放在同一个TCP连接中进行,还是分开单独建立TCP连接进行?
这个问题也正是HTTP协议的1.1版本对1.0版本的一个重要升级。
在HTTP 1.0版本中,默认是每个资源单独建立TCP连接去请求。
在HTTP 1.1版本中,引入了长连接机制——keep-alive,可以在同一条TCP连接中请求多个资源。
那这跟白屏又有什么关系呢?
nginx的转发是基于连接的,同一个连接中的多个请求会转发给同一个服务器。
这样,HTML和它里面嵌入的那些资源,都是走的同一个连接,发到了同一台服务器,HTML中引入的JS文件名字和这台服务器上存放的JS文件名字是匹配的。
反之,如果HTML请求和那些资源的请求,走的是不同的连接,就可能会被nginx转发到不同的服务器,就可能会出现HTML里面引入的JS文件名,和被转发到的服务器上存放的JS文件资源不匹配,张冠李戴了!
而当我绕过nginx,直接使用内部域名来请求时,HTML和资源请求不管是不是走的同一个连接,都是那一台服务器负责处理,虽然这台服务器跟别的服务器前端包的版本不同,但其HTML和JS是匹配的,所以不会出现张冠李戴的现象,也就不会白屏。
那基于这个设定,浏览器到nginx的连接肯定就不是长连接,而是短连接了!
我抓包验证了一下:
好家伙,看看这是多少条连接。
再点进去看一下:
好家伙,nginx居然用的HTTP 1.0!
真相自此大白!