1. 程式人生 > 實用技巧 >javascript中DOM獲取和設定元素的內容、樣式及效果

javascript中DOM獲取和設定元素的內容、樣式及效果

Nginx

學習視訊:

https://www.bilibili.com/video/BV1ug4y1z7fQ?from=search&seid=2127281453570494480

簡介

訊號控制

訊號量USR1的使用

nginx配置

基本配置

server

location

rewrite

gzip壓縮

原理

優點

注意

常用引數

nginx快取配置

nginx反向代理

nginx負載均衡

負載均衡演算法 一致性雜湊

日誌管理

server log

日誌定時切割

高效能伺服器架設

apache壓力測試

安裝

apache 測試工具使用

nginx統計工具

優化思路

遇到的問題

記一次上線

nginx響應超時

nginx請求時間過長

簡介

Nginx("engine x")是一個高效能的 HTTP 和 反向代理 伺服器,也是一個IMAP/POP3/SMTP 代理伺服器。Nginx是Igor Sysoev為俄羅斯訪問量第二的Rambler.ru站點開發的。國內使用者有:新浪、趕集...

訊號控制

TERM, INT

fast shutdown

QUIT

graceful shutdown 優雅的關閉程序,即等請求執行結束後在關閉

HUP

changing configuration, keeping up with a changed time zone (only for FreeBSD and Linux), starting new worker processes with a new configuration, graceful shutdown of old worker processes 改變配置檔案,平滑的重新讀取配置檔案。使用新配置檔案啟動時,他會慢慢的使用新配置檔案接收的nginx接收請求,然後將舊的nginx關閉。

USR1

re-opening log files 重讀寫日誌,在日誌按月/日分割時有用。由於linux寫檔案真正指向的是磁碟上的那個inode節點,和檔名稱無關,所以你要想讓nginx重新將日誌寫到一個新檔案裡,光靠改檔名和替換檔名是沒用的,因為nginx是一直往那個inode節點裡寫,和檔案叫啥名沒關係。這個時候你要想去備份日誌檔案就需要使用這個訊號量了。

USR2

upgrading an executable file 平滑的升級nginx

WINCH

graceful shutdown of worker processes 優雅的關閉舊的程序(配合USER2使用)

sudo kill -INT nginx程序號
sudo kill -HUP nginx程序號

# 如果不想總是使用pid去控制,我們還可以使用pid檔案去控制(本質上還是通過pid檔案裡的程序號控制)
sudo kill -HUP `cat /usr/local/nginx/nginx.pid` # 這樣只要記住程序id檔案地址就行
sudo kill -usr1 `cat /usr/local/nginx/nginx.pid`
sudo kill -quit `cat /usr/local/nginx/nginx.pid`

# 除了使用訊號量,還可以使用nginx提供的一些命令去操作,但沒有使用訊號量豐富
/usr/local/nginx/nginx -s reload
/usr/local/nginx/nginx -s start
/usr/local/nginx/nginx -s stop
/usr/local/nginx/nginx -s quit 
/usr/local/nginx/nginx -s reopen  # 相當於USER1

訊號量USR1的使用

1、修改nginx.conf部分如下:

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  a.html index.html index.htm;
        }
}

2、在nginx根目錄的html目錄下新建a.html如下:

<html>
hello! welcome nginx!

<script>
window.location.href="/?time="+ new Date().getTime()
</script>

</html>

3、重啟nginx,使用瀏覽器請求nginx,檢視./logs/目錄下的日誌大小,會發現hosts.access.log越來越大,此時我們mv hosts.access.log並不會改變inode。

bash-4.2$ ll
total 24
-rw-r--r--. 1 root   root  8269 Apr 29 14:05 access.log
-rw-r--r--. 1 nobody root 10087 Apr 29 14:15 error.log
-rw-r--r--  1 root   root     0 Apr 29 14:30 host.access.log

-bash-4.2$ ll
total 472
-rw-r--r--. 1 root   root   8269 Apr 29 14:05 access.log
-rw-r--r--. 1 nobody root  10087 Apr 29 14:15 error.log
-rw-r--r--  1 root   root 214162 Apr 29 14:39 host.access.log
-bash-4.2$

-bash-4.2$ mv host.access.log ./host.access.bak.log

-bash-4.2$ ll  # inode節點並沒有變,所以依舊往那個節點裡寫日誌
total 472
-rw-r--r--. 1 root   root   8269 Apr 29 14:05 access.log
-rw-r--r--. 1 nobody root  10344 Apr 29 14:59 error.log
-rw-r--r--  1 root   root 346912 Apr 29 15:00 host.access.bak.log

-bash-4.2$ sudo kill -usr1 `cat /usr/local/nginx/nginx.pid`

-bash-4.2$ ll  # 執行reopen後,重新將日誌輸出到host.access.log,改變了inode
total 1276
-rw-r--r--. 1 root   root   8269 Apr 29 14:05 access.log
-rw-r--r--. 1 nobody root  10405 Apr 29 15:01 error.log
-rw-r--r--  1 root   root 492007 Apr 29 15:01 host.access.bak.log
-rw-r--r--  1 nobody root 329970 Apr 29 15:03 host.access.log

-bash-4.2$ ll  # host.access.bak.log大小不在改變,但host.access.log在不停的增加,自此,就完成了日誌的備份
total 1276
-rw-r--r--. 1 root   root   8269 Apr 29 14:05 access.log
-rw-r--r--. 1 nobody root  10405 Apr 29 15:01 error.log
-rw-r--r--  1 root   root 492007 Apr 29 15:01 host.access.bak.log
-rw-r--r--  1 nobody root 397212 Apr 29 15:04 host.access.log

