官术网_书友最值得收藏!

2.1 nginx服務(wù)器

Web網(wǎng)站的功能由編程語言實現(xiàn),例如Java、Python和PHP等。編程語言專注于網(wǎng)站功能的實現(xiàn),資源映射與連接處理則由服務(wù)器軟件完成。常見的服務(wù)器軟件有Apache、nginx和Tomcat等,接下來我們將通過nginx來增進對服務(wù)器的了解。

nginx是一個HTTP和反向代理服務(wù)器,同時也是郵件代理服務(wù)器和通用的TCP / UDP代理服務(wù)器。它具有模塊化設(shè)計、可擴展、低內(nèi)存消耗、支持熱部署等優(yōu)秀特性,所以非常多的Web應(yīng)用將其作為服務(wù)器軟件。本書也將使用它實現(xiàn)一些反爬蟲的功能。

nginx有一個主進程和若干工作進程,其中主進程用于讀取和評估配置并維護工作進程,工作進程會對請求進行實際處理。nginx采用基于事件的模型和依賴于操作系統(tǒng)的機制,有效地在工作進程之間分發(fā)請求。工作進程數(shù)在配置文件中進行定義,可以設(shè)定具體數(shù)值或使用默認選項。

2.1.1 nginx的信號

信號(signal)是控制nginx工作狀態(tài)的模塊,我們可以在終端使用信號來控制nginx的啟動、停止和配置重載等。信號的語法格式如下:

nginx -s signal

其中signal是信號名稱。常用的nginx信號有以下幾種。

? stop:快速關(guān)機。

? quit:正常關(guān)機。

? reload:重新加載配置文件。

? reopen:重新打開日志文件。

假如我們需要停止nginx服務(wù),但又希望它處理完當前請求后再停止工作進程,可以在終端向nginx發(fā)送正常關(guān)機的信號,命令為:

$ nginx -s quit

當nginx的配置被更改或者添加新的輔助配置文件時,它們不會立即生效。如果想讓新的配置生效,就必須重新啟動nginx或者進行配置重載。假如我們希望nginx在不影響當前任務(wù)處理的情況下重載配置,可以通過終端向nginx發(fā)送重新加載配置文件的信號,命令為:

$ nginx -s reload

一旦主進程收到配置重載信號,它將檢查新配置文件的語法有效性,并嘗試應(yīng)用其中的配置。如果成功,主進程將啟動新的工作進程并向舊工作進程發(fā)送關(guān)閉請求,否則主進程將回滾更改,并繼續(xù)使用舊配置。舊工作進程在接收關(guān)閉請求后會停止接受新連接,并且繼續(xù)為當前請求提供服務(wù),直到當前請求處理完畢才關(guān)閉。

更多nginx信號的知識可前往nginx官方文檔查看,詳見http://nginx.org/en/docs/control.html

2.1.2 nginx配置文件

nginx由模塊組成,而這些模塊由配置文件中特定的指令控制,也就是說nginx的配置文件決定了nginx及其模塊的工作方式。nginx的配置文件分為主配置文件和輔助配置文件:主配置文件名為nginx.conf,默認存放在 /etc/nginx目錄中;輔助配置文件要求以 .conf作為文件后綴,并且默認存放在/etc/nginx/conf.d目錄中。要注意的是,nginx允許同時存在多個輔助配置文件。

nginx的指令分為簡單指令和塊指令。一個簡單的指令由指令名稱和參數(shù)組成,它們以空格作為分隔符,并以分號結(jié)尾,如:

error_page 404 /404.html;

其中error_page是指令名稱,404和 /404.html共同組成參數(shù),作用是指定404錯誤顯示的HTML文件。

塊指令與簡單指令具有相同的結(jié)構(gòu),但它不是以分號結(jié)尾,而是以花括號包圍的一組附加指令結(jié)束,如:

location /404.html {
    root   /home/async/www/error_page;
}

如果塊指令內(nèi)包含其他指令,則該塊指令稱為上下文。常見的上下文有events、http、server和location。要注意的是,這里還有一個隱藏的main上下文,它并非實際存在,類似于層級的根目錄,即所有的指令的最外層都是main。main上下文作為其他上下文的參考對象,例如events和http,必須寫在main上下文中,server必須寫在http中,而location則必須寫在server中。對于它們的關(guān)系,我們可以通過一段簡單的配置來理解:

http {
    server {
        location / {
            root /www/index index.html;
        }
        location /images/ {
            # ...
        }
    }
}

