網站優化

優化方案

定義大型網站php

  • pv值(page views)頁面瀏覽量:一個網站全部頁面在24小時內,被瀏覽的總的次數。達到千萬級別/百萬級別。
  • uv值(unique visitor)獨立訪客:一個網站在24小時內,有多少個用戶來訪問網站。達到10w左右。
  • 獨立IP值:有多少個獨立的IP地址來訪問網站。達到10w左右。

uv值約等於獨立IP值。考慮局域網,或者校內網,則獨立IP小於uv值。css

產生的問題html

  • 大併發(大訪問量):在同一個時間點,有多少個用戶來訪問網站。 --> web吞吐量
  • 大流量:用戶訪問網站時,須要的大的帶寬,10G。 --> 流量
  • 大存儲:網站的數據庫(表)數據流成海量趨勢,表到了1T。 --> 如何快速查詢數據庫

常規解決方案前端

併發對應方案:對網站的架構重整,使用分層的結構,同時使用負載均衡/讀寫分離+集羣mysql

負載均衡器(器,軟件/硬件):
硬件:BIG-IP,F5,Net-Scaler。硬件的特色:效率高,價格貴。
軟件:LVS(Linux virtual server)linux虛擬服務,NginxWeb服務器+反向代理linux

讀(select)寫(update,insert,delete)分離:
寫,通常把請求發送到master服務器上。
讀,根據實際狀況均衡到其它多個服務器上。(網站的程序80%都是讀操做)nginx

讀寫分離原理:
主服務器上操做完以後,會造成bin文件,操做的行爲會記錄在bin文件中。在從服務器中讀取該文件,取在主服務器的行爲,完成主從數據同步。(經過一個線程,不停的去讀取bin文件)
經過配置完成讀寫分離。git

集羣github

  1. 解決單點故障的恢復(冗餘技術)web

    備用`apache`,`nginx`(處於休眠狀態),經過心跳檢測來檢測`apache`,`nginx`(被檢測的服務器屬於激活狀態)是否宕機。(備用服務器+服務器稱之爲集羣)
  2. 無論一個集羣中有多少個服務器,可是在同一時間,只有一個服務器處於激活/工做狀態。

負載均衡的實現方案:

  • 輪詢技術(NAT實現)
  1. 靜態實現
  2. 動態實現
  3. 端口複用
  • 直接路由(DR)

大流量的解決方案

  • 網站的圖片在保證使用狀況下,儘量小。
  • 對數據壓縮再傳輸 例如:gzip,deflate
  • 把佔用流量大的資源,放到專門的服務器上.例如:圖片->圖片服務器,視頻->視頻服務器
  • 買帶寬

大存儲對應方案:

  • 使用緩存:經過緩存來儘可能減小或者不查詢數據庫。

    常見的有:頁面靜態化(磁盤緩存),把動態頁面轉換成靜態頁面。 內存緩存`redis,memcached,mysql數據庫的memory存儲引擎`。
  • 表的設計要知足3NF(3範式)
  • 建立適當的索引(主鍵索引,惟一索引,普通索引,全文索引,空間索引)
  • 建立適當存儲過程,視圖,函數,觸發器
  • 分表技術(水平分表和垂直分表):一個大表分紅幾個小表。從邏輯上分開。
  • 分區技術:從物理上把數據分配到不一樣的磁盤空間
  • 讀寫分離
  • 優化程序中的SQL語句
  • 優化apache的my.ini的配置,nginx的nginx.conf的配置,好比配置最大併發,調整一些緩存大小。
  • 硬件升級(使用64位機器,多個cpu)

頁面靜態化

頁面靜態化分爲:

  • 真靜態:把一個動態的頁面,轉成一個靜態的頁面。
  • 僞靜態:所謂僞靜態是從url地址上看是一個靜態頁面的,可是實際上仍是對應一個動態頁面。

    僞靜態的原理:

第一次訪問的時候去查詢數據庫,而且生成靜態頁面。
第二次訪問返回靜態頁面。

linxingzhang.com/index.html -> 請求靜態頁面 <-- 首頁對應的靜態頁面

僞靜態的局部配置:

RewriteRule ^index\.html$ index\.php [L]

概念