nginx配置

基本配置

work_process 1; # 有1個工作的子程序,可以自行修改,但太大無益,因為要爭奪CPU,一般設定為CPU數*核數

event {
    # 一般配置nginx連線的特性
    # 如一個worker能同時允許產生多少連線
    worker_connections 1024; # 這是指 一個子程序最大允許連線1024個連線(這個引數要和系統配置配合才能有更好的效果)
}

http { # 這是配置http伺服器的主要段

    # 可以配置多個server

    # 基於域名的虛擬主機
    server { 
        listen 80; # 你要監聽哪一個埠
        server_name z.com; # 域名
        
        location / {
            root html; # 你專案的根目錄在哪裡?可以是相對路徑(相對/usr/local/nginx,也就是nginx的安裝目錄),也可以是絕對路徑
            index index.html; # 如果別人請求沒有寫明具體檔名時,預設給他哪個訪問頁面,這裡設定給它根目錄下的index.html頁面
        }
    }
    
     # 基於埠的虛擬主機
    server { 
        listen 20200; # 你要監聽哪一個埠
        server_name z.com; # 域名
        
        location / {
            root html; # 你專案的根目錄在哪裡?可以是相對路徑(相對/usr/local/nginx,也就是nginx的安裝目錄),也可以是絕對路徑
            index index.html; # 如果別人請求沒有寫明具體檔名時,只寫了請求根目錄,預設將請求轉發到/index.html,這裡設定的是根目錄下的index.html頁面
        }
    }
    
    # 基於ip的虛擬主機
    server { # 這是虛擬主機段
        listen 20200; # 你要監聽哪一個埠
        server_name 10.1.192.130; # 你要監聽哪一個域名
        
        location / {
            root html; # 你專案的根目錄在哪裡?可以是相對路徑(相對/usr/local/nginx,也就是nginx的安裝目錄),也可以是絕對路徑
            index index.html;  #如果別人請求沒有寫明具體檔名時,只寫了請求根目錄,預設將請求轉發到/index.html,這裡設定的是根目錄下的index.html頁面
        }
    }
    
}

server

syntax句法格式,沒有預設值,server的上下文環境是用在http內。

基於域名的虛擬主機,修改nginx的server_name之後,重新軟起動nginx,並且修改hosts檔案,否則是訪問不到nginx的,因為這個z.com域名並不屬於本機,所以當本機發起z.com請求的時候會先去hosts檔案裡找,找不到這個域名就回去網際網路上去解析去找,所以我們需要在本機配置hosts

sudo vim /etc/hosts
# 加入一行
10.1.192.130 z.com

# 再次訪問nginx
curl z.om # 即可看到nginx首頁資訊了

當我只修改ip的時候,訪問curl localhost依舊會得到nginx首頁資訊,localhost就相當於本機,而埠為80,所以就直接到80埠,nginx開放的埠也恰好為80,所以就獲取到了首頁。

基於埠的虛擬主機,我們依舊設定的是原來的z.com的域名,但是埠號改成20200,當再次訪問:

-bash-4.2$ curl z.com
curl: (7) Failed connect to z.com:80; Connection refused

-bash-4.2$ curl z.com:20200 # 再次出現nginx首頁資訊

基於ip的虛擬主機,如果我請求只寫ip不寫埠20200的話,nginx找不到埠,就會去找預設的80埠,所以會將請求傳送到第一個基於域名的虛擬主機,而不是傳送到第三個,所以我們基於ip的虛擬主機需要在nginx中指明ip,請求時指明埠。

location

如上圖官方說明,syntax表示location的句法,沒有預設值,context表示location的上下文可以在server內配置,也可以在location內配置(巢狀)。

location有”定位“的意思,根據Uri來進行不同的定位。在虛擬主機的配置中,是必不可少的,location可以把網站的不同部分,定位到不同的處理方式上。比如,碰到.php,如何呼叫PHP直譯器? -- 這時就需要location。

語法

location [=|~|~*|^~] pattern {
    
}
# 中括號可以不寫任何引數,此時稱為一般匹配,也可以寫引數
# 因此,大型別可以分為3種
location = pattern {} [精準匹配]
location pattern{} [一般匹配]
location ~ pattern {} [正則匹配]

如何發揮作用?

首先看有沒有精準匹配,如果有,則停止匹配過程。

一般匹配patt,先理解成”字串“,在理解成”正則表示式“,儘量的多匹配URI,如果有多個一般匹配,誰匹配上的長度大,以誰為準。

精準匹配和一般匹配對比

# 案例1:

location =/ {
    root /var/www/html;
    index index.htm index.html;
}


location / {
    root   html;
    index  index.html index.htm;
}
# 當我們在瀏覽器只輸入一個ip去訪問nginx的時候,我們最終應該是訪問一個目錄或檔案才是,
# 而不應該是個ip,所以當我們不寫具體檔案而只寫10.1.192.130這個請求的時候,nginx
# 下的location裡的index屬性就發揮了作用,它將會把這個請求理解成
# 10.1.192.130/index.htm,它就內部把請求轉發到10.1.192.130/index.htm上了,
# 這就相當於我們在瀏覽器位址列訪問了10.1.192.130/index.htm的效果一樣。

# 整個流程下來先走精準匹配,精準匹配命中,匹配到了/,但/只是一個目錄,它無法直接給你
# 迴應,它最終要給你引導到索引頁上,因此它內部給你轉到/index.htm,此時請求就變成了10.1.192.130/index.htm,
# 再次匹配,精準匹配沒命中,而一般匹配命中,此時訪問的已不在是一個目錄,而是一個
# 檔案,所以他就會直接在root指定了目錄下找這個index.htm檔案返回給你。

