知识点梳理iOS 其他乱七八糟风暴

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

2015-12-08  本文已影响1392人  猫哥学前班

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

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

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

从 BAT 面试题说起

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

这里有:

  1. 国内最大的第三方支付舞台,体验亿万资金穿梭代码的快感;
  2. 一群热爱前端技术的伙伴,最快的成长经历;
  3. 持续的培训体系,完善的项目开发环境,最具潜力的UED团队。

你需要:

  1. 热爱前端,热爱设计,对新鲜事物充满好奇心,喜欢捣鼓各种互联网应用;(兴趣、学习能力、创新能力)
  2. 自我管理能力强,健康的创业心态,乐于分享与沟通;(心态、分享、性格)
  3. 具备基本的前端素质,了解WEB标准化、性能优化方法,了解可用性、可访问性;(基本技能)
  4. 能和设计师谈产品设计,和后端开发研讨技术实现方案,制定服务接口,崇尚团队合作;(向前向后能力)

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

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

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

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

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

解题思路

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

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

1、安装 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

2、查看 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 协议的格式规范:

这便是一个 HTTP 协议报文的源码格式,以下我们简单讲解下最常见的 HTTP header 的含义。

3、常见 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 状态码,与网站性能优化有关的主要是以下几个。

如果你对上文提及的「网络性能优化」的知识点十分感兴趣,建议你通读 Steve Souders 的《高性能网站建设指南》与《高性能网站建设进阶指南》,Steve Souders 的个人网站上积累了很多性能优化的方法与案例。

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

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

上一篇下一篇

猜你喜欢

热点阅读