猫哥网络编程系列:详解 BAT 面试题

从产品上线前的接口开发和调试,到上线后的 bug 定位、性能优化,网络编程知识贯穿着一个互联网产品的整个生命周期。不论你是先后端的开发岗位,仍是 SQA、运维等其余技术岗位,掌握网络编程知识均是岗位的基础要求,即便是产品、设计等非技术岗位,在灰度环境体验产品时也须要理解页面缓存、Host 切换等网络基础概念。php

「猫哥网络编程系列」一直是我想沉淀的一个技术知识点,由于我认为:网络编程相关知识(尤为是 HTTP 协议),是互联网产品开发当中最重要的基础知识(没有之一)。掌握这方面的基础知识,对一个新手程序员来讲相当重要。本系列将会在个人微信公众号「猫哥学前班」上连载,并在 Github 上维护更新css

使用「详解 BAT 面试题」做为本文的副标题,是为了吸引更多的技术新人浏览此文,然而本文并不是标题党。掌握本文所提到的知识点必将大幅提高程序员的面试成功率,由于「网络编程」方面的基础知识,是 BAT 面试的必考项目。html

从 BAT 面试题提及

2009 年我在支付宝作前端开发时,参与草拟了一份非正式的前端岗位招聘要求。前端

这里有:

国内最大的第三方支付舞台,体验亿万资金穿梭代码的快感;git

一群热爱前端技术的伙伴,最快的成长经历;程序员

持续的培训体系,完善的项目开发环境,最具潜力的UED团队。github

 

你须要:

热爱前端,热爱设计,对新鲜事物充满好奇心,喜欢捣鼓各类互联网应用;(兴趣、学习能力、创新能力)web

自我管理能力强,健康的创业心态,乐于分享与沟通;(心态、分享、性格)面试

具有基本的前端素质,了解WEB标准化、性能优化方法,了解可用性、可访问性;(基本技能)数据库

能和设计师谈产品设计,和后端开发研讨技术实现方案,制定服务接口,崇尚团队合做;(向前向后能力)

当时在面试时最流行问的前端技术问题是:Web 标准化、AJAX 与 YSlow

  • 问:一个 AJAX 请求从开始建立到最后的响应阶段,在其整个生命周期中,使用到了哪些 JavaScript 对象与方法?
  • 问:YSlow 的 34 条性能优化建议中,哪些与 HTTP 协议相关,请尽量多的列举出来,并说说你的理解?

2011 年我开始成为腾讯的前端开发面试官,负责腾讯电商的前端开发(网页重构方向)笔试出题与面试工做。在 2012 年的校招过程当中,我发现不管是我出的笔试题,仍是其余面试官出的题目,HTTP 协议相关的知识都是必考项。例如,

  • 问:HTTP 协议中与缓存相关的 HTTP Header 有哪些?
  • 问:列举出你所知道的 HTTP 状态码,并描述它们的含义与发生的场景?

后来我在学习百度 FIS 框架的过程当中,无心间看到百度 FEX 团队的这份开源前端开发面试题,不出所料,一样有一道与网络编程相关的题目:

一个页面从输入 URL 到页面加载完的过程当中都发生了什么事情?越详细越好

因而可知,网络编程相关知识的确是 BAT 前端面试题中的必考项,而对于负责输出 API 接口的后台开发岗位来讲,更是如此:

  • 问:一个 POST 请求的 Content-Type 有多少种,传输的数据格式有何区别?
  • 问:什么是 RESTful API,如何设计一个 Open API 的接口?

解题思路

接下来咱们一块儿探讨下具体的解题思路,浏览完本文以后,你将会首先掌握 HTTP 协议相关的先后端基础知识。

要掌握 HTTP ,就须要先看到 HTTP 到底长什么样?(不了解「网络七层协议模型」和 TCP 的同窗先不着急,本系列的后面几篇会涉及到。)

一、安装 HTTP 抓包工具

在 Chrome 开发者工具下咱们能够看到,打开一个网页后,浏览器会发起许多 HTTP 的请求(HTTP Request),这些请求通过服务器端处理后会返回对应的数据(HTTP Response),浏览器会按照这些数据的类型将它们渲染出来。