配置文件的注釋符為#。以上配置默認監(jiān)聽80端口,當我們在本地訪問http://localhost/時,服務(wù)器將根據(jù)配置文件設(shè)定的資源路徑尋找資源,并將符合條件的資源發(fā)送給客戶端,如果資源不存在,則發(fā)送404錯誤。我們并沒有在配置中添加任何有關(guān)main的文字,但http上下文確實包含在main中。

nginx提供了一個默認的輔助配置文件default.conf,存放在 /etc/nginx/conf.d目錄中,里面包含了若干server塊指令示例。我們可以在終端使用如下命令查看default.conf文件的內(nèi)容:

$sudo cat /etc/nginx/conf.d/default.conf

命令執(zhí)行后,終端會輸出如下內(nèi)容(以 ... 代替部分被注釋的內(nèi)容):

server {
    listen         80;
    server_name   localhost;
    #charset koi8-r;
    #access_log   /var/log/nginx/host.access.log   main;

    location / {
        root    /usr/share/nginx/html;
        index   index.html index.htm;
    }

    #error_page   404                  /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page    500 502 503 504   /50x.html;
    location = /50x.html {
        root    /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    # ...
    #}
}

2.1.3 簡單的代理服務(wù)

提供資源是服務(wù)器的功能之一,接下來我們就來學(xué)習(xí)如何通過nginx實現(xiàn)簡單的代理服務(wù)。本次任務(wù)要求服務(wù)器根據(jù)用戶的請求URL,響應(yīng)服務(wù)器本地目錄中的資源(如 /data/www目錄中包含的HTML文件和 /data/images目錄中包含的圖片文件)。我們可以通過編輯nginx的配置文件,將URL和本地目錄中的資源進行映射,從而實現(xiàn)這個需求。

首先,我們需要準備本地目錄的資源。在用戶目錄(如 /home/async)下創(chuàng)建www文件夾,將包含任意內(nèi)容的index.html文件放到www目錄中。在www文件夾中創(chuàng)建images文件夾,并在里面放置一張名為example.png的圖片。

然后使用編輯器打開nginx默認的輔助配置文件default.conf,將其中的所有代碼注釋后,編寫新的配置:

http {
    server {
    }
}

在通常情況下,server需要確定監(jiān)聽的端口和服務(wù)器名稱, nginx一旦決定處理請求,就會根據(jù)塊指令中定義的指令參數(shù)測試請求頭中指定的URI。我們將下方的location添加到server中:

location / {
    root /home/async/www;
}

配置中的location指定“/”與請求中的URI進行比較。對于匹配的請求,URI將指向root指令中指定的路徑,即 /home/async/www,以便將本地資源與請求對應(yīng)。如果存在多個匹配的location塊指令,那么nginx會選擇具有最長前綴的塊指令。當無法匹配到其他前綴時,就會匹配“/”。

接下來添加第二個location塊指令:

location /images/ {
    root /home/async/www;
}

它將匹配以“/images/”開頭的URI(“/”也匹配此類請求,但由于“/”的前綴長度比“/images/”短,所以優(yōu)先匹配“/images/”)。完整的輔助配置文件內(nèi)容如下:

server {
location / {
root /home/async/www;
}

location /images/ {
root /home/async/www;
}
}

如果想讓剛才的配置生效,我們需要給nginx發(fā)送重載配置的信號:

$ nginx -s reload

配置生效后,nginx就會監(jiān)聽80端口(80是默認值),我們可以在瀏覽器中訪問http://localhost/。服務(wù)器為響應(yīng)這次請求,會根據(jù)配置文件中指定的文件路徑搜索HTML文件,并優(yōu)先選擇名為index.html的文件作為響應(yīng)內(nèi)容。如果在瀏覽器中訪問http://localhost/images/example.png,那么nginx會根據(jù)配置文件,從指定的路徑中搜索example.png文件,如果存在,則返回文件資源,否則觸發(fā)404錯誤。

在真正訪問http://localhost/之前,還有兩件事要做。第一件事是編輯nginx主配置文件,將配置中User指定的用戶從nginx改為你的操作系統(tǒng)的用戶名(比如我的操作系統(tǒng)的用戶名為async)。打開主配置文件的命令為:

$sudo nano /etc/nginx/nginx.conf

主配置文件的第一行即是User設(shè)置。

第二件事是關(guān)閉SELinux。SELinux是美國國家安全局(NSA)對于強制訪問控制的實現(xiàn),默認安裝在版本較新的Linux系統(tǒng)中。如果當前操作系統(tǒng)中沒有SELinux,則無須關(guān)閉。打開SELinux配置文件的命令為:

$sudo nano /etc/selinux/config

注意將配置文件中的SELINUX=enforcing改為SELINUX=disabled,保存改動后重新啟動操作系統(tǒng)。

接著我們用瀏覽器訪問http://localhost/,此時顯示的內(nèi)容如圖2-2所示。

圖2-2 瀏覽器顯示的內(nèi)容

再試一試訪問http://localhost/images/example.png,瀏覽器顯示的內(nèi)容如圖2-3所示。

圖2-3 瀏覽器中顯示的圖片

當我們使用瀏覽器訪問指定的URL后,如果瀏覽器中能夠正確顯示我們準備的HTML內(nèi)容和圖片,就說明nginx輔助配置已生效。

2.1.4 nginx模塊與指令

nginx內(nèi)置了很多模塊,可以從nginx文檔中的Modules reference部分查看。其中比較常用的模塊是ngx_http_rewrite_module(詳見http://nginx.org/en/docs/http/ngx_http_rewrite_module.html),我們可以通過學(xué)習(xí)該模塊來熟悉nginx模塊的語法。

ngx_http_rewrite_module模塊的主要作用是重定向,它通過正則表達式或判斷語句來更改請求的URI。該模塊有一些主要的指令,這些指令分別是if、set、break、return和rewrite。if指令的語法和語境如表2-1所示。

表2-1 if指令的語法和語境

我們可以看到if指令的語法是:

if 條件 {
    ...
}

這與其他編程語言的語法非常相似,很容易理解。if指令沒有默認值,使用范圍限制在server塊和location塊內(nèi)。它的條件有以下幾種情況。

? 變量名稱:如果變量的值是空字符串或0,則條件的布爾值為false;在nginx 1.0.1版本之前,任何以0開頭的字符串都被認為是錯誤的值。

? =或!=:比較變量和字符串。

? ~或~*:將變量與正則表達式匹配,區(qū)分大小寫。如果正則表達式包含}或;字符,則整個表達式應(yīng)該用單引號或雙引號括起來。

? -f或!-f:檢查文件是否存在。

? -d或!-d:檢查目錄是否存在。

? -e或!-e:檢查文件、目錄或符號鏈接是否存在。

? -x或!-x:檢查可執(zhí)行文件。

我們可以通過一些例子來理解這些條件判斷:

# 當請求頭中User-Agent 頭域的值包含MSIE 字符串,則重定向到指定URI
if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /msie/$1 break;
}
# 當請求頭中Cookie 頭域的值滿足條件,則設(shè)定$id 變量值為正則部分
if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;
}
# 如果請求方式是 POST,則返回 405
if ($request_method = POST) {
    return 405;
}
# 限制下載速度為10k,$slow 可以通過set 指令設(shè)置
if ($slow) {
    limit_rate 10k;
}
#當請求頭中Referer 頭域的值為空或www.example.com 時,允許訪問,否則返回403
valid_referers none www.example.com;
if ($invalid_referer) {
    return 403;
}

