貓哥網絡編程系列:詳解 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:)

相關文章
相關標籤/搜索