Chrome Network Panel

Chrome 中看到的 Request/Response Header 是其格式化以后的形式,要看到它们的原始模样(Raw Source),咱们须要借助两个 HTTP 接口调试利器。

其中 Windows 系统下使用 Fiddler,Mac 系统下使用 Charles。Fiddler 具体的安装与使用教程,请自行百度(安装 Fiddler4 还需同时安装 .NET Framework 4),Charles 相关教程,推荐参考 iOS 大神唐巧的《Charles 从入门到精通》。使用 Linux 系统的说明已是网络编程方面的大牛了,不须要继续往下看 :P

二、查看 HTTP 详细报文

运行 Fiddler(或 Charles) 以后,使用 Chrome 浏览器打开「猫哥学前班」的新浪微博主页:http://weibo.com/mgxqb

在 Fiddler 左侧面板下选中该条 HTTP 请求,再将右侧面板的请求部分和响应部分都切换到 Raw 标签页。以下图所示:

Fiddler Panel

Charles 下的操做与 Fiddler 相似:

Charles Panel

HTTP 协议规范由 W3C 制定,与具体的抓包工具无关,接下来咱们主要以 Charles 为例,详细讲解下 HTTP 的报文格式,这对理解基于 HTTP 的 API 接口设计和网页性能优化有很大帮助。

咱们先看一下请求头的源码(Request Raw),为了防止隐私泄露,我已删除部分 Cookie 信息:

GET /mgxqb HTTP/1.1
Host: weibo.com
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4
Cookie: YF-Page-G0=f70469e0b5607cacf38b47457e34254f; _s_tentry=passport.weibo.com


仔细观察以上源码,咱们能大概总结出 HTTP 协议的格式规范:

  • 第一行定义了请求类型(GET)、请求路径(/mgxqb)与协议类型及其版本号(HTTP/1.1),使用一个半角空格间隔这三块信息;
  • 示例源码的最后是两个空行。因为 HTTP 规范中要求一个合法的 HTTP 报文至少包含有一个空行,其中第一个空行用来间隔报文的头部信息(HTTP Request/Response Header)和主体信息(HTTP Request/Response Body)。在空行的下一行是报文的主体信息,因为本例为 GET 类型请求,其主体(Body)信息一般为空,这即是第二个空行的含义;
  • 余下的部分有着相同的格式,即 「HTTP Header 字段名+半角冒号+半角空格+值」,咱们能够把它当作YAML 格式的简易版。其中 HTTP Header 在规范中有着明确的定义,具体参见 HTTP头字段列表

这即是一个 HTTP 协议报文的源码格式,如下咱们简单讲解下最多见的 HTTP header 的含义。

三、常见 HTTP header

User-Agent:客户端身份标识

User-Agent (如下简称 UA)字段记录了访问当前网页的用户浏览器的类型与版本、操做系统类型与版本。根据不一样的 UA 信息,提供不一样的站点内容是使用 UA 的常见场景。例如,若是用户使用手机访问魅族官网www.meizu.com,浏览器会自动跳转至魅族手机官网 m.meizu.com。这种跳转实现既能够由前端 JavaScript 完成,也能够经过后端返回 302 重定向来完成。

JavaScript 访问 window.navigator.userAgent 属性便可获取该信息。虽然该属性是只读的,但有不少前端手段能够伪造 UA 。以下图,Chrome 开发者工具在模拟不一样的手机机型时,也会改变浏览器 UA 值。因而可知,经过检测 HTTP User-Agent Header 来识别是否为爬虫程序,不是一个有效的方法。

使用 Chrome 开发者工具模拟不一样手机设备 UA

在 PHP 中,全部的 HTTP Header 字段信息都保存在 $_SERVER 对象中,经过访问$_SERVER['HTTP_USER_AGENT'] 便可获取 User-Agent 的值。

Cookie:用户身份标识

因为 HTTP 协议最初被设计成一种无状态的数据传输协议,服务器端没法判断每次处理的请求相互之间以及与以前处理的请求之间的关系,Cookie 的设计就是为了解决这个问题。