動態網址:
通常來講去查詢數據庫,linxingzhang.com/news.php?id=1
特色:

  • 查詢數據庫,速度慢
  • 接口參數,安全性注意(SQL注入)
  • 不利於SEO

只要有數據的接收,則要求對在客戶端使用正則驗證,同時當數據接收後,對數據再次經過正則驗證。

SQL注入

SQL注入:利用現有應用程序,攻擊者將精心構造的SQL語句注入到後臺數據庫,並使得數據庫引擎成功執行。

SQL注入特色:

  • 變種極多
  • 攻擊簡單
  • 危害極大

SQL注入攻擊的危害性

  • 未經受權狀況下操做數據庫中的數據
  • 惡意篡改網頁的內容
  • 私自添加系統帳號或者是數據庫使用者帳號
  • 網頁木馬

SQL注入工具:

  • 掃描檢測

    AWVS,web掃描器,APPScan Web掃描器等,抓包代理工具burpsuite
  • 驗證測試

    sqlmap

手工探測SQL漏洞:
經過拼接SQL語句來判斷和驗證漏洞

// 測試語句
id=1'
and 1=1
and 1=2
order by num
union 聯合查詢
and 1=2 union select 1,2,3,4
user()
database()
group_coucat(table_name) from information_schema.tables where table_schema=tableName // table名十六進制
select * from downloads where id=1 and 1=2 union select 1,2,3,4,5,6,group_concat(username) from members;

?id=1 and 1=2 union select 1,2,3,4,5,6,load_file('/etc/passwd')
?id=1 and 1=2 union select 1,2,3,4,5,6,@@version
?id=1 and 1=2 union select 1,2,3,4,5,6,@@version_compile_os
?id=1 and 1=2 union select 1,2,3,4,5,6,@@basedir
?id=1 and 1=2 union select 1,2,3,4,5,6,@@datadir

SQL防護:

  • 代碼層防護
  • 第三方安全程序及設備

代碼層防護

編碼階段:

  • 對輸入進行驗證
  • 靜態查詢
  • 最小權限
  • 通用防注入腳本
  • 安全函數(PHP程序:addslashes, mysql_real_escape_string等)

測試階段:

  • 代碼驗證

產品化階段:

  • Web應用安全網關

第三方安全程序

  • 軟件產品

    mod_security
    互聯網安全防禦產品(阿里雲盾,安全寶等同類產品)
  • 硬件

    web應用防火牆

SQL語句驗證

function CheckSql($db_string, $querytype = 'select') {
    global $cfg_cookie_encode;
    $clean = '';
    $error = '';
    $old_pos = 0;
    $pos = -1;
    $log_file = DEDEINC.'/../data/'.md5($cfg_cookie_encode).'_safe.txt';
    $userIP = GetIP();
    $getUrl = GetCurUrl();

    // 若是是普通查詢語句,直接過濾一些特殊語法
    if ($querytype=='select') {
        $notallow1 = "[^0-9a-z@\._-]{1,}(union|sleep|benchmark|load_file|outfile)[^0-9a-z@\.-]{1,}";

        // $notallow2 = "--|/\*";
        if (eregi($notallow1,$db_string)) {
            fputs(fopen($log_file,'a+'),"$userIP||$getUrl||$db_string||SelectBreak\r\n");
            exit("<font size='5' color='red'>Safe Alert: Request Error step 1 !</font>");
        }
    }

    // 完整的SQL檢查
    while (true) {
        $pos = strpos($db_string, '\'', $pos + 1);
        if ($pos === false) {
            break;
        }
        $clean .= substr($db_string, $old_pos, $pos - $old_pos);
        while (true) {
            $pos1 = strpos($db_string, '\'', $pos + 1);
            $pos2 = strpos($db_string, '\\', $pos + 1);
            if ($pos1 === false) {
                break;
            } elseif ($pos2 == false || $pos2 > $pos1) {
                $pos = $pos1;
                break;
            }
            $pos = $pos2 + 1;
        }
        $clean .= '$s$';
        $old_pos = $pos + 1;
    }
    $clean .= substr($db_string, $old_pos);
    $clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));

    // 老版本的Mysql並不支持union,經常使用的程序裏也不使用union,可是一些黑客使用它,因此檢查它
    if (strpos($clean, 'union') !== false && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0) {
        $fail = true;
        $error="union detect";
    }

    // 發佈版本的程序可能比較少包括--,#這樣的註釋,可是黑客常用它們
    elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, '#') !== false) {
        $fail = true;
        $error="comment detect";
    }

    // 這些函數不會被使用,可是黑客會用它來操做文件,down掉數據庫
    elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[a-z])~s', $clean) != 0) {
        $fail = true;
        $error="slown down detect";
    }
    elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0) {
        $fail = true;
        $error="slown down detect";
    } elseif (strpos($clean, 'load_file') !== false && preg_match('~(^|[^a-z])load_file($|[^[a-z])~s', $clean) != 0) {
        $fail = true;
        $error="file fun detect";
    } elseif (strpos($clean, 'into outfile') !== false && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~s', $clean) != 0) {
        $fail = true;
        $error="file fun detect";
    }

    // 老版本的MYSQL不支持子查詢,程序裏可能也用得少,可是黑客可使用它來查詢數據庫敏感信息
    elseif (preg_match('~\([^)]*?select~s', $clean) != 0) {
        $fail = true;
        $error="sub select detect";
    }
}