# 案例2:

location /index.html {
    root   html;
    index  index.html index.htm;
}

location =/index.html {
    root /var/www/html;
    index index.htm index.html;
}

# 此時我們在位址列中輸入10.1.192.130/index.html,按照推理,nginx應該返回給我們的是/var那
# 個裡面的index.html,輸入訪問後正如我們預測,說明精準匹配的優先順序大於一般匹配。

# 此時,我們如果還在位址列中只輸入10.1.192.130,按照推測,精準匹配沒有命中,一般匹配也沒
# 有命中,當都沒有命中時會怎麼樣呢?如果都沒有命中的話,nginx會繼承nginx伺服器的配置,
# 也就是說,如果沒命中任何一個location時會在內部將uri重新定位到10.1.192.130/index.html,
# 如果這個uri又沒命中,那直接返回根目錄下的index.html。
# 驗證:輸入10.1.192.130後,沒有命中任何一個location,走繼承的配置內部將路由轉到10.1.192.130/index.html
# 這個uri命中精準匹配,所有應該返回的是var目錄下的index.html,如我們所料。

# 在改一下:
location /index.htm {
    root   html;
    index  index.html index.htm;
}

location =/index.htm {
    root /var/www/html;
    index index.htm index.html;
}
# 如上,還是訪問10.1.192.130,都沒命中,然後走繼承的配置內部轉10.1.192.130/index.html,發現精準的
# 沒命中,一般的命中了,逾期返回結果是根html目錄下的index.html,如我們所料。

# 我們在來改
location = /index.htm {
        root   /var/www/html;
        index  index.html index.htm;
    }

location /php.html {
        root   html;
        index  php.html index.html;
    }
    # 建立一個php.html ,內容隨意
    
   # 推理輸入10.1.192.130,兩個location都沒命中,繼承預設配置內部重定向10.1.192.130/index.html又
   # 沒命中,直接返回根目錄下的index.html,如我們所料。我在將精準匹配的index.htm改成index.html
   # 預期又會出現var那個目錄裡的index.html,再次訪問10.1.192.130,果然,又如我們預期的那樣。

一般匹配和正則匹配對比:

location / {
    root   html; # 在html目錄下新建image目錄,並新增一張home.jpg圖片
    index  index.html index.htm;  # 在index.html中新增一行<img  src="./image/home.jpg" />
}

location ~ image {
    root   /var/www/image; # 新建image目錄,然後用另一張也叫home.jpg的圖
    index  index.html index.htm;
}

# 官方解釋:正則表示式由前面的“〜*”修飾符(不區分大小寫)或“〜”修飾符(不區分大小寫)指定。
#  為了找到與給定請求匹配的位置,nginx首先檢查使用字首字串定義的位置(字首位置)。 其中,將選
# 擇並記住具有最長匹配字首的位置。 然後按照在配置檔案中出現的順序檢查正則表示式。 正則表示式的搜
# 索在第一個匹配項上終止,並使用相應的配置。 如果未找到與正則表示式匹配的內容,則使用前面記住的
# 字首位置的配置。如果最長的匹配字首位置具有“ ^〜”修飾符,則不檢查正則表示式。
# 同樣,使用“ =”修飾符可以定義URI和位置的精確匹配。 如果找到完全匹配的內容,搜尋將終止。 
# 例如,如果“ /”請求頻繁發生,則定義“ location = /”將加快這些請求的處理速度,因為搜尋將在第一次
# 比較後立即終止。 這樣的位置顯然不能包含巢狀位置。
    
# 正則匹配的優先順序大於一般匹配,如上兩個location,當訪問10.1.192.130/image/home.jpg時都會命中,
# 但一般匹配命中後並不會直接將結果返回,而是繼續走正則匹配,如果正則匹配還命中,則使用正則匹配
# 的結果返回。

# 訪問10.1.192.130/,此時命中一般匹配,訪問html目錄下的index.html,而它引用著html目錄下的
# image/home.jpg,但訪問的結果卻是:

# 沒有找到?我們看一下nginx的error.log
#0: *102 open() "/var/www/image/image/home.jpg" failed (2: No such file or directory), client...
# 目錄寫錯了
location ~ image {
    root   /var/www;
    index  index.html index.htm;
}
# 再次訪問成功簡單home.jpg圖

rewrite

可以將請求路徑內部重定向到其他地方。

gzip壓縮

當我們傳送一個請求時,請求頭會帶有瀏覽器能支援的壓縮演算法,如下:Accept-Encoding:gzip,deflate,br

返回的響應結果可以根據Accept-Encoding裡的某種演算法進行壓縮,並告訴請求方自己選擇的壓縮演算法是哪種,如下:

content-encoding:br

原理

瀏覽器請求---》宣告可以接受gzip壓縮或deflate壓縮或compress或sdch壓縮,從http協議的角度看--請求頭 宣告 accecpt-encoding:gzip deflate sdch(是指壓縮演算法,其中sdch是google倡導的一種壓縮演算法,目前支援的伺服器不多)

伺服器響應---》把內容用瀏覽器能支援的某種壓縮演算法壓縮---》發給瀏覽器---》瀏覽器解碼---》渲染解碼後的內容。

優點

節省頻寬。假設news.163.com一天的PV為2億

2×10^8  ×   9×10^4 位元組 == 2×10^8 × 9×10^4 × 10^-9 = 18×K×G = 18 T

一天可節省的頻寬是18T,很驚人。

注意

