高併發優化思路筆記

一,web資源防盜鏈

通過Referer 或者簽名,檢測目標來源。

1,nginx模塊ngx_http_referer_module用於阻擋來源非法的域名請求

例:

location ~.*\.(gif|jpg|png|flv|swf|rar|zip)$ {
    valid_referers none blocked xxx.com *.xxx.com;
    if ($invalid_referer) {
        rewrite ^/http://www.xxx.com/403.jpg;
    }
}

 此方法是可以被僞造http頭信息referer破解。

2,使用加密簽名

第三方模塊HttpAccessKeyModule實現nginx防盜鏈

location ~.*\.(gif|jpg|png|flv|swf|rar|zip)$ {
    accesskey on;
    accesskey_hashmethod md5;
    accesskey_arg "key";  #GET參數名
    accesskey_signature "mypass$remote_addr";  #加密規則
}

 

<?php

// md5 (mypass.ip)
$key = md5('mypass'.$_SERVER['REMOTE_ADDR']);

echo '<img src="xxx.jpg?key=$key">';

二,減少頁面大小,啓用nginx gzip壓縮

gzip on;
gzip_min_length 1k; #小於1k不進行壓縮
gzip_buffers 4 16k;
gzip_comp_level 6;
gzip_types text/plain application/javascript text/css application/xml;

重複內容越多,壓縮效率越好!

三,減少資源請求數量,合併壓縮靜態文件。

1,圖片地圖 合併圖片

將n個圖片合併爲一張圖片,以位置信息定位超鏈接。

<map><area></area></map>

2,css sprite 合併圖片

調整background-position。

3,圖片使用Base64編碼減少頁面請求數。

<img src="data:image/gif;base64,/xx/xxxxxxxxxxxx.....">

4,Minify 把CSS和JS壓縮和削減,以及把多個CSS,JS文件合併到一個文件裏(減少資源請求數量)。https://tool.css-js.com/;圖片壓縮https://tinypng.com/

四,設置瀏覽器緩存。

1,nginx配置

location ~.*\.(js|css|jpg|jpeg|gif|png)$ {
    expires 7d; #瀏覽器過期時間
}

2,HTTP緩存

200from cache(本地緩存),304Not Modified(服務器裏取緩存,協商緩存),200OK(沒用到緩存)。

本地緩存:設置http頭信息緩存 ,緩存的優先級 Pragma > Cache-Control > Expires ;

協商緩存:header Last-Modified  If-Modified-Since提交到服務器做檢查,如果沒有修改,返回304。E-tag通過"指紋"判斷

五,CDN加速

六,圖片服務器

NFS 或者 FTP 同步圖片(多臺圖片服務器)

七,動態語言靜態化及代碼邏輯優化(秒殺爲例)

1,實時性不高的頁面(ob_系列方法)如index.php

2,快速終止的邏輯放在前面(跳出循環亦是如此),把用不到數據庫緩存的代碼往前放。

3,秒殺接口:如5W庫存產品,1億次請求,放50W請求進來,後續的請求就請求靜態化後的接口(結合CDN)。

#原理nginx處理靜態文件要比動態文件效率高太多
#可以擋住大部分流量
#驗證活動和商品的狀態
#這個接口對應着 /astatus/{aid}_{gid}.js 靜態文件的動態實現(也方便SEO)
#例如: /astatus/1_1.js
#不能秒殺的時候,靜態文件纔會存在
#活動開始前,靜態文件存在
#互動進行中,會統一把靜態文件刪除,則nginx的rewrite失效,進入到這個動態文件
#預估的搶完庫存的流量滿後也可以生成靜態文件,防止後面流量進來導致負載過大

#nginx配置(文件不存在的時候走rewrite到動態文件)
if (!-e $request_filename) {
    rewrite ^([^\.]*)/astatus/([0-9]+)_([0-9]+).js$ $1/astatus.php?aid=$2&gid=$3 last;
}

#例如: /astatus/1_2.js
#文件如果存在,則nginx直接返回靜態文件的內容
#如果不存在,則把參數賦值給動態接口 /astatus.php?aid=1&gid=2

 

4,增加冗餘的數據結構。比如 活動-商品 存入redis  減輕mysql壓力

/**
  * astatus.php
  * 秒殺接口
  */
<?php
    $redis = \Predis::getRedis('instance1');
    $data = $redis->mget(array(
        'status_a_' . $aid,
        'status_g_' . $gid,
    ));
    if($data && $data[0] && $data[1]) {
        // 活動狀態和商品狀態都是正常狀態,纔可以返回一個正確的驗證碼(返回驗證碼而不是直接返回成功狀態爲了防止機器拿到返回結果直接刷下一個邏輯,下一個邏輯必須要正確的驗證碼纔可以繼續)
        $info = array(
            'now' => time(),
            'ip'  => getClientIp(),
            'user_id' => $login_userinfo['uid'],
        );
        $str = signQuestion($info);
        echo json_encode(array('user_sign' => $str));
    }else {
        $result = array('code' => '-1', 'msg' => '活動商品已下架或者已售完', 'data' => array());
        echo json_encode($result);
    }
    