靜態網址:
linxingzhang.com/a.html靜態的網址

特色:

  • 通常不查詢數據庫,速度快
  • 不接收參數,安全性比較高
  • 利於SEO

僞靜態網址:
從形式上看是一個靜態的頁面,可是實際上對應一個動態的頁面。
特色:

  • 自己須要查詢數據庫,速度慢
  • 不接收參數,安全比較高
  • 利於SEO

局部動態的方式:

  1. 直接嵌入JS

    <script scr="xx.php"></script>
  2. ajax
  3. 若是動態的數據是由於用戶某個操做引起的,則可使用事件+ajax來處理

如何實現頁面靜態化

實現頁面靜態化(真靜態),有兩種方法:

  • 使用PHP的OB機制
  • 使用模板替換技術(正則)

OB緩存機制

OB就是output_buffering,輸出緩存,在請求一個PHP的過程當中,實際上過三個緩存:1.程序緩存,2.OB緩存,3:瀏覽器緩存

<?php
echo 1;
header('Content-Type: text/html; charset=utf-8');
echo 2; // 報警告

<?php
ob_start(); // 開啓
echo 1;
header('Content-Type: text/html; charset=utf-8');
echo 2; // 正常輸出 // OB緩存打開以後,輸出的語句,不會放到程序緩存中,而是放到OB緩存中,當程序執行完畢以後,OB緩存的東西強制放到程序緩存後邊,而後再二次加工成ht響應報,返回。

程序緩存,該緩存是PHP固有的,不能關閉。
每行代碼,每一個函數在程序緩存中,處理結果以後,拼接在一塊兒經過HTTP響應返回給瀏覽器。

開啓OB緩存:

  1. php.ini中開啓:output_buffering = 4096 4096 緩存大小。做用於全部的php後綴文件.
  2. 調用PHP函數ob_start(),只能做用於該頁面

若是沒有OB緩存,全部的緩存都放在程序緩存中。
header信息無論你是否開啓ob,老是放入到程序緩存中。

ob_get_contents(); // 獲取OB中的內容
ob_clean(); // 清除ob緩存,但不關閉ob緩存
ob_end_clean(); // 清除ob緩存,同時關閉ob緩存
ob_end_flush(); // 強制把ob緩存刷新到程序緩存,並關閉ob緩存
ob_flush(); // 強制把ob緩存刷新到程序緩存
flush(); //  把程序緩存刷新到瀏覽器緩存中

ob緩存細節

ob緩存究竟能夠存放什麼樣的數據?
靜態數據:html,css,js,動態語言輸出的結果.

ob放入的數據,從ob_start()開始到ob_get_contents()之間的返回給瀏覽器的靜態頁面

併發測試工具

經常使用工具

ab.exe,winrunner,loadrunner

ab.exe基本用法:
切換到apache-bin目錄底下,而後運行ab.exe命令

// ab.exe -n 總的請求次數 -c 併發量 請求頁面地址
ab.exe -n 10000 -c 100 http://127.0.0.1 // 100我的完成10000次

測試結果:

> ab.exe -n 10000 -c 100 http://www.ting.com/index.html