圖片/mp3這樣二進位制檔案,不必壓縮,因為能壓縮的地方比較小,比如100->80位元組,壓縮比較消耗的CPU資源,所以這樣的不建議壓縮。

比較小的不建議壓縮(和gzip_min_length設定的值相關)。

常用引數

gzip on|off; # 是否開啟gzip
gzip_buffers 32 4K|16 8K # 緩衝(壓縮在記憶體中緩衝幾塊?每塊多大?)
gzip_comp_level[1-9] # 推薦6 壓縮級別越高,壓的越小,越浪費CPU計算資源
gzip_disable # 正則匹配uri,什麼樣的uri不進行gzip
gzip_min_length 200 # 開始壓縮的最小大小(在小就不要在壓縮了,在壓縮的意義不大)
gzip_http_version 1.0|1.1 # 開始壓縮的http協議版本,可以不設定,目前幾乎全是1.1協議了
gzip_proxied # 如果請求者是代理伺服器,代理伺服器該如何快取我的內容?允許不允許
gzip_types text/plain,application/xml # 對哪些型別的檔案用壓縮[參加mime.types中寫法],如t/xt,/xml,/css,/js等系統預設對html進行壓縮,所以可不寫html
gzip_vary on|off # 是否傳輸gzip壓縮標誌

nginx快取配置

nginx的快取設定,可以提高網站的效能,對於網站的圖片,尤其是新聞站,圖片一旦釋出,改動的可能是非常小的,我們希望能否在使用者訪問一次後,圖片快取在使用者的瀏覽器端,且時間比較長的快取。這需要用到nginx的expires設定,設定方式:

# 在location或if段裡,來寫
#格式 
expires 30s;
expires 30m;
expires 2h;
expires 30d;

# 注意:伺服器的日期要準確,如果伺服器的日期落後於實際日期,可能導致快取失效。

另外:304也是一種很好的快取手段

原理是:伺服器響應檔案內容時,同時響應etag標籤(內容的簽名,內容一變,它也變)和last_modified_since這兩個標籤值,瀏覽器下次請求時,頭資訊傳送這兩個標籤,伺服器檢測檔案有沒有發生變化,若無,直接返回etag,last_modfied_since,瀏覽器得知內容無改變,於是直接呼叫本地快取。這個過程也請求了伺服器,但是傳遞的內容極少,對於變化週期較短的,如靜態html,js,css比較適於這個方式。

server {
        listen       80;
        server_name  localhost;

    gzip on;
    gzip_buffers 32 4k;
    gzip_comp_level 6;
    gzip_min_length 200;
    gzip_types text/css application/x-javascript text/plain; # 具體參見mime.types
    gzip_vary on;

   access_log  logs/host.access.log  main;

    location ~ image {
        expires 3m;
            root   /var/www;
            index  index.html index.htm;
        }

    location / {
            root   html;
            index  index.html index.htm;
        }
        
    }

再次重新整理頁面

nginx反向代理

nginx做反向代理和負載均衡非常簡單,它支援兩個用法:1個proxy,1個upstream,分別用來做反向代理,和負載均衡。

以反向代理為例,nginx不自己處理php的相關請求,而是把php的相關請求轉發給apache來處理。

這就是傳說的“動靜分離”,動靜分離不是一個嚴謹的說法,叫反向代理比較規範。

location ~ \.php$ {
    proxy_pass   http://127.0.0.1;  # 注意一定要寫協議
}
# 如上,只是代理一個伺服器,如果你要把任務分給多臺機器,那就形成了負載均衡,就不能單單在location寫多個proxy_pass了,需要藉助upstream

nginx負載均衡

nginx反向代理中在location裡proxy_pass只能配置一臺伺服器,反向代理後端如果有多臺伺服器,自然可形成負載均衡,但proxy_pass如何指向多臺伺服器?把多臺伺服器用upstram指定繫結在一起並起個組名,然後proxy_pass指向該組即可。

預設的負載均衡演算法就是針對後端伺服器的順序,逐個請求。也有其他負載均衡演算法,如一致性雜湊,需要安裝第三方模組。

使用負載均衡後的一個問題:

由於我們server是使用nginx作為代理,如果我們的server列印日誌資訊,那此時請求的ip卻不是真實使用者的請求ip而是nginx伺服器的ip,這是因為對於我們的server來說,nginx就是那個請求者,這樣是不利於我們查詢真實使用者ip的,怎麼辦呢?其實我們只需要在nginx轉發請求的時候把真實使用者的ip也傳遞到後端,後端在獲取不就好了嗎?使用proxy_set_header指令,將值設定在header裡,然後在日誌裡獲取即可。

location / {
    proxy_set_header  X-Forwarded-For  $remote_addr; 
    proxy_pass http://dynamic;
}
# 一般約定成俗的是將源ip設定在x-forwarded-for內,因為
log_format  main  'CDN_IP:$remote_addr - CLIENT_IP:$HTTP_CDN_SRC_IP - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
# nginx的log_format裡已經引用了 x_forwarded_for這個變數,這是nginx約定的。 

注意在upstream配置的server裡不能是localhost(是一個保留域名),只能是ip或域名(非保留域名)