我們可以從其中選出一個作為驗證對象。打開nginx輔助配置文件default.conf,并在文件中添加對Referer的判斷語句:

server {
    location / {
        # 對請求的Referer 進行驗證,如果沒有Referer 頭域
        # 或者頭域值為www.example.com,則允許訪問
        valid_referers none www.example.com;
        if ($invalid_referer) {
            return 403;
        }
        root /home/async/www;
    }
    location /images/ {
        root /home/async/www;
    }
}

為了讓nginx啟用新配置,我們需要給它發(fā)送reload信號:

$ nginx -s reload

為了驗證請求的過濾效果,我們可以使用Postman進行測試。首先測試沒有Referer頭域的請求,得到的結(jié)果如圖2-4所示。

圖2-4 Postman請求結(jié)果1

本次請求的響應(yīng)狀態(tài)碼為200,說明服務(wù)器可以正常響應(yīng)客戶端的請求。然后測試Referer頭域的值不滿足條件的情況,結(jié)果如圖2-5所示。

圖2-5 Postman請求結(jié)果2

本次請求的響應(yīng)狀態(tài)碼為403,說明服務(wù)器對Referer頭域的值進行判斷后,發(fā)現(xiàn)它并不符合要求。除了以上給出的$invalid_referer和$request_method變量之外,還有哪些變量呢?在介紹ngx_http_core_module時,我們給出了nginx支持的嵌入式變量,其中常用的變量及含義如表2-2所示。

表2-2 nginx中常用的變量及其含義

nginx所支持的嵌入變量詳見http://nginx.org/en/docs/http/ngx_http_core_module.html#variables

nginx指令列表詳見http://nginx.org/en/docs/http/ngx_http_core_module.html#Directives。以limit_rate指令為例,其語法和語境如表2-3所示。

表2-3 limit_rate指令的語法和語境