This is ApacheBench, Version 2.3 <$Revision: 1554214 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.ting.com (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests


Server Software:        Apache/2.4.9
Server Hostname:        www.ting.com
Server Port:            80

Document Path:          /index.html
Document Length:        6847 bytes

Concurrency Level:      100 // 併發數
Time taken for tests:   9.118 seconds // 完成的時間,值越小,服務器越好,越強悍。
Complete requests:      10000
Failed requests:        0
Total transferred:      71060000 bytes
HTML transferred:       68470000 bytes
Requests per second:    1096.69 [#/sec] (mean)  // 一秒鐘完成多少次請求
Time per request:       91.184 [ms] (mean) // 100我的併發一次花費的時間
Time per request:       0.912 [ms] (mean, across all concurrent requests) // 一我的併發花費的時間
Transfer rate:          7610.41 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   1.2      0      11
Processing:    26   90  23.8     84     424
Waiting:       26   76  24.4     72     410
Total:         27   90  23.9     85     425

Percentage of the requests served within a certain time (ms)
  50%     85
  66%     91
  75%     96
  80%    101
  90%    109
  95%    117
  98%    128
  99%    188
 100%    425 (longest request)
Finished 10000 requests

調整apache的最大併發數

在默認狀況下,apache的最大併發數爲150
配置文件:httpd-mpm.conf

MPM(多路處理模塊),就是apache處理併發的方式:

  • perfork 預派生模式:預先建立配置的進程,等待瀏覽器的鏈接
  • worker 工做者模式:有必定的進程,開了一些配置的線程,等待瀏覽器的鏈接(優於perfork模式,線程消耗更小)
  • winnt模式 (windows下默認的模式):父進程-->子進程-->線程,等待瀏覽器的鏈接

肯定mpm的指令

> httpd.exe -l
Compiled in modules:
  core.c
  mod_win32.c
  mpm_winnt.c // MPM模式
  http_core.c
  mod_so.c

須要在httpd.conf文件的打開mpm從配置文件(Include conf/extra/httpd-mpm.conf),而後在httpd-mpm.conf文件中修改最大配置數

配置最大併發數,須要在合適數值之間,須要考慮服務器自己的性能問題

SEO

  • url地址255字節
  • 不要給靜態頁面帶參數,不然spider不抓取
  • 在前端儘可能不使用frame,iframe
  • 圖片alt信息須要添加上
  • 關鍵字,描述

移動端首屏優化

DNS預解析

DNS預解析與業務相關,

DNS缺點:

  • 比較耗時
  • 每一個連接都須要從新創建鏈路
<meta htt-equiv="x-dns-prefetch-control" content="on" />
<link rel="dns-prefetch" href="" />

域名收斂

域名收斂:將靜態資源只放在一個域名下面,而非發散狀況下的多個域名下
做用:減小DNS解析的開銷

一個頁面所產生的域名解析數不能超過5個

performance對象

performance對象做用:查看網頁性能數據

// 獲取 performance 數據
var performance = {
    // memory 是非標準屬性,只在 Chrome 有
    // 財富問題:我有多少內存
    memory: {
        usedJSHeapSize:  16100000, // JS 對象(包括V8引擎內部對象)佔用的內存,必定小於 totalJSHeapSize
        totalJSHeapSize: 35100000, // 可以使用的內存
        jsHeapSizeLimit: 793000000 // 內存大小限制
    },

    //  哲學問題:我從哪裏來?
    navigation: {
        redirectCount: 0, // 若是有重定向的話,頁面經過幾回重定向跳轉而來
        type: 0           // 0   即 TYPE_NAVIGATENEXT 正常進入的頁面(非刷新、非重定向等)
                          // 1   即 TYPE_RELOAD       經過 window.location.reload() 刷新的頁面
                          // 2   即 TYPE_BACK_FORWARD 經過瀏覽器的前進後退按鈕進入的頁面(歷史記錄)
                          // 255 即 TYPE_UNDEFINED    非以上方式進入的頁面
    },

    timing: {
        // 在同一個瀏覽器上下文中,前一個網頁(與當前頁面不必定同域)unload 的時間戳,若是無前一個網頁 unload ,則與 fetchStart 值相等
        navigationStart: 1441112691935,

        // 前一個網頁(與當前頁面同域)unload 的時間戳,若是無前一個網頁 unload 或者前一個網頁與當前頁面不一樣域,則值爲 0
        unloadEventStart: 0,

        // 和 unloadEventStart 相對應,返回前一個網頁 unload 事件綁定的回調函數執行完畢的時間戳
        unloadEventEnd: 0,

        // 第一個 HTTP 重定向發生時的時間。有跳轉且是同域名內的重定向纔算,不然值爲 0 
        redirectStart: 0,

        // 最後一個 HTTP 重定向完成時的時間。有跳轉且是同域名內部的重定向纔算,不然值爲 0 
        redirectEnd: 0,

        // 瀏覽器準備好使用 HTTP 請求抓取文檔的時間,這發生在檢查本地緩存以前
        fetchStart: 1441112692155,

        // DNS 域名查詢開始的時間,若是使用了本地緩存(即無 DNS 查詢)或持久鏈接,則與 fetchStart 值相等
        domainLookupStart: 1441112692155,

        // DNS 域名查詢完成的時間,若是使用了本地緩存(即無 DNS 查詢)或持久鏈接,則與 fetchStart 值相等
        domainLookupEnd: 1441112692155,

        // HTTP(TCP) 開始創建鏈接的時間,若是是持久鏈接,則與 fetchStart 值相等
        // 注意若是在傳輸層發生了錯誤且從新創建鏈接,則這裏顯示的是新創建的鏈接開始的時間
        connectStart: 1441112692155,

        // HTTP(TCP) 完成創建鏈接的時間(完成握手),若是是持久鏈接,則與 fetchStart 值相等
        // 注意若是在傳輸層發生了錯誤且從新創建鏈接,則這裏顯示的是新創建的鏈接完成的時間
        // 注意這裏握手結束,包括安全鏈接創建完成、SOCKS 受權經過
        connectEnd: 1441112692155,

        // HTTPS 鏈接開始的時間,若是不是安全鏈接,則值爲 0
        secureConnectionStart: 0,

        // HTTP 請求讀取真實文檔開始的時間(完成創建鏈接),包括從本地讀取緩存
        // 鏈接錯誤重連時,這裏顯示的也是新創建鏈接的時間
        requestStart: 1441112692158,

        // HTTP 開始接收響應的時間(獲取到第一個字節),包括從本地讀取緩存
        responseStart: 1441112692686,

        // HTTP 響應所有接收完成的時間(獲取到最後一個字節),包括從本地讀取緩存
        responseEnd: 1441112692687,

        // 開始解析渲染 DOM 樹的時間,此時 Document.readyState 變爲 loading,並將拋出 readystatechange 相關事件
        domLoading: 1441112692690,

        // 完成解析 DOM 樹的時間,Document.readyState 變爲 interactive,並將拋出 readystatechange 相關事件
        // 注意只是 DOM 樹解析完成,這時候並無開始加載網頁內的資源
        domInteractive: 1441112693093,

        // DOM 解析完成後,網頁內資源加載開始的時間
        // 在 DOMContentLoaded 事件拋出前發生
        domContentLoadedEventStart: 1441112693093,

        // DOM 解析完成後,網頁內資源加載完成的時間(如 JS 腳本加載執行完畢)
        domContentLoadedEventEnd: 1441112693101,

        // DOM 樹解析完成,且資源也準備就緒的時間,Document.readyState 變爲 complete,並將拋出 readystatechange 相關事件
        domComplete: 1441112693214,

        // load 事件發送給文檔,也即 load 回調函數開始執行的時間
        // 注意若是沒有綁定 load 事件,值爲 0
        loadEventStart: 1441112693214,

        // load 事件的回調函數執行完畢的時間
        loadEventEnd: 1441112693215
    }
};

鏈路複用

  • TCP3次握手
  • keep-alive(鏈路複用)提高的部分
  • Server內存消耗,預選擴容

資源內聯

在移動APP上面,在特定狀況下會將CSS樣式放在.html

組件化開發

  • 按需/異步加載
  • 異步渲染
  • 結合服務端渲染

服務端渲染

  • Ajax模式下,數據與頁面資源串行
  • 網絡路徑的增加,提高了網絡異常的影響
  • 使用Node.js進行服務端渲染
  • 組件先後端同構
相關文章
相關標籤/搜索