# 使用案例片段:
http {

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    # 使用預設演算法進行負載均衡
    upstream imgserver {
            server 127.0.0.1:81 fail_timeout=5s max_fails=3 weight=1;
        server 127.0.0.1:82 fail_timeout=5s max_fails=3 weight=1;
    }

    # 指定兩個虛機分別佔用81、82埠
    server {
        listen 81;
        server_name localhost;
     
        access_log  logs/81-access.log  main;
    
        root   /var/www;
            index  index.html index.htm;
    }

    server {
        listen 82;
        server_name localhost;

        access_log  logs/82-access.log  main;

        root   /var/www;
            index  index.html index.htm;
    }

    server {
            listen       80;
            server_name  localhost;

             access_log  logs/host.access.log  main;

        location ~ image {
            proxy_set_header  X-Forwarded-For  $remote_addr;
            proxy_pass http://imgserver;
        }

        location / {
                root   html;
                index  index.html index.htm;
            }
    }
}

負載均衡演算法 一致性雜湊

nginx之間支援對memcached的訪問,但對於redis不提供之間支援,如果是將頁面等資訊之間快取在memcached之中,下次再次請求nginx時nginx之間從memcached裡取出,這就實現了在接入層的直接快取。[nginx+redis實現接入層高效能快取技術]

但是對於有多臺memcached,某個key去請求哪個memcached?回撥php又幫nginx把內容儲存在哪個memcached?就以/user2.html為例,a b c d e 5臺server,請求誰?回撥php,又把user資訊寫在哪?多臺mc時,nginx與php,如何保持叢集上的演算法同步?

1、要有穩定的叢集演算法

2、nginx與php對於memcache的演算法,要同步

nginx的第三方模組一致性雜湊演算法解決了這個問題,先看官方簡介:ngx_http_upstream_consistent_hash是一個負載平衡器,它使用一個內部一致的雜湊環來選擇正確的後端節點。它被設計為與memcache相容。雜湊策略= php-memcache模組的一致性。這意味著您可以使用php-memcache模組將值儲存到memcached叢集中,然後NGINX可以在叢集中找到該值並從那裡讀取它。

nginx下載並安裝第三方模組ngx_http_upstream_consistent_hash

cd /usr/local/src
sudo wget https://github.com/replay/ngx_http_consistent_hash/archive/master.zip
unzip master.zip
cd ./ngx_http_consistent_hash-master


# 檢視之前nginx安裝引數
cd /usr/local/nginx
./nginx  -V
    nginx version: nginx/1.4.2
    built by gcc 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1) 
    TLS SNI support enabled
    configure arguments: --sbin-path=/usr/local/nginx/nginx --conf-path=/usr/local/nginx/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --with-http_ssl_module --with-pcre=/usr/local/src/pcre-8.37 --with-zlib=/usr/local/src/zlib-1.2.11 --with-openssl=/usr/local/src/openssl-1.0.1t
    
# 重新編譯安裝nginx    
cd /usr/local/src/nginx-1.4.2
# 檢視nginx配置引數幫助
./configure --help|grep add
    ...
    --add-module=PATH                  enable an external module
    
sudo pkill -9 nginx
sudo rm -rf /usr/local/nginx

# 使用--add-module為nginx新增第三方模組
sudo ./configure --sbin-path=/usr/local/nginx/nginx --conf-path=/usr/local/nginx/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --with-http_ssl_module --with-pcre=/usr/local/src/pcre-8.37 --with-zlib=/usr/local/src/zlib-1.2.11 --with-openssl=/usr/local/src/openssl-1.0.1t --add-module=/usr/local/src/ngx_http_consistent_hash-master
# 修改objs目錄下的Makefile檔案 去除第三行的-Werror
sudo gedit ./objs/Makefile
sudo make && make install

nginx第三方一致性演算法使用方式

upstream somestream {
  consistent_hash $request_uri;
  server 10.50.1.3:11211;
  server 10.50.1.4:11211;
  server 10.50.1.5:11211;
}

...

server {
  listen       80;
  server_name  localhost;

  location / {
    default_type text/html;
    set $memcached_key $request_uri;
    memcached_pass somestream;
    error_page      500 404 405 = @fallback;
  }

  location @fallback {
    root /srv/www/whatever;
    fastcgi_intercept_errors on;
    error_page 404 = @404;

    set $script $uri;
    set $path_info "";

    include /usr/local/nginx/conf/fastcgi_params;
    fastcgi_param SCRIPT_FILENAME /srv/www/whatever/test.php;
    fastcgi_param SCRIPT_NAME $script;
    fastcgi_param REQUEST_URI $uri;
    fastcgi_pass   127.0.0.1:9000;
  }
}

日誌管理

server log

Nginx可以針對不同的server做不同的log

server {
        ...
        #charset koi8-r;
        access_log  logs/host.access.log  main;
        ...
}

這說明 serevr,它的訪問日誌檔案是 logs/host.access.log,使用的日誌格式是“main”格式,除了main格式,你可以定義其他格式。

# main格式,main格式是我們定義好的一種日誌格式,並起個名字便於引用
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

# 遠端ip-遠端使用者/使用者時間 請求方法(如GET/POST) 請求的body長度   referer來源資訊
# http-user-agent使用者代理/蜘蛛 被轉發的請求的原始ip

# 這個referer來源資訊是隻你是從哪裡訪問到我這個地址的,可能是百度,google,或者站內地址
# http-user-agent使用者可能使用瀏覽器作為代理去解析response,或其他代理等

更多格式

修改日誌格式只能去修改nginx.conf,不能修改站點default.conf,但可以在站點裡複用父類配置

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  'CDN_IP:$remote_addr - CLIENT_IP:$HTTP_CDN_SRC_IP - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
                      '(REQUEST TIME:"$request_time") (RESPONSE TIME:"$upstream_response_time")';
     ...
    server {
        ...
    }
}     
                 