limit_rate的作用是限制客戶端的傳輸速度,單位為字節(jié)/秒,默認值為0,即不限速。limit_rate是對單個請求設(shè)置限制的,意味著如果客戶端同時打開兩個連接,則總速率將是指定限制的兩倍。除了全局限速以外,還可以根據(jù)條件設(shè)置限速:

server {
    if (condition) {
        set $limit_rate 100k;
    }
}

ngx_http_rewrite_module模塊的語法和語境如表2-4所示。

表2-4 ngx_http_rewrite_module模塊的語法和語境

如果指定的正則表達式與請求URI匹配,則URI將根據(jù)replacement字符串的指定進行更改。該rewrite指令按照其在配置文件中出現(xiàn)的順序依次執(zhí)行,可以使用標志終止對指令的進一步處理。如果替換字符串以http://、https://或$scheme開頭,則處理停止并將重定向返回給客戶端。

flag參數(shù)可以是以下值之一。

? last:停止處理當前的ngx_http_rewrite_module指令集,并開始搜索與更改后的URI匹配的新位置。

? break:ngx_http_rewrite_module與break指令一樣,可以停止處理當前的指令集。

? redirect:返回帶有302代碼的臨時重定向。如果替換字符串不以http://、https://或$scheme開頭,則使用它。

? permanent:返回301代碼的永久重定向。

官方給出的重定向示例代碼如下:

server {
    ...
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra   last;
    return   403;
    ...
}

但是如果將這些指令放在/download/位置內(nèi),則該last標志應(yīng)替換為break,否則nginx將進行10次循環(huán)并返回500錯誤:

location /download/ {
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra   break;
    return   403;
}

2.1.5 nginx日志

日志是nginx的重要組成部分,記錄著每一次請求的相關(guān)信息,是開發(fā)者了解客戶端請求和服務(wù)器端響應(yīng)狀態(tài)的好幫手。nginx的日志分為訪問日志和錯誤日志,存儲路徑可以在nginx主配置文件中查看,設(shè)置訪問日志存放路徑的指令名為access_log,而設(shè)置錯誤日志存放路徑的指令名為error_log,示例命令如下:

access_log   /var/log/nginx/access.log   main;
error_log /var/log/nginx/error.log;

1. 訪問日志

訪問日志主要記錄客戶端訪問nginx的請求信息,如客戶端的IP地址、請求的URI、響應(yīng)狀態(tài)、Referer頭域的值等。以下記錄為本機nginx訪問記錄中的一條:

127.0.0.1 - - [11/Mar/2019:08:42:43 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11;
Fedora; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0" "-"

nginx中與訪問日志相關(guān)的指令是log_format和access_log。log_format用來設(shè)置訪問日志的格式,也就是日志文件中每條日志記錄的格式,它在主配置文件中的設(shè)置如圖2-6所示。

圖2-6 訪問日志格式

訪問日志默認使用main格式,并且里面記錄了很多的信息,這些信息的含義如表2-5所示。

表2-5 log_format支持的變量及其釋義

2. 錯誤日志

錯誤日志主要記錄客戶端訪問nginx錯誤時的請求信息,不支持自定義格式。錯誤日志有多種等級,如debug、info、notice、warn、error和crit,從左到右日志級別逐步遞增。nginx的主配置文件中將錯誤日志的級別設(shè)為warn。以下記錄為本機nginx錯誤記錄中的一條:

2019/03/10 20:30:44 [error] 11160#11160: *1 "/home/async/www/index.html" is forbidden
(13: Permission denied), client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host:
"localhost"

我們可以在錯誤記錄中看到錯誤發(fā)生的具體時間、原因、客戶端的IP地址、請求方式和協(xié)議版本等信息,這對我們排查錯誤和測試有很大的幫助。

2.1.6 小結(jié)

nginx是一款輕量、高性能的服務(wù)器應(yīng)用,配置靈活、功能強大,深受開發(fā)者喜愛。nginx具有條件判斷、連接限制和客戶端信息獲取等功能,這些功能為開發(fā)者限制爬蟲程序提供了條件。

主站蜘蛛池模板: 潞城市| 邯郸市| 大化| 广丰县| 新河县| 鹰潭市| 两当县| 静安区| 开封县| 清镇市| 教育| 灌云县| 鄢陵县| 眉山市| 古浪县| 饶阳县| 屏东市| 尖扎县| 晴隆县| 布尔津县| 杭锦后旗| 大安市| 根河市| 山东| 平泉县| 曲靖市| 永仁县| 延庆县| 乃东县| 内江市| 垫江县| 阿巴嘎旗| 罗平县| 肇源县| 石柱| 西昌市| 濉溪县| 收藏| 台山市| 乌拉特后旗| 肃宁县|