7月27日,我收到百度的面試邀約(8月1日),興奮之余,我也做了5天的面試準(zhǔn)備。從下午兩點(diǎn)開始一直面試到五點(diǎn)半,長達(dá)三個半小時的面試也讓我知道百度確實(shí)是一家重視技術(shù)的公司。其中有一道面試題尤其讓我印象深刻,因?yàn)樗謩e是一面和二面的 “壓軸題”,可以說這道面試題自己回答的好與否直接決定著自己能否進(jìn)入下一輪面試,今天我主要就從這道面試題開始,一步一步講解其中涉及到的知識點(diǎn)。這次的面經(jīng)(四面)會在下一篇文章總結(jié)后分享給大家。
真題:
PHP 如何解決網(wǎng)站大流量與高并發(fā)的問題(一面最后一題)
變體:
網(wǎng)站高并發(fā)的優(yōu)化方法,從瀏覽器到服務(wù)器中間經(jīng)過所有組件的你能想到的優(yōu)化點(diǎn)(二面最后一題)
高并發(fā)架構(gòu)相關(guān)概念
高并發(fā)解決方案(優(yōu)化方案)
由于涉及到的知識點(diǎn)太多,我這里先給出我當(dāng)時回答的總的結(jié)構(gòu),之后會對每個分點(diǎn)進(jìn)行更為詳細(xì)的分析,如果你有更好的答案,或者有一些遺漏的點(diǎn)還請指出。
并發(fā)(百度百科的定義):
并發(fā),在操作系統(tǒng)中,是指一個時間段中有幾個程序都處于已啟動運(yùn)行到運(yùn)行完畢之間,且這幾個程序都是在同一處理機(jī)上運(yùn)行,但任意時刻點(diǎn)上都只有一個程序在處理機(jī)上運(yùn)行
web開發(fā)中說的高并發(fā)指的是什么?
上面的定義明顯不是我們通常所言的并發(fā),在互聯(lián)網(wǎng)時代,所講的并發(fā)、高并發(fā),通常是指并發(fā)訪問。也就是在某個時間點(diǎn),有多少個訪問同時到來,通常如果一個系統(tǒng)的日 PV 在干萬以上,有可能是一個高并發(fā)的系統(tǒng)。
高并發(fā)的問題,我們具體該關(guān)心什么?
QPS:
每秒鐘請求或者査詢的數(shù)量,在互聯(lián)網(wǎng)領(lǐng)域,指毎秒響應(yīng)請求數(shù)(指 HTTP 請求),一個頁面中可能有多個 HTTP 請求。
吞吐量:
單位時間內(nèi)處理的請求數(shù)量(通常由QPS與并發(fā)數(shù)決定)
響應(yīng)時間:
從請求發(fā)出到收到響應(yīng)花費(fèi)的時間。例如系統(tǒng)處理一個 HTTP 請求需要 100ms,這個 100ms 就是系統(tǒng)的響應(yīng)時間
PV:
綜合瀏覽量(Pageview),即頁面瀏覽量或者點(diǎn)擊量
UV:
獨(dú)立訪客(UniqueVisitor),即一定時間范圍內(nèi)相同訪客多次訪問網(wǎng)站,只計算為 1 個獨(dú)立訪客
帶寬:
計算帶寬大小需關(guān)注兩個指標(biāo),峰值流量和頁面的平均大小
日網(wǎng)站帶寬 = PV/統(tǒng)計時間(換算到秒)* 平均頁面大?。▎挝?KB)* 8
常用的壓測工具
ApacheBench、WRK、http_load、Web bench、Siege、Apache Jmeter
ApacheBench 概念
ApacheBench(ab),是 Apache 官方推出的工具刨建多個并發(fā)訪問線程,模擬多個訪問者同時對某一 URL 地址進(jìn)行訪問。它的測試目標(biāo)是基于 URL 的,因此,它既可以用來測試 Apache 的負(fù)載壓力,也可以測試 Nginx、 Lighttpd、 Tomcat、IS 等其它 Web 服務(wù)器的壓力。
ApacheBench 的使用
模擬并發(fā)請求 1000次,總共請求 50000次
ab -c 1000 -n 50000 www.test.com
注意事項
測試機(jī)器與被測試機(jī)器分開
不要對線上服務(wù)做壓力測試
觀察測試工具 ab 所在機(jī)器,以及被測試的前端機(jī)的 CPU,內(nèi)存,網(wǎng)絡(luò)等都不超過最高限度的 75% (top 命令)
高并發(fā)解決方案(優(yōu)化方案)
流量層優(yōu)化:
防盜鏈處理
前端層優(yōu)化:
減少 HTTP 請求
添加異步請求
啟用瀏覽器緩存和文件壓縮
CDN 加速
建立獨(dú)立圖片服務(wù)器
服務(wù)器層優(yōu)化:
頁面靜態(tài)化
并發(fā)處理
數(shù)據(jù)庫層優(yōu)化:
數(shù)據(jù)庫緩存(Memache, Redis)
分庫分表(水平、垂直切分)、分區(qū)操作
讀寫分離
負(fù)載均衡
Web 服務(wù)器架構(gòu)優(yōu)化:
負(fù)載均衡
如果我們發(fā)現(xiàn)我們的網(wǎng)站主頁的流量很大(服務(wù)器負(fù)荷很重)但是網(wǎng)站的 PV、UV 卻很小,這時候我們就需要考慮我們的網(wǎng)站是不是被盜鏈了。
盜鏈概念
盜鏈?zhǔn)侵冈谧约旱捻撁嫔险故疽恍┎⒉辉谧约悍?wù)器上的內(nèi)容獲得他人服務(wù)器上的資源地址,繞過別人的資源展示頁面,直接在自己的頁面上向最終用戶提供此內(nèi)容,常見的是小站盜用大站的圖片、音樂、視頻、軟件等資源,通過盜鏈的方法可以減輕自己服務(wù)器的負(fù)擔(dān),因?yàn)檎鎸?shí)的空間和流量均是來自別人的服務(wù)器。
防盜鏈概念
防止別人通過一些技術(shù)手段繞過本站的資源展示頁面,盜用本站的資源,讓繞開本站資源展示頁面的資源鏈接失效,可以大大減輕服務(wù)器及帶寬的壓力。
工作原理
通過 Referer 或者簽名,網(wǎng)站可以檢測目標(biāo)網(wǎng)頁訪問的來源網(wǎng)頁,如果是資源文件,則可以跟蹤到顯示它的網(wǎng)頁地址。一旦檢測到來源不是本站即進(jìn)行阻止或者返回指定的頁面。
Referer 方式防盜鏈
Nginx 模塊 ngx_http_referer_module 用于阻擋來源非法的域名請求。
配置
location ~ .*\.(gifljpglpnglflvlswfrarlzips)${
valid_referers none blocked test.com * test.com:
if($invalid_referer){
#return 403:
rewrite ^/ http://www.test.com/403.jpg;
}
}
valid_referers none I blocked I server_names,string
解釋
none: 'Referer' 來源頭部為空的情況
blocked: 'Referer' 來源頭部不為空,但是里面的值被代理或者防火墻刪除了,這些值都不以 http:// 或者 https:// 開頭。
server names: 'Referer' 來源頭部包含當(dāng)前的 server names
加密簽名防盜鏈
Referer 這種方式雖然可以防止一下低中級的防盜鏈,但是更高級的盜鏈往往可以偽造 Referer,從而繞過 Referer 檢測,更安全的方式是可以采用加密簽名解決 。
使用第三方模塊 HttpaccesskeyModule 實(shí)現(xiàn) Nginx 防盜鏈
Nginx 配置:
accesskey on off 模塊開關(guān)
accesskey_hashmethod md5 | sha-1 簽名加密方式
accesskey_arg GET 參數(shù)名稱
accesskey signature 加密規(guī)則
location ~ .*\.(gifljpglpnglflvlswfrarlzips)${
accesskey on;
accesskey_hashmethod md5;
accesskey_arg sign;
accesskey_signature'yourPrefix$remote_addr; # 你的前綴碼和客戶端 IP
}
PHP 代碼段:
$sign= md5('yourPrefix'. $_SERVER['remote_addr'];
echo '<img src=”./youimage.png?sign=. $sign.''>';
減少 HTTP 請求
性能黃金法則:
只有 10% - 20% 的最終用戶響應(yīng)時間花在接收請求的 HTML 文檔上,剩下的 80% - 90% 時間花在 HTML 文檔所引用的所有組件(圖片,腳本,CSS,F(xiàn)lash 等等)進(jìn)行的 HTTP 請求上。
如何改善:
改善響應(yīng)時間的最簡單途徑就是減少組件數(shù)量,并由此減少 HTTP 請求的數(shù)量。
減少 HTTP 請求的方式
圖片地圖
圖片地址允許你在一個圖片上關(guān)聯(lián)多個 URL,目標(biāo) URL 的選擇取決于用戶單擊了圖片上的哪個未知。
將五張圖片合并為一張圖片,然后以位置信息定位超鏈接,把五個 HTTP 請求減少為一個,可以保證設(shè)計的完整性和功能的齊全性
使用 標(biāo)簽
CSS 精靈(CSS Sprites)
Sprites 中文翻譯為 CSS 精靈,通過使用合并圖片,通過指定 CSS 的 background-image 和 background-position 來顯示元素。
合并腳本和樣式表
使用外部的 JS 和 CSS 文件引用的方式,因?yàn)檫@要比直接寫在頁面中性能更好一點(diǎn)
獨(dú)立的一個 JS 比用多個 JS 文件組成的頁面載入要快 38%
把多個腳本合并為一個腳本,把多個樣式表合并為一個樣式表
圖片使用 Base64 編碼減少頁面請求數(shù)
采用 Base64 的編碼方式將圖片直接嵌入到網(wǎng)頁中,而不是從外部載入。
添加異步請求
Ajax 可以實(shí)現(xiàn)動態(tài)不刷新(局部刷新),就是能在不更新整個頁面的前提下維護(hù)數(shù)據(jù)。這使得 Web 應(yīng)用程序更為迅捷地回應(yīng)用戶動作,并避免了在網(wǎng)絡(luò)上發(fā)送那些沒有改變過的信息。
優(yōu)勢:
通過異步模式,提升了用戶體驗(yàn)
優(yōu)化了瀏覽器和服務(wù)器之間的傳輸,減少不必要的數(shù)據(jù)往返,減少了帶寬占用
Ajax 引擎在客戶端運(yùn)行,承擔(dān)了一部分本來由服務(wù)器承擔(dān)的工作,從而減少了大用戶量下的服務(wù)器負(fù)載
啟用瀏覽器緩存和文件壓縮
緩存分類:
HTTP 緩存模型中,如果請求成功會有三種請款
200 from cache:直接從本地緩存中獲取響應(yīng),最快速,最省流量,因?yàn)楦緵]有向服務(wù)器發(fā)送請求
304 Not Modified:協(xié)商緩存,瀏覽器再本地沒有命中的情況下請求頭中發(fā)送一定的校驗(yàn)數(shù)據(jù)到服務(wù)端,如果服務(wù)端數(shù)據(jù)沒有改變?yōu)g覽器從本地緩存響應(yīng),返回304,快速,發(fā)送的數(shù)據(jù)很少。只返回一些基本的響應(yīng)頭信息,數(shù)據(jù)量很小,不發(fā)送實(shí)際響應(yīng)體
200 OK:以上兩種緩存全部失敗,服務(wù)器返回完整響應(yīng),沒有用到緩存,相對最慢
本地緩存
瀏覽器認(rèn)為本地緩存可以使用,不會去請求服務(wù)端
相關(guān) Header:
Expires:expires 值對應(yīng)一個形如 Thu,32 Dec 2037 23:55:55 GMT 的格林威治時間,告訴瀏覽器緩存實(shí)現(xiàn)的時刻,如果還沒到該時刻,標(biāo)明緩存有效,無需發(fā)送請求(絕對時間)
Cache-Control(設(shè)置緩存秒數(shù)):運(yùn)用 Cache-Control 告知瀏覽器緩存過期的時間間隔不是時刻(時間間隔)
no-store:禁止瀏覽器緩存響應(yīng)
no-cache:不允許直接使用本地緩存,先發(fā)起請求和服務(wù)器協(xié)商
max-age=delta-seconds:告知瀏覽器該響應(yīng)本地緩存有效的最長期限,以秒為單位
協(xié)商緩存
當(dāng)瀏覽器沒有命中本地緩存,如本地緩存過期或者響應(yīng)中聲明不允許直接使用本地緩存,那么瀏覽器肯定會發(fā)起服務(wù)端請求,服務(wù)端會驗(yàn)證數(shù)據(jù)是否修改,如果沒有通知瀏覽器使用本地緩存
相關(guān) Header:
Last-Modified:通知瀏覽器資源的最后修改時間
If-Modified-Since:得到資源的最后修改時間后,會將這個信息通過 If-Modified-Sinc 提交到服務(wù)器做檢查,如果沒有修改,返回 304 狀態(tài)碼
Etag:HTTP1.1 推出,文件的指紋標(biāo)識符,如果文件內(nèi)容修改,指紋會改變
If-None-Match:本地緩存失效,會攜帶此值去請求服務(wù)端,服務(wù)端判斷該資源是否改變,如果沒有改變,直接使用本地緩存,返回 304
適合緩存的內(nèi)容
不變的圖像,如 logo,圖標(biāo)等
JS、CSS 靜態(tài)文件
可下載的內(nèi)容,媒體文件
建議使用協(xié)商緩存
HTML 文件
經(jīng)常替換的圖片
經(jīng)常修改的 JS、CSS 文件
JS、CSS 文件的加載可以加入文件的簽名來拒絕緩存
index.css? 簽名
不建議緩存的內(nèi)容
用戶隱私等敏感數(shù)據(jù)
經(jīng)常改變的 API 數(shù)據(jù)接口
前端代碼和資源的壓縮
優(yōu)勢:
讓資源文件更小,加快文件再網(wǎng)絡(luò)中的傳輸,讓網(wǎng)頁更快的展現(xiàn),降低帶寬和流量開銷
壓縮方式:
JS、CSS、圖片、HTML 代碼的壓縮、Gzip 壓縮
JavaScript 代碼壓縮:
JavaScript 壓縮的原理一般是去掉多余的空格和回車、替換長變量名、簡化一些代碼寫法等
JavaScript 代碼壓縮工具很多,有在線工具,有應(yīng)用程序,有編輯器插件
CSS 代碼壓縮:
原理跟 JavaScript 壓縮原理類似,同樣是去除空白符、注釋并且優(yōu)化一些 CSS 語義規(guī)則等
使用 CDN 加速
CDN 的全稱是 Content Delivery Network, 即內(nèi)容分發(fā)網(wǎng)絡(luò),盡可能避開互聯(lián)網(wǎng)上有可能影響數(shù)據(jù)傳輸速度和穩(wěn)定性的瓶頸和環(huán)節(jié),使內(nèi)容傳輸?shù)母?,更穩(wěn)定。
在網(wǎng)絡(luò)各處放置節(jié)點(diǎn)服務(wù)器所構(gòu)成的在現(xiàn)有的互聯(lián)網(wǎng)基礎(chǔ)之上的一層智能虛擬網(wǎng)絡(luò)
CDN 系統(tǒng)能夠?qū)崟r地根據(jù)網(wǎng)絡(luò)流量和各節(jié)點(diǎn)的連接、負(fù)載狀況以及到用戶的距離和響應(yīng)實(shí)踐等綜合信息將用戶的請求重新導(dǎo)向離用戶最近的服務(wù)器節(jié)點(diǎn)上。
使用 CDN 的優(yōu)勢
本地 cache 加速, 提高企業(yè)站點(diǎn)(尤其含有大量圖片和靜態(tài)頁面站點(diǎn))的訪問速度
跨運(yùn)營商的網(wǎng)絡(luò)加速, 保證不同網(wǎng)絡(luò)的用戶都得到良好的訪問質(zhì)量
遠(yuǎn)程訪問用戶根據(jù) DNS 負(fù)載均衡技術(shù)智能自動選擇 cache 服務(wù)器
自動生成服務(wù)器的遠(yuǎn)程 Mirror(鏡像)cache 服務(wù)器,遠(yuǎn)程用戶訪問時從 cache 服務(wù)器上讀取數(shù)據(jù),減少遠(yuǎn)程訪問的帶寬、分擔(dān)網(wǎng)絡(luò)流量、減輕原站點(diǎn) Web 服務(wù)器負(fù)載等功能。
建立獨(dú)立的圖片服務(wù)器
好處
分擔(dān) Web 服務(wù)器的 I/O 負(fù)載將耗費(fèi)資源的圖片服務(wù)分離出來,提高服務(wù)器的性能和穩(wěn)定性
能夠?qū)iT對圖片服務(wù)器進(jìn)行優(yōu)化-為圖片服務(wù)設(shè)置有針對性的緩存方案,減少帶寬成本,提高訪問速度
提高網(wǎng)站的可擴(kuò)展性-通過增加圖片服務(wù)器,提高圖片吞吐能力
頁面靜態(tài)化(動態(tài)語言靜態(tài)化)
將現(xiàn)有 PHP、Python 等動態(tài)語言的邏輯代碼生成為靜態(tài) HTML 文件,用戶訪問動態(tài)腳本重定向到靜態(tài) HTML 文件的過程
使用場景
對實(shí)時性要求不高的頁面
好處
動態(tài)腳本通常會做邏輯計算和數(shù)據(jù)查詢,訪問量越大,服務(wù)器壓力越大
訪問量大時可能會造成 CPU 負(fù)載過高,數(shù)據(jù)庫服務(wù)器壓力過大
PHP 的并發(fā)處理
PHP 的 Swoole 擴(kuò)展
PHP 的異步、并行、高性能網(wǎng)絡(luò)通信引擎,使用純 C 語言編寫,提供了 PHP 語言的異步多線程服務(wù)器,異步 TCP/UDP 網(wǎng)絡(luò)客戶端,異步 MYSQL,異步 Redis,數(shù)據(jù)庫連接池,Asynctask,消息隊列,毫秒定時器,異步文件讀寫,異步 DNS 查詢,除了異步 I/O 的支持之外, Swoole 為 PHP 多進(jìn)程的模式設(shè)計了多個并發(fā)數(shù)據(jù)結(jié)構(gòu)和 IPC 通信機(jī)制,可以大大簡化多進(jìn)程并發(fā)編程的工作。
消息隊列
使用場景
用戶注冊:
場景說明
用戶注冊后,需要發(fā)注冊郵件和注冊短信
串行方式
將注冊信息寫入數(shù)據(jù)庫成功后,發(fā)送注冊郵件,再發(fā)送注冊短信
并行方式
將注冊信息寫入數(shù)據(jù)庫成功后,發(fā)送注冊郎件的同時發(fā)送注冊短信
消息隊列方式
將注冊信息寫入數(shù)據(jù)庫成功后,將成功信息寫入隊列,此時直接返回成功給用戶,寫入隊列的時間非常短,可以忽略不計,然后異步發(fā)送郵件和短信
日志處理:
應(yīng)用場景
解決大量日志的傳輸,日志采集程序?qū)⒊绦驅(qū)懭胂㈥犃?,然后通過日志處理程序的訂閱消費(fèi)日志
常見消息隊列產(chǎn)品
Kafka、 Activemq、 Zeros、 Rabbitmq、 Redis 等
數(shù)據(jù)庫緩存(Memache, Redis)
MySQL 等一些常見的關(guān)系型數(shù)據(jù)庫的數(shù)據(jù)都存儲在磁盤當(dāng)中,在高并發(fā)場景下,業(yè)務(wù)應(yīng)用對 MySQL 產(chǎn)生的增、刪、改、查的操作造成巨大的 I/O 開銷和查詢壓力,這無疑對數(shù)據(jù)庫和服務(wù)器都是一種巨大的壓力,為了解決此類問題,緩存數(shù)據(jù)的概念應(yīng)運(yùn)而生。
優(yōu)點(diǎn):
極大地解決數(shù)據(jù)庫服務(wù)器的壓力
提高應(yīng)用數(shù)據(jù)的響應(yīng)速度
使用 Memcache 緩存查詢數(shù)據(jù)
對于大型站點(diǎn),如果沒有中間緩存層,當(dāng)流量打入數(shù)據(jù)庫層時,即便有之前的幾層為我們擋住一部分流量,但是在大并發(fā)的情況下,還是會有大量請求涌入數(shù)據(jù)庫層,這樣對于數(shù)據(jù)庫服務(wù)器的壓力沖擊很大,響應(yīng)速度也會下降,因此添加中間緩存層很有必要。
工作原理
Memcache 是一個高性能的分布式的內(nèi)存對象緩存系統(tǒng),通過在內(nèi)存里維護(hù)一個統(tǒng)一的巨大的 hash 表,它能夠用來存儲各種格式的數(shù)據(jù),包括圖像、視頻、文件以及數(shù)據(jù)庫檢索的結(jié)果等。簡單的說就是將數(shù)據(jù)調(diào)用到內(nèi)存,然后從內(nèi)存中讀取,從而大大提高讀取速度
使用 Redis 綬存查詢數(shù)據(jù)
Memcache 的區(qū)別:
Redis 支持(快照、AOF),依賴快照進(jìn)行持久化,aof增強(qiáng)了可靠性的同時,對性能有所影響
Memcache 不支持持久化,通常做緩存,提升性能
Redis 支持多種類的數(shù)據(jù)類型
Redis 用于數(shù)據(jù)量較小的高性能操作和運(yùn)算上
Memcache 用于在動態(tài)系統(tǒng)中減少數(shù)據(jù)庫負(fù)載,提升性能,適合做緩存,提高性能
數(shù)據(jù)表數(shù)據(jù)類型優(yōu)化
tinyint (0-255) smallint, bigint(考慮空間的問題,考慮范圍的問題)
char,vachar
enum 特定、固定的分類可以使用 enum 存儲,效率更快
IP 地址的存儲,用 PHP 的 ip2long('192.168.1.38'); //3232235814
索引優(yōu)化
索引不是越多越好,在合適的字段上創(chuàng)建合適的索引
復(fù)合索引的前綴原則
lke 查詢 % 的問題(% 在前如:%name 則索引失?。?/p>
全表掃描優(yōu)化(MySQL 自動識別)
or 條件索引使用情況(or 后的索引不會使用)
字符串類型索引失效的問題(如字符串類型的字段必須要加引號查詢)
SQL 語句的優(yōu)化
使用慢查詢?nèi)罩菊业叫枰獌?yōu)化的 SQL 語句,一般會再用 explain 分析。
優(yōu)化查詢過程中的數(shù)據(jù)訪問
使用 Limit
返回列不用 *
優(yōu)化特定類型的查詢語句
優(yōu)化關(guān)聯(lián)查詢
優(yōu)化子查詢
優(yōu)化 Group by 和 distinct
數(shù)據(jù)庫服務(wù)器架構(gòu)的優(yōu)化
分庫分表
水平切分
垂直切分
讀寫分離
負(fù)載均衡
通過 LVS 的三種基本模式實(shí)現(xiàn)負(fù)載均衡
My Cat 數(shù)據(jù)庫中間件實(shí)現(xiàn)負(fù)載均衡
七層負(fù)載均衡的實(shí)現(xiàn)
基于 URL 等應(yīng)用層信息的負(fù)載均衡
Nginx 的 proxy 是它一個很強(qiáng)大的功能,實(shí)現(xiàn)了7層負(fù)載均衡
Nginx 的簡單配置
Nginx 配置
http{
upstream test_cluster {
server 121.41.68.2:8001 weight=11;// 加權(quán)中
server 121.41.69.2:8002 weight=10;
#server 121.41.68.2:8003;
#server 121.41.68.9;
}
server {
listen 80;
location / {
proxy_pass http: //test_cluster;
}
}
}
四層負(fù)載均衡的實(shí)現(xiàn)
通過報文中的目標(biāo)地址和端口,再加上負(fù)載均衡設(shè)備設(shè)置的服務(wù)器選擇方式,決定最終選擇的內(nèi)部服務(wù)器
LVS 實(shí)現(xiàn)服務(wù)器集群負(fù)載均衡有三種方式,NAT,DR 和 TUN