# log配置時間說明:
$request_time – Full request time, starting when NGINX reads the first byte from the client and ending when NGINX sends the last byte of the response body
$upstream_connect_time – Time spent establishing a connection with an upstream server
$upstream_header_time – Time between establishing a connection to an upstream server and receiving the first byte of the response header
$upstream_response_time – – Time between establishing a connection to an upstream server and receiving the last byte of the response body

# 站點內複用日誌配置
server {
      listen       80 default_server
      ;
      listen       [::]:80 default_server;
      server_name  localhost;
  
      #charset koi8-r;
      # 複用http內的main日誌格式
      access_log  /var/log/nginx/host.access.log  main;
      
      location / {
          include /etc/nginx/uwsgi_params;
          uwsgi_connect_timeout 30;
          uwsgi_pass django;
          root   /usr/share/nginx/html;
          index  index.html index.htm;
      }

日誌定時切割

實際上使用的是linux定時器+shell指令碼【使用USER1訊號】

# 知識補充:linux作業系統獲取設定日期
$ date # 獲取當前日誌
Wed Apr 29 13:43:09 CST 2020

$ date -d yesterday # 獲取昨天的日期
Tue Apr 28 13:43:24 CST 2020

$ date -d yesterday +%Y%m%d
20200428

按照年月日時分切割日誌

-bash-4.2$ pwd
/usr/local/nginx/logs

-bash-4.2$ sudo touch crontab_nginx.sh
-bash-4.2$ sudo vim crontab_nginx.sh
BASEDIR="/usr/local/nginx/logs"

logfile="$BASEDIR/access.log.$(date -d yesterday +%Y%m%d%H%M)"

mv $BASEDIR/host.access.log $logfile

kill -usr1 `cat /usr/local/nginx/nginx.pid`

-bash-4.2$ sudo crontab -e # 編輯一個定時任務,編輯完後會自動啟動
*/1 * * * * bash /usr/local/nginx/logs/crontab_nginx.sh

-bash-4.2$ ll
total 624
-rw-r--r--. 1 root   root   8269 Apr 29 14:05 access.log
-rw-r--r--  1 nobody root 428832 Apr 29 15:31 access.log.202004281537
-rw-r--r--  1 nobody root      0 Apr 29 15:37 access.log.202004281538
-rw-r--r--  1 nobody root 143970 Apr 29 15:39 access.log.202004281539
-rw-r--r--  1 nobody root  29325 Apr 29 15:39 access.log.202004281540
-rw-r--r--  1 root   root    179 Apr 29 15:36 crontab_nginx.sh
-rw-r--r--. 1 nobody root  10662 Apr 29 15:38 error.log
-rw-r--r--  1 nobody root      0 Apr 29 15:40 host.access.log

除了使用年月日時分切割日誌你還可以按照年月劃分目錄去儲存日誌,或者按照檔案日期+檔案大小切割

# 需要獲取今天的日誌檔案列表,對檔案列表裡每個檔名分割,對分割後的陣列判斷
# 如果存在某檔名分割後的長度大於3,直接在原來基礎上繼續加一,否則取當前日期加.1為字尾
BASEDIR="/usr/local/nginx/logs"

logfile="$BASEDIR/access.log.$(date -d yesterday +%Y%m%d%H%M)"

# 過濾當日日誌檔案
# ls -a access.log access.log.$(date -d yesterday +%Y%m%d%H)0*

# 獲取某個檔案的大小
size=`ls -l /usr/local/nginx/logs/host.access.log | awk '{print $5}'`

# 字串擷取
STR="123,45,6ab,c"
FINAL=`echo ${STR: -1}`

# 字串分割成陣列
string="78 9 12"
array=(${string//,/ })

# 陣列排序
echo ${array[@]}
array=$(echo ${array[@]} | python -c "print ' '.join(sorted(raw_input().split(' '), key=lambda x:int(x)))")
echo ${array[@]}

# for迴圈
#for var in ${array[@]}
#do
#   echo $var
#done

# 取陣列元素
#echo ${array[-1]}

#echo ${#array[@]}

#echo $FINAL


# if語句
#if [ $size -gt 300]
#then
#  mv $BASEDIR/host.access.log $logfile
#  kill -usr1 `cat /usr/local/nginx/nginx.pid`

高效能伺服器架設

對於高效能網站,請求量大,如何支撐?

一、減少請求

1、對於開發人員--合併css,背景圖片,減少mysql查詢等

2、對於運維nginx的expires,利用瀏覽器快取等減少查詢

3、利用cdn來響應請求,如用百度cnd等分走一部分請求

二、伺服器叢集+負載均衡

減少請求的操作我們都做好了後,接下來的響應是不可避免的了,接下來該考慮如何更好的響應高併發請求,我們要做的是把工作內容“平均”分給每臺伺服器,最理想的狀態,每臺伺服器的效能都被充分利用。

apache壓力測試

安裝

apache 測試工具使用

/usr/local/httpd/bin/ab --help

# 2000併發請求5000次nginx的首頁
/usr/local/httpd/bin/ab -c 2000 -n 5000 http://localhost/index.html

解決問題:

1、socket: Too many open files (24)

2、apr_socket_recv: Connection reset by peer (104)

針對問題1

Unix系統中將一切都視為檔案,無論是硬碟,軟盤,印表機器,滑鼠,網路請求都當成檔案來處理,網路請求的併發超過了系統預設支援的最大開啟連結數目,先檢視一下系統支援最大開啟連線數:

ulimit -a # 檢視使用者資源使用限制極限
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 31347
max locked memory       (kbytes, -l) 16384
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 30000
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

針對ulimit說明:顯示(或設定)使用者可以使用的資源的限制(limit),這限制分為軟限制(當前限制)和

硬限制(上限),其中硬限制是軟限制的上限制,應用程式在執行過程中使用的系統資源不超過相應的軟限制,任何的超越都導致程序的終止。

對於ulimit引數描述,ulimit不限制使用者可以使用的資源,但設定對可開啟的最大檔案數(max open files)和可同時執行的最大程序數(max user processes)無效。

-a 列出所有當前資源極限
-c 設定core檔案的最大值.單位:blocks
-d 設定一個程序的資料段的最大值.單位:kbytes
-f Shell 建立檔案的檔案大小的最大值,單位:blocks
-h 指定設定某個給定資源的硬極限。如果使用者擁有 root 使用者許可權,可以增大硬極限。任何使用者均可減少硬極限
-l 可以鎖住的實體記憶體的最大值
-m 可以使用的常駐記憶體的最大值,單位:kbytes
-n 每個程序可以同時開啟的最大檔案數。 n的取值一般為2的指數冪
-p 設定管道的最大值,單位為block,1block=512bytes
-s 指定堆疊的最大值:單位:kbytes
-S 指定為給定的資源設定軟極限。軟極限可增大到硬極限的值。如果 -H 和 -S 標誌均未指定,極限適用於以上二者
-t 指定每個程序所使用的秒數,單位:seconds
-u 可以執行的最大併發程序數
-v Shell可使用的最大的虛擬記憶體,單位:kbytes
# 我們請求的併發數為2000,而系統允許的最大開啟連線數(open files)才1024,所以我們要調大這個引數,調大到多少,看機器的效能
ulimit -n 10000  # 只是在當前命令列臨時修改,當命令列被關閉,設定的該值也隨即失效。root使用者下,是可以設定的。
bash: ulimit: open files: 無法修改 limit 值: 不允許的操作

# 由於我不是root使用者,所以我是不能直接設定max open files的,解決辦法:
# 檢視系統最大開啟檔案數系統級硬限制:
cat /proc/sys/fs/file-max   # 直接修改這個數可以修改系統預設的硬限制
6553560  #  這個數已經很大了,我們沒必要在動

# 1、修改系統預設的預設的硬限制
vi /etc/sysctl.conf  # 在檔案最後加入fs.file-max=6553560
sysctl -p # 使修改生效,或者直接修改/proc/sys/fs/file-max也可以修改系統預設的硬限制

# 2、修改系統使用者級檔案開啟數限制
vi /etc/security/limits.conf # 在檔案最後加入(這裡我只修改了對我自己的限制,使用"*"設定的話則對所有使用者生效)
liuwei            soft    nofile            100000   # 檔案開啟數,使用者級軟限制,針對liuwei使用者
liuwei            hard    nofile            300000  # 檔案開啟數,使用者級硬限制
liuwei            soft    nproc            100000  # 可使用執行緒數,使用者級軟限制,與我們要解決的這個問題無關
liuwei            soft    nproc            300000  # 可使用執行緒數,使用者級硬限制,與我們要解決的這個問題無關

# 3、儲存;重啟系統生效

重啟後再次檢視

ulimit -n
1024

ulimit -n 10000

我擦,上面設定的不行,即沒有生效,又不讓直接設定。最終在這裡找到了答案,可能是因為ubuntu的gnome terminal預設是none-login的,所以我們在配置檔案中修改並沒有影響到當前的terminal,所有我們使用su username登陸後試試

su liuwei

ulimit -n # 這回正常了,是我們之前設定的軟連結數目
100000

# 我們su root試試,看看ulimit -n是不是我們設定的10000,發現root下ulimit -n還是1024,因為
# 我們之前修改只是針對liuwei使用者並不是*也不是root所以他們並不會影響。

針對問題2

apr_socket_recv這是一個作業系統核心的一個引數,在高併發下,核心會認為系統受到了SYN flood攻擊(洪水攻擊),會發送cookies(possible SYN flooding on port 80.Sending cookies),這樣會減慢影響請求的速度,所以在應用伺服器上設定這個引數為0,即禁用系統保護就可以進行大併發量測試了。

vim /etc/sysctl.conf  # 修改net.ipv4.tcp_syncookies=0
sysctl -p

# 還可以直接設定系統執行引數,立即生效,重啟系統後失效
echo 0 > /proc/sys/net/ipv4/tcp_syncookies

# 其他核心引數說明
net.ipv4.tcp_max_syn_backlog  # 決定了SYN_RECV狀態佇列的數量,一般預設值為512或1024,
# 即超過這個數量,系統將不再接受新的TCP連線請求,一定程度上可以防止系統資源耗盡。可根據
# 情況增加該值以接受更多的連線請求。

net.ipv4.tcp_tw_recycle  # 引數決定是否加速TIME_WAIT的sockets,預設為0。

net.ipv4.tcp_tw_reuse # 引數決定是否可將TIME_WAIT狀態的sockets用於新的TCP連線
# ,預設為0

net.ipv4.tcp_max_tw_buckets # 引數決定TIME_WAIT狀態的sockets總數量,可根據連線
# 數和系統資源需要進行設定。
# 測試使用
/usr/local/httpd/bin$ ./ab -c 100 -n 500 http://10.1.192.130/index.html
This is ApacheBench, Version 2.3 <$Revision: 1874286 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 10.1.192.130 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Finished 500 requests


Server Software:        nginx/1.18.0
Server Hostname:        10.1.192.130
Server Port:            80

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

Concurrency Level:      100 # 併發數
Time taken for tests:   2.268 seconds # 總耗時
Complete requests:      500 # 500 個請求
Failed requests:        0  # 0個錯誤
Total transferred:      422500 bytes
HTML transferred:       306000 bytes
Requests per second:    220.49 [#/sec] (mean)
Time per request:       453.539 [ms] (mean)
Time per request:       4.535 [ms] (mean, across all concurrent requests)
Transfer rate:          181.95 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        9  169 289.9     77    1123
Processing:    14  163 168.3     99    1331
Waiting:        8  162 168.3     98    1331
Total:         22  332 338.6    197    1821

Percentage of the requests served within a certain time (ms)
  50%    197
  66%    228
  75%    422
  80%    469
  90%    795  # 90%的請求在1秒內完成了
  95%   1161
  98%   1247
  99%   1809
 100%   1821 (longest request)

nginx統計工具

nginx的觀察統計模組,方便我們觀察每時每刻到底有多少併發多少等待,便於我們調優,使用的是--with-http_stub_status_module,使用/usr/sbin/nginx -V檢視是否安裝了這個模組,如果沒安裝安裝。

配置統計模組:

server {
    listen     localhost:80;

    location /status {
        stub_status on; # 開啟統計模組
        access_log off;   # 禁止日誌
        allow 127.0.0.1; # 允許訪問的ip,其他的ip一律禁止
        deny all;
    }
}

優化思路

分析:對於nginx請求無非是讀取mysql或磁碟上的html等檔案,那就必須要有兩個步驟要走,1、建立socket連線。2、開啟檔案。

要建立socket連線,要考慮兩個硬性的限制,第一你的socket連線能不能建立這麼多,你的記憶體是不是足夠大,因為socket連線上的資訊在記憶體維護著。

開啟檔案並沿socket返回,要考慮作業系統允不允許你開啟那麼多檔案,預設狀況下,一個程序同時只能開啟1024個檔案。

高併發無非就是能建立足夠多的socket和開啟足夠多的檔案,還有就是你的網絡卡,萬兆乙太網能撐得住流量。

排查問題,也要注意觀察這兩點,主要從系統的dmesg和nginx的error.log來觀察。

# socket
  # 系統層面
      #1、 最大連線數 somaxconn   開啟足夠多的連線【餐館:把座位搞得足夠多】
      #2、加快tcp連線的回收 recycle    有人用完了連線快速回收【餐館裡來人坐下吃飯,服務員在催吃完快走】
      #3、空的tcp連線是否允許回收利用 reuse   【有人點餐沒來的急吃飯或不吃了,這個座位立刻被回收利用】
      #2、洪水攻擊-不做洪水攻擊 【來的客人越多越好,不做洪水抵禦】
  # nginx層面
      # 子程序允許開啟的連線(worker_connections)
      
# 檔案
    #nginx層面
        #1、子程序允許開啟的檔案 worker_limit_nofile    一個子程序最大能開啟多少個檔案
    
  #系統層面      
      #1、ulimit -n 設一個比較大的值

keep_alivetime  0;  # 高併發下,tcp連線非常珍貴,在http1.1中為了防止頻繁的握手,會保持著一個tcp連線,在這個連線之上可能還會繼續請求
# 一些css,js等其它連線,在nginx中這個keep_alivetime預設為65s,也就是說在高併發下,tcp資源這麼珍貴的情況下,它不僅佔著,還一直保持
# 著65s的連線。所以在高併發下建議講該值設定為2s以下。
worker_limit_nofile 10000   # 全域性區配置
event {
    worker_connections 10240;
}
ulimit -n 30000  # 設定最大檔案連線數目,uninx把一切都當做檔案,包括網路連結也認為是檔案,所以要設定足夠大的連結數目
cat 10000 > /proc/sys/net/core/somaxconn
cat 1 > /proc/sys/net/ipv4/tcp_tw_recycle
cat 1 > /proc/sys/net/ipv4/tcp_tw_reuse
cat 0 > /proc/sys/net/ipv4/tcp_tw_syncookies
# 客戶端要求也能撐得住  
ulimit

遇到的問題

記一次上線nginx響應超時

upstream timed out (110: Connection timed out) while reading response header from upstream

解決辦法:

https://blog.csdn.net/u014218983/article/details/81217032

https://www.cnblogs.com/succour/p/6305574.html

server {
    listen       80;
    server_name  XXX.rong360.com;
    
    large_client_header_buffers 4 16k;     # 讀取大型客戶端請求頭的緩衝區的最大數量和大小
    client_max_body_size 300m;     #設定nginx能處理的最大請求主體大小。
    client_body_buffer_size 128k;  #請求主體的緩衝區大小。 
    proxy_connect_timeout 600;
    proxy_read_timeout 600;
    proxy_send_timeout 600;
    proxy_buffer_size 64k;
    proxy_buffers   4 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;
    
    ……
    location / {
        uwsgi_send_timeout 600;        # 指定向uWSGI傳送請求的超時時間,完成握手後向uWSGI傳送請求的超時時間。
        uwsgi_connect_timeout 600;   # 指定連線到後端uWSGI的超時時間。
        uwsgi_read_timeout 600;        # 指定接收uWSGI應答的超時時間,完成握手後接收uWSGI應答的超時時間。
        ……
    }

nginx請求時間過長

在測試環境使用k8s部署的時候,當啟動一個nginx,在啟動十個server容器時,正常建立請求是沒問題的,但是我現在關閉九個server容器,支流一個時,我再次發請求就出現了以下情況:

響應時間列印了10個,我剛才是關閉了九個server容器的,但是nginx依舊沒反應過來,還把請求往我那關閉的容器裡發,導致請求不到耗時超長,nginx再次轉發到其他容器,直到有一個容器響應了為止。解決辦法:重新部署nginx+重新部署server