用户在浏览器中首次访问一个站点时,会经过请求响应头或页面JS脚本生成一些用于标识用户身份的 Cookie 信息,这些信息会按照域名分类,存放在浏览器本地缓存文件当中。例如 Windows 系统下经过访问 「C:\Users<用户名>\AppData\Local\Microsoft\Windows\Temporary Internet Files」 目录能够查看到 IE 浏览器保存在本地的 Cookie 文件。当用户再次访问该站点时,这些 Cookie 信息会被浏览器自动添加到 HTTP Request Header 的 Cookie 字段中,服务器经过读取这些信息,来区分当前请求的用户身份与状态。

浏览器能够经过读写 document.cookie 属性来添加或删除 Cookie 信息,服务器端能够经过 HTTP Response Header(响应头)中的 Set-Cookie 来改写客户端的 Cookie 信息。每一条 Cookie 属性一般都会设置一个过时时间,过时以后的 Cookie 浏览器将会自动清理它们,不会再被携带在 HTTP Request Header(请求头)中。

例如,如下 PHP 语句能够经过设置 Cookie 过时时间为前一个小时来触发客户端 Cookie 过时,达到删除 Cookie 的目的:

setcookie('key', '', time() - 3600, '/');

因为 Cookie 一般用于记录用户「账号信息」和用户的「操做记录」,因此泄露 Cookie 会带来我的账号与隐私泄露的风险。这也是为何你在百度上搜索「贷款」的关键词以后,访问其余网站时就能看到相关的推荐广告,甚至次日就会有各类放贷电话找上门来。

又因为 Cookie 能够随意被客户端修改(经过修改 document.cookie 属性),所以浏览器厂商们一块儿制定了HttpOnly 的 Cookie 机制。服务器端在 setcookie 时,经过设置 HttpOnly 的标识,能够防止客户端经过 JavaScript 修改 Cookie 的信息。不过这种方法对于基于 HTTP 协议进行篡改的方法来讲没法防范,在以后的猫哥网络编程系列中,我将会介绍如何经过监控 Wi-Fi 流量来截取、伪造用户身份。

在 YSlow 性能优化最佳实践中,有两条与 Cookie 相关的建议:

虽然浏览器对 Cookie 的大小与数量有着较为严格的限制,但不少网站(尤为是包含登陆态的)的 Cookie 信息量一般比其余全部 HTTP Header 加起来的还要多。为了减小没必要要的 HTTP 数据传输量,YSlow 给出了以上两条优化建议。因为网页的静态资源(图片、CSS、JS)文件无需记录用户状态,所以一般会使用一个额外的域名(Cookie 是按域名来分类存储)来存放静态资源文件。

咱们使用 Chrome 开发者工具查看「猫哥学前班」新浪微博主页,能够看到新浪微博使用了 img.t.sinajs.cn的域名来存放它的 CSS 文件,这个域名发起的 HTTP Request Header 中没有自动带上 Cookie 字段信息 (由于先后端脚本都没有在这个域名上设置 Cookie,而是设置在了 weibo.com 域名上):

Use Cookie-free Domains

这里还须要引伸一个知识点:Session,它和 Cookie 有什么关系?因为 Session 与本文所讲的 HTTP 协议关系不大,相关知识点请自行百度。

Cache-Control:浏览器资源缓存标识

网站性能优化中,最为关键的是缓存机制(又是没有之一)。在服务器端一般会使用 Memcached、Redis 等服务来缓存常常访问的数据。例如在一个电商网站中,用户常常访问的热卖商品数据会被缓存在内存中,用户在必定时间内访问商品详情页时,后台程序直接从缓存服务中获取这段数据,这种方法能够大幅下降数据库的访问压力。

在用户端,浏览器会有一系列机制经过缓存来提高页面加载速度。例如 IE/Chrome 都会缓存 GET 类型的 AJAX 请求,IE 甚至会缓存 POST 类型的请求,须要经过增长时间戳参数的方式来强制清除缓存。对于全部的静态资源文件来讲,最佳实践是为它们增长一个 「Never Expires」(永不过时)的强(长)缓存,如下是一个强缓存静态资源服务器的 Nginx 配置示例:

server {
    listen 80;
    server_name yekai.net;
    root /var/www/yekai.net;

    location / {
        index index.html index.htm;
    }
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {  
        expires 365d;  
    }
}

经过配置 「expires 365d」,HTTP Response Header(响应头)中会返回 「Cache-Control: max-age=31536000」 的头字段,配合 Last-Modified 头字段。浏览器即可以自动完成资源的强缓存。

Cache-Control 是浏览器缓存机制中最为重要的一个配置,如下是浏览器加载静态资源文件时的缓存检查机制流程:

浏览器缓存检查机制流程

因而可知,静态资源缓存优化的最佳状态是:直接从本地缓存中读取 > 304 状态 > 200 状态。关于 HTTP 状态码,与网站性能优化有关的主要是如下几个。

  • 尽可能减小 200 状态码的请求。200 表示是一个正常的请求返回,此条优化规则要求尽量多的减小页面的 HTTP Request 数量。常见的方法有:合并打包静态资源、使用 CSS Sprite 雪碧图合并、缓存 AJAX、使用 LocalStorage/UserData/Manifest 等本地缓存技术。
  • 清理返回 301/302 状态码的入口连接。301 表示永久重定向,302 表示临时重定向。服务器端使用重定向返回一般是为了兼容一个旧的入口连接。咱们能作的优化是,将调用旧入口的场景进行清理,直接调用重定向以后的新 URL 地址。
  • 304 表示静态资源未更新,浏览器可直接使用本地缓存文件。一般 304 的产生与浏览器的处理机制以及服务器缓存头配置有必定的关系。304 虽然未传输文件主体内容,但 HTTP 请求的创建依然是一个能够避免的性能损耗。腾讯 KM(内部知识分享平台)上有一篇文章经过在真实海量业务场景(没记错的话是 Qzone 业务)中,正交验证 HTTP 1.0 与 1.1 协议中与缓存相关的 HTTP Header 配置,结合日志分析得出了一个最佳实践:关闭 Etag 配置,只启用 Cache-Control 与 Last-Modified 响应头。为了兼容老浏览器,可保留 Expires。由于 Etag 的缓存方案,在通过 CDN 及网关代理服务器后,会致使缓存命中率降低。从以上「浏览器缓存检查机制流程」图上能够看出,使用强缓存(Cache-Control max-age 设置为一年)后浏览器在资源过时前不会发起 HTTP 请求,那如何保证静态资源在服务器上更新后本地的缓存也能同步更新呢?可参考百度 FIS 的「文件指纹」方案。
  • 清理返回 404 状态码的入口连接。静态资源文件的 404 调用需严格避免,而入口页面的 404 则在所不免。经过在全站 404 页面进行产品引导与体验优化,并结合数据上报记录来源页(HTTP Referer Header 或 document.referrer),能够找到并清理 404 来源入口。对于由搜索引擎进入的来源,可经过主动提交新索引至搜索引擎,或使用 301/302 重定向的方式,有效利用起这些「被浪费的流量」。
  • 502 服务器出错。若是是 Nginx + FastCGI 的常见架构,一般是因为 Nginx 缓冲区溢出或服务器资源被耗尽引发,针对不一样的业务场景进行 Nginx 的配置优化能显著提高服务器抗压性能。

若是你对上文说起的「网络性能优化」的知识点十分感兴趣,建议你通读 Steve Souders 的《高性能网站建设指南》与《高性能网站建设进阶指南》,Steve Souders 的我的网站上积累了不少性能优化的方法与案例。

若是你能看到这里,相信你已经知道如何解答前文提到的几道 BAT 网络编程面试题中,关于 「HTTP 协议、状态码、缓存与性能优化」相关的问题。

在下一章节中,咱们会继续 HTTP 协议的话题,并详细讲解 HTTP POST 相关的网络编程细节与调试技巧,相信看完以后你将能轻松定位并解决全部接口联调的问题与 Bug:)

相关文章
相关标签/搜索