<?php
    //非法用戶不能繞過astatus
    $status_check = false;
    $str_sign_data = unsignQuestion($sign_data);
    $sign_data_info = json_decode($str_sign_data, true);
    // 時間不能超過當前時間2分鐘,IP和用戶保持不變
    if ($sign_data_info
        && $sign_data_info['now'] < $now
        && $sign_data_info['now'] > $now - 120
        && $sign_data_info['ip'] == $client_ip
        && $sign_data_info['uid'] == $uid
    ) {
        $status_check = true;
    }

    if (!$status_check) {
        $result = array('code' => '-1', 'msg' => '用戶校驗值驗證沒有通過', 'data' => array());
        show_result($result);
    }

扣減庫存操作要用表達式,redis用原子操作,防止高併發時候出現髒數據。

栗子:

<?php
    
    $left = changeLeftNumCached($goods_id, 0-$goods_num);
    $res = false;
    if ($left >= 0) {
        $res = changeLeftNum($goods_id, 0-$goods_num);
    } else {
        // 扣除商品庫存失敗
        //改變商品狀態,生成'astuatus/'.$active_id .'_'.$goods_id.'.js'靜態文件,下次客戶從源頭就進不來(此商品nginx不會訪問動態程序),減輕服務器壓力
    }

    function changeLeftNumCached($id, $num) {
        $key = 'info_g_' . $id;
        $redis = \Preds::getRedis('instance1');
        $left = $redis->hincrby($key, 'num_left', $num);
        return $left; //這裏返回的是操作後的庫存
    }

    function changeLeftNum($id, $num) {
        $params = array('id' => $id);
        $sql = "UPDATE " . $this->table . " SET num_left=num_left" . ($num > 0 ? '+' : '') . "$num WHERE id=:id";
        return $this->getDb()->query($sql, $params);
    }

5,防機器人,防黃牛。

各邏輯之間要有所關聯,不容易被機器人破解;

問答題(接口交互要加密,提交驗證數據可以把問題和對應的答案也傳過去,這樣驗證的時候可以不需要查數據庫);

點觸式驗證(持續時間長的活動),但是性能差;

6,事後用戶行爲分析

用戶訪問頁面路徑;

頁面停留時間,點擊位置和時間;

用戶基本信息,用戶ip,瀏覽信息等等;

7,減少數據庫依賴

數據庫儘量只處理,插入更新,數據庫依賴降到最低。

8,減少數據規模

訂單表,創建一次活動,創建一張新的訂單表,活動結束後 彙總 原先訂單表。

八,PHP併發編程

1,swoole 2,消息隊列 3,curl_multi_add_handle等

九,數據庫緩存層的優化

redis,memcached。

十,數據庫層的優化

1,數據類型優化

2,索引的優化

3,SQL優化

3,表結構設計

4,服務器架構優化

十一,分佈式

依賴session不一致問題;統一放在一臺機子memcache裏面

依賴本地文件不一致問題;放到共享磁盤裏面

擴展性要好,隨時可以增減服務器

多個機房,每個機房部署一個集羣,每個集羣一個LVS;智能DNS爲不同網絡不同地域的用戶解析到不同LVS,部分接口引入CDN

=============================================================================================

多Web單數據中心

優點:數據好管理,操作方便。

缺點:數據處理成爲瓶頸,跨機房網絡問題。

多Web多數據中心

優點:每一組服務器都有數據中心,局域網完成。

缺點:數據同步,數據一致性。(引入中控服務器來解決,如秒殺庫存,可以平衡庫存,散列分配;5萬庫存放入10個集羣,每個集羣5千個庫存,每個羣體只負責賣自己的,互不相關,如果出現不同羣體同一個用戶由於網絡變更導致重複下單,活動結束後,彙總10臺集羣訂單,用戶排重找到重複下單用戶,然後聯繫用戶溝通解決這個問題,業務層面解決,技術層面放過;中控服務器分配策略:1,直接平均分發下去,然後事實監控各個集羣庫存,調度庫存多的去庫存少的集羣;2,先分配一部分庫存下去給各個集羣,預留一部分庫存用動態調度,可防止程序BUG導致超賣)

===============================================================================================

大規模時候使用LVS

http://www.cnblogs.com/liwei0526vip/p/6370103.html

智能DNS 可以解析域名 跳入哪個LVS羣。

==============================================================================================

服務器訪問量規模預估

數據量,訪問量(秒殺時候用戶體驗要好數據更新要及時,不然訪問量會加倍增加,用戶不停的刷,這時候就需要在訪問第一次的沒庫存時候在用戶瀏覽器植入一個cookie,判斷有這個cookie就返回沒庫存,根本就不需要發起請求了,防止用戶不停刷瀏覽器),資源量。

==========================================================================================

中控服務器 平衡調度 庫存。每個中控服務器都有一個接口和發起接口請求的程序,根據策略自動平衡庫存。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章