【高併發解決方案】php
1. 瞭解 PV、UV、QPS
2. 優化方案:防盜鏈、減小HTTP請求、瀏覽器緩存、CDN、數據庫緩存、MySQL讀寫分離、分區分庫分表、LVS負載均衡
QPS:每秒鐘請求或者查詢的數量,在互聯網領域,指每秒響應請求數(指HTTP請求);
吞吐量:單位時間內處理的請求數量(一般由QPS與併發數決定);
響應時間:從請求發出到收到響應花費的時間;
PV:綜合瀏覽量(Page View),即頁面瀏覽量或者點擊量,一個訪客在 24 小時內訪問的頁面數量。同一我的瀏覽你的網站同一頁面,只記做一次PV;
UV:獨立訪客(UniQue Visitor),即必定時間範圍內相同訪客屢次訪問網站,只計算爲1個獨立訪客;
【QPS】
QPS(Query Per Second),QPS 實際上是衡量吞吐量(Throughput)的一個經常使用指標,
就是說服務器在一秒的時間內處理了多少個請求 —— 咱們一般是指 HTTP請求,顯然數字越大表明服務器的負荷越高、處理能力越強。
併發用戶數 是指系統能夠同時承載的正常使用系統功能的用戶的數量。
性能測試工具ab的用法:例如模擬併發請求 100次,總共請求 5000次
ab -c 100 -n 5000 待測試網站:
-c 表示 併發數,這兒是100; -n表示 總共請求次數,這兒是5000)
QPS達到極限,該怎麼辦?
數據庫緩存層、數據庫的負載均衡。
CDN加速、負載均衡。
靜態HTML緩存。
作業務分離,分佈式存儲。
高併發解決方案案例
(1)流量優化:
防盜鏈處理。
(2)前端優化:
減小HTTP請求、
添加異步請求、
啓用瀏覽器緩存和文件壓縮、
CDN 加速、
創建獨立圖片服務器
(3)服務端優化:
頁面靜態化、
併發處理、
隊列處理
(4)數據庫優化:
使
用緩存(memcache)、
分庫分表分區、
讀寫分離、
負載均衡
(5)Web 服務器優化:
負載均衡(Nginx 反向代理實現負載均衡;七層/四層(LVS)軟件實現負載均衡。)
【服務器負載太高的解決方案】
執行top -c命令,找到cpu最高的進程的id
top命令查詢服務器負載達到2.0-5之間,nginx的cpu使用率達到104%
問題分析過程:
(1)df -h 命令,查看磁盤是不是否超出正常範圍
(2)free 命令,查看內存使用率是否超出正常範圍
問題引出:如何肯定nginx的100%的cpu使用率到底問題在哪?
ps -ef | grep nginx 查詢出nginx的進程PID(eg:8209)
ps -aux | grep nginx 查詢出該進程是哪一個用戶啓動的(即便ROOT用戶可能也導出線程快照失敗)
su root 切換到進程啓動用戶
【防盜鏈】
防盜鏈的工做原理:
一、經過
Referer 或者
簽名,網站能夠檢測目標網頁訪問的來源網頁,若是是資源文件,則能夠跟蹤到顯示它的網頁地址;
二、一旦檢測到來源表示本站即進行阻止或者返回指定的頁面;
三、經過計算簽名的方式,判斷請求是否合法,若是合法則顯示,不然返回錯誤信息。
防盜鏈的實現方法
一、Referer
Nginx 模塊
ngx_http_referer_module 用於阻擋來源非法的域名請求;
Nginx 指令
valid_referers,全局變量
$invalid_referer;
none :「Referer」 來源頭部爲空的狀況;
blocked:「Referer」 來源頭部不爲空,可是裏面的值被代理或者防火牆刪除了;
referer防盜鏈的實現方法:打開配置文件nginx.conf
#對文件防盜鏈
location ~.*\.(gif|jpg|png|flv|swf|rar|zip)$
{
if ($invalid_referer) {
# return 403;
}
}
#對目錄防盜鏈
location /images/
{
…… 同上
}
傳統防盜鏈遇到的問題:經過僞造Referer來實現盜鏈,可使用加密簽名解決該問題。
二、加密簽名
使用第三方模塊
HttpAccessKeyModule 實現 Nginx防盜鏈;
accesskey on | off 模塊開關;
accesskey_hashmethod md5 | sha-1 簽名加密方式;
accesskey_arg GET參數名稱;
accesskey_signature 加密規則(在PHP與Nginx中籤名的加密規則要一致,這樣才能經過,從而進行訪問,不然沒法進行訪問);
加密簽名防盜鏈的實現方法:
location ~.*\.(gif|jpg|png|flv|swf|rar|zip)$
{
accesskey on;
accesskey_hashmethod md5;
accesskey_arg "key";
accesskey_signature "mypass$remote_addr"; // $remote_addr 客戶端IP
}
PHP中用以下代碼實現:
// 客戶端IP 進行md5 加密
$sign = mg5('jason', $_SERVER['REMOTE_ADDR']);
echo '<img src="./logo_new.png?sign='.$sign.'">';
HTTP鏈接產生的開銷:域名解析——TCP鏈接——發送請求——等待——下載資源——解析時間
減小HTTP請求的方式:
圖片地圖、
合併腳本和樣式表
【緩存】
HTTP緩存模型中,若是請求成功會有三種狀況:
【特色:快速,發送的數據不多,只返回一些基本的響應頭信息,數據量很小,不發送實際響應體】;
本地緩存:
瀏覽器認爲本地緩存可使用,不會去請求服務端【它是最快的】。
相關Header:
Cache-Control:HTTP1.1針對 Expires 時間不一致的解決方案,Cache-Control 給的是一個秒數,即該文件多少秒後過時。
Cache-Control 相關設置:
no-store:禁止瀏覽器緩存響應
no-cache:不容許直接使用本地緩存,先發起請求和服務器協商
max-age=delta-seconds:告知瀏覽器該響應本地緩存有效的最長氣象,以秒爲單位
若是三個被同時設定,則優先級以下:
Pragma > Cache-Control > Expires
協商緩存:
當瀏覽器沒有命中本地緩存,如本地緩存過時或者響應中聲明不容許直接使用本地緩存,那麼瀏覽器確定會發起服務端請求。
服務端會驗證數據是否被修改,若是沒有則通知瀏覽器使用本地緩存。
相關Header:
If-None-Match:本地緩存失效,會攜帶此值去請求服務端,服務端判斷該資源是否改變,若是沒有改變,直接使用本地緩存,返回 304。例如:
If-None-Match:「78437822c-6739
」
關閉 Etag;也能夠去設置
add_header :
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
etag off;
add_header cache-control max-age=3600 ;
}
Nginx中配置expires【經常使用】
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d; //圖片緩存30天
}
location ~ .*\.(js|css)?$
{
expires 12h; //js 和 css 緩存 12小時
}
緩存策略的選擇:
1)適合緩存的內容:不變的圖像,如 Logo,圖標等;js,css 靜態文件;可下載的內容,媒體文件
2)建議使用協商緩存:HTML文件;常常替換的圖片;常常修改的 js,css 文件;
若是不想使用緩存,則能夠加入簽名:
index.css?簽名 或者:
index.簽名.js
3)不建議緩存的內容
PHP配置緩存策略:
$since = $_SERVER['HTTP_IF_MODIFIED_SINCE']; // 獲取頭
$lifetime = 3600; // 假設該文件 3600s 後過時
// 判斷該文件是否過時
if (strtotime($since) + $lifetime > time()) { // 未過時
// 通知瀏覽器使用本地緩存
header('HTTP/1.1 304 Not Modified');
exit;
}
header('Last-Modified:'. gmdate('D, d M Y H:i:s', time()). ' GMT');
echo time();
前端代碼和資源的壓縮:
優點:讓資源文件更小,加快文件在網絡中的傳輸,讓網頁更快的展示,下降帶寬和流量開銷。
壓縮方式:
(1)JS、CSS、圖片、HTML代碼的壓縮(能夠將裏面的空白符,變量,進行語法的合併);
(2)Gzip壓縮(在服務器開啓 Nginx 中的 Gzip壓縮)。Nginx配置:
gzip on|off; # 是否開啓 gzip
gzip_buffers 32 4K| 16 8K # 緩衝(在內存中緩衝幾塊?每塊多大)
gzip_types text/plain application/xml # 對那些類型的文件用壓縮 如:txt,xml,html,css
【CDN加速】
一、CDN:即內容分發網絡。CDN系統可以實時地根據網絡流量和各節點的鏈接、負載情況以及到用戶的距離和響應時間等綜合信息,將用戶的請求從新導向離用戶最近的服務器節點上。
二、使用CDN的優點:a.本地Cache加速,提升了企業站點的訪問速度; b.跨運營商的網絡加速;c.減小原站點WEB服務器負載等。
三、CDN的適用場景:大量靜態資源的加速分發,例如:CSS、JS、圖片和HTML;大文件下載;直播網站等(節省帶寬、流量)。
【動態語言靜態化】
靜態化的實現方式
(1)使用模板引擎,例如 Smarty
(2)利用ob系統的函數
ob_start(); 打開輸出控制緩衝;
ob_get_contents(); 返回輸出緩衝區內容;
ob_clean(); 清空輸出緩衝區;
ob_end_flush(); 沖刷出(送出)輸出緩衝區內容並關閉緩衝;
【動態語言的併發處理】
【關於進程和線程】
進程:正在運行中的程序。線程:進程中的一條執行路徑。
一個線程只能屬於一個進程,而一個進程能夠有多個線程,但至少有一個線程。
不只進程之間能夠併發執行,同一個進程的多個線程之間也可併發執行
關於進程、線程、協程:
進程是一個"執行中的程序";
進程的三態模型:
運行、
就緒、
堵塞。
進程的五態模型:
新建態、
終止態、
活躍就緒、
靜止就緒(掛起就緒)、
活躍堵塞、
靜止堵塞。
因爲用戶的併發請求,爲每個請求都建立一個進程顯然是行不通的,從系統資源開銷方面或者是響應用戶請求的效率方面來看,所以就須要線程。
線程:有時被稱爲輕量級進程(LWP:Light Weight Process),是程序執行流的最小單元;
線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程本身不擁有系統資源,只擁有一點在運行中必不可少的資源,但它可與同屬一個進程的其餘線程共享進程所擁有的所有資源。
一個線程能夠建立和撤銷另外一個線程,同一進程中多個線程之間能夠併發執行;
線程是程序中一個單一的順序控制流程,進程內一個相對獨立的,可調度的執行單元,是系統獨立調度和分派CPU的基本單位,指運行中的程序的調度單位。在單個程序中同時運行多個線程徹底不一樣的工做,稱爲
多線程;
每個程序都至少擁有一個線程,如果程序只有一個線程,那就是程序自己;
線程的狀態:
就緒狀態、
運行狀態、
阻塞狀態。
協程:是一種用戶態的輕量級線程,
協程的調度徹底由用戶控制(線程是由操做系統控制的)
線程與進程的區別:
-
線程是進程內的一個執行單元,進程內至少有一個線程,他們共擁享進程的地址空間,而進程有本身獨立的地址空間;
-
進程是資源分配和擁有的單位,同一個進程內的線程共享進程的資源
-
線程是處理器調度的基本單位,而進程不是;
-
兩者都可併發執行;
-
每一個獨立的線程有一個程序運行的入口,順序執行序列和程序的出口,可是線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制;
線程和協程的區別:
-
一個線程能夠多個協程,一個進程也能夠單獨擁有多個協程;
-
線程進程都是同步機制,而協程是異步;
-
協程能保留上一次調用時的狀態每次過程重入時,就想當於進入上一次調用的狀態;
什麼是多進程,什麼是多線程?
多進程:同一時間裏,同一個計算機系統中若是容許兩個或者兩個以上的進程處於運行狀態,這就是多進程;
多線程:把一個進程分紅不少片,每一片均可以是一個獨立的流程;與多進程的區別是隻會使用一個進程的資源,線程之間能夠通訊;
[舉例說明]
單進程單線程:一我的在一個桌子上吃飯
單進程多線程:多我的在同一個桌子上吃飯
多進程單線程:多我的每一個人在本身的桌子上吃飯
同步堵塞:
多進程:最先的服務器端程序都是經過多進程、多線程來解決併發IO的問題,一個請求建立一個進程,而後子進程進入循環同步堵塞地與客戶端鏈接進行交互,收發處理數據。
多線程:用多線程模式實現很是簡單,線程中能夠直接向某一個客戶端鏈接發送數據。
缺點:這種模式最大的問題是,進程/線程建立和銷燬的開銷很大,嚴重依賴進程的數量解決併發問題。
PHP代碼實例:
<?php
//PHP多進程和多線程的處理
//建立socket監聽
//建立5個子進程
for ($i = 0; $i < 5; $i++) {
//使用pcntl_fork()建立進程,會返回pid,若是pid==0,則表示主進程
if (pcntl_fork() == 0) {
//循環監聽
while (true) {
$conn = stream_socket_accept($socketserv);
//若是監聽失敗,則從新去監聽
if(!$conn){
continue;
}
//讀取流信息,讀取的大小 是9000
$request = fread($conn, 9000);
//寫入響應
$response = 'hello';
fwrite($conn, $response);
//關閉流
fclose($conn);
}
//建立完全部的子進程,而後退出
exit(0);
}
}
運行
php stream_socket.php,使用
ps -ef 查看進程,會看到多出了以下的5個進程:
異步非阻塞:
-
Epoll是Linux內核爲處理大批量句柄而做了改進的poll。 要使用epoll只須要這三個系統調用:epoll_create(2), epoll_ctl(2), epoll_wait(2)。它是在2.5.44內核中被引進的(epoll(4) is a new API introduced in Linux kernel 2.5.44),在2.6內核中獲得普遍應用。
-
如今各類高併發異步IO的服務器程序都是基於
epoll實現的。
-
IO複用異步非阻塞程序使用經典的
Reactor模型,Reactor顧名思義就是
反應堆的意思,它自己不處理任何數據收發。只是能夠監視一個socket句柄的事件變化。
Reactor
有4個核心的操做:
(C語言用函數指針實現,JS能夠用匿名函數,PHP能夠用匿名函數、對象方法數組、字符串函數名)
Reactor只是一個事件發生器,實際對socket句柄的操做,如
connect/accept、send/recv、close是在callback中完成的。
Reactor模型還能夠與多進程、多線程結合起來用,既實現異步非阻塞IO,又利用到多核。
目前流行的異步服務器程序都是這樣的方式:如
PHP併發編程實踐:
1.PHP的Swoole擴展:
-
PHP的異步、並行、高性能網絡通訊引擎,Swoole 使用純 C 語言編寫,
提供了 PHP 語言的異步多線程服務器,異步 TCP/UDP 網絡客戶端,異步 MySQL,異步 Redis,數據庫鏈接池,AsyncTask,消息隊列,毫秒定時器,異步文件讀寫,異步DNS查詢。
-
除了異步 IO 的支持以外,Swoole 爲 PHP 多進程的模式設計了多個併發數據結構和IPC通訊機制,能夠大大簡化多進程併發編程的工做。其中包括了
併發原子計數器,併發 HashTable,Channel,Lock,進程間通訊IPC等豐富的功能特性。
-
Swoole2.0 支持了相似 Go 語言的協程,能夠
使用徹底同步的代碼實現異步程序。PHP 代碼無需額外增長任何關鍵詞,底層自動進行協程調度,實現異步。
2.消息隊列
消息隊列的應用場景:
(1)註冊後,短信和郵件發送。
場景說明:用戶註冊後,須要發註冊郵件和註冊短信。
消息隊列方式:將註冊信息寫入數據庫成功後,將成功信息寫入隊列,此時直接返回成功給用戶,寫入隊列的時間很是短,能夠忽略不計,而後異步發送郵件和短信
(2)應用解耦。
場景說明:用戶下單後,訂單系統須要通知庫存系統。
用戶下單後,訂單系統完成持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功。
訂閱下單的消息,採用拉/推的方式,獲取下單信息,庫存系統根據下單信息,進行庫存操做。
(3)流量削鋒。
應用場景:秒殺活動,流量瞬間激增,服務器壓力大。
用戶發送請求,服務器接受後,先寫入消息隊列。假如消息隊列長度超過最大值,則直接報錯或提示用戶。
這樣作能夠控制請求量、緩解高流量。
(4)日誌處理。
應用場景:解決大量日誌的傳輸。
日誌採集程序將程序寫入消息隊列,而後經過日誌處理程序的訂閱消費日誌。
(5)消息通信。
應用場景:聊天室。
多個客戶端訂閱同一主題,進行消息發佈和接收。
常見消息隊列產品:
Kafka、ActiveMQ、ZeroMQ、RabbitMQ、Redis等
3.接口的併發請求:
curl_multi_init
【數據庫緩存層的優化】
【MySQL數據庫層的優化】
【Web服務器的負載均衡】
七層負載均衡的實現
(Nginx)
:
基於URL等應用層信息的負載均衡,通常使用Nginx來實現
Nginx的proxy是它一個很強大的功能,實現了7層負載均衡
功能強大、性能卓越、運行穩定
配置簡單靈活
可以自動剔除工做不正常的後端服務器
上傳文件使用異步模式
支持多種分配策略,能夠分配權重,分配方式靈活
1.Nginx負載均衡:
內置策略:IP Hash、加權輪詢
擴展策略:fair策略、通用hash、一致性hash
(1)加權輪詢策略
首先將請求都分給高權重的機器,直到該機器的權值降到了比其餘機器低,纔開始將請求分給下一個高權重的機器。
當全部後端機器都down掉時,Nginx會當即將全部機器的標誌位清成初始狀態,以免形成全部的機器都處在timeout的狀態
(2)IP Hash 策略
Nginx內置的另外一個負載均衡的策略,流程和輪詢很相似,只有其中的算法和具體的策略有些變化
IP Hash 算法是一種變相的輪詢算法
(3)fair策略
根據後端服務器的響應事件判斷負載狀況,從中選出負載最輕的機器進行分流
(4)通用Hash、一致性Hash策略
通用hash比較簡單,能夠以Nginx內置的變量爲key進行hash,一致性hash採用Nginx內置的一致性hash環,支持memcache
2.Nginx配置:
http{
upstream cluster{
server srv1;
server srv2;
server srv3;
}
server {
listen 80;
location /{
}
}
}
四層負載均衡的實現
(LVS/硬件設備)
:
LVS實現服務器集羣負載均衡有三種方式:
NAT、DR、ip-TUN。
硬件設備:經過報文中的目標地址和端口,再加上負載均衡設備設置的服務器選擇方式,決定最終選擇的內部服務器
【負載均衡】
MySQL分庫分表:垂直分表(列數太多)、水平分表(數據太多)
網站加速技術:Squid代理緩存技術、頁面靜態化緩存、Memcache、Sphinx搜索加速
Squid反向緩存(動靜分離):
靜態數據:靜態頁面、圖片、CSS文件、JS文件。
動態數據:從數據庫中取出來的數據
sphinx能夠結合MySQL、PostgreSQL作全文索引。
Sphinx單一索引最大可包含1億條記錄,在1千萬條記錄狀況下的查詢速度爲 0.x 秒(毫秒級)。
Sphinx建立索引的速度:建立100萬條記錄的索引只需3~4分鐘,建立1000萬條記錄的索引能夠在50分鐘內完成,而只包含最新10萬條記錄的增量索引重建一次只需幾十秒。
網站服務監控:Mrtg網站監控、Postfix郵件服務器、Shell郵件報警、Cacti網站監控、Cacti郵件報警
網站服務、流量監控:Apache web服務監控、mysql數據庫監控、磁盤空間監控
Lvs經常使用的三種負載均衡模式:
(1)Lvs nat模式 (網絡地址轉換技術)
(2)Lvs ip-tun模式 (IP隧道技術)
(3)Lvs dr模式 (直接路由技術)