當運維遇到要重寫情況時,往往是要程式員把重寫規則寫好後,發給你,你再到生産環境下配置。對于重寫規則說到底就是正則比對,做運維的豈能對正規表達式不了解的?最起碼最基本的正規表達式會寫。套用一句阿裡的話(某網友說是阿裡說的,不清楚到底是不是出自阿裡)“不懂程式的運維,不是好運維;不懂運維的開發,不是好開發。”。 正規表達式也是一門語言哈。當你學習一門語言時,必然會遇到該門語言的正規表達式這章節的。 在這裡推薦一本非常好的正規表達式書,包含常用的語言的正則寫法如sed、perl、bash、awk、php、c#、java、javascript、python、ruby等等,《regular expressions cookbook, 2nd edition》,也有中文版的,大家可以到網絡上找找。
本文介紹nginx的重寫子產品,建立重寫規則向導,便于快捷正确的建立新的重寫規則,不求救于人。同時,如果想把apache轉換成nginx,重寫規則也是要改的咯。
<a target="_blank"></a>
nginx的重寫子產品是一個簡單的正規表達式比對與一個虛拟堆疊機結合。依賴于pcre庫,是以需要安裝pcre。根據相關變量重定向和選擇不同的配置,從一個location跳轉到另一個location,不過這樣的循環最多可以執行10次,超過後nginx将傳回500錯誤。同時,重寫子產品包含set指令,來建立新的變量并設其值,這在有些情景下非常有用的,如記錄條件辨別、傳遞參數到其他location、記錄做了什麼等等。
文法:break
預設值:none
使用字段:server, location, if
完成目前設定的重寫規則,停止執行其他的重寫規則。
文法:if (condition) { … }
使用字段:server, location
注意:盡量考慮使用try_files代替。判斷的條件可以有以下值:
一個變量的名稱:空字元傳“”或者一些“0”開始的字元串為false。
字元串比較:使用=或!=運算符
正規表達式比對:使用~(區分大小寫)和~*(不區分大小寫),取反運算!~和!~*。
檔案是否存在:使用-f和!-f操作符
目錄是否存在:使用-d和!-d操作符
檔案、目錄、符号連結是否存在:使用-e和!-e操作符
檔案是否可執行:使用-x和!-x操作符
文法:return code
停止處理并為用戶端傳回狀态碼。非标準的444狀态碼将關閉連接配接,不發送任何響應頭。可以使用的狀态碼有:204,400,402-406,408,410, 411, 413, 416與500-504。如果狀态碼附帶文字段落,該文本将被放置在響應主體。相反,如果狀态碼後面是一個url,該url将成為location頭補值。沒有狀态碼的url将被視為一個302狀态碼。
文法:rewrite regex replacement flag
按照相關的正規表達式與字元串修改uri,指令按照在配置檔案中出現的順序執行。可以在重寫指令後面添加标記。
注意:如果替換的字元串以http://開頭,請求将被重定向,并且不再執行多餘的rewrite指令。
尾部的标記(flag)可以是以下的值:
last – 停止處理重寫子產品指令,之後搜尋location與更改後的uri比對。
break – 完成重寫指令。
redirect – 傳回302臨時重定向,如果替換字段用http://開頭則被使用。
permanent – 傳回301永久重定向。
文法:rewrite_log on | off
預設值:rewrite_log off
變量:無
啟用時将在error log中記錄notice級别的重寫日志。
文法:set variable value
為給定的變量設定一個特定值。
文法:uninitialized_variable_warn on|off
預設值:uninitialized_variable_warn on
使用字段:http, server, location, if
控制是否記錄未初始化變量的警告資訊。
可以使用括号來捕獲,後續可以根據位置來将其引用,位置變量值取決于捕獲正規表達式中的順序,$1引用第一個括号中的值,$2引用第二個括号中的值,以此類推。如:
<code>^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$</code>
$1是兩個小寫字母組成的字元串,$2是由小寫字母和0到9的數字組成的5個字元的字元串,$3将是個檔案名,$4是png、jpg、gif中的其中一個。
請求被改寫。該uri可能包含正規表達式中的捕獲的位置參數或這個級别下的nginx任何配置變量。如:
<code>/data?file=$3.$4</code>
如果這個uri不比對nginx配置的任何location,那麼将給用戶端傳回301(永久重定向)或302(臨時重定向)的狀态碼來表示重定向類型。該狀态碼可以通過第三個參數來明确指定。
第三部分也就是尾部的标記(flag)。 last标記将導緻重寫後的uri搜尋比對nginx的其他location,最多可循環10次。如:
<code>rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4 last;</code>
break指令可以當做自身指令。如:
<code>if ($bwhog) {</code>
<code>limit_rate 300k;</code>
<code>break;</code>
<code>}</code>
另一個停止重寫子產品處理指令是return, 來控制主http子產品處理請求。 這意味着,nginx直接傳回資訊給用戶端,與error_page結合為用戶端呈現格式化的html頁面或激活不同的子產品來完成請求。如果狀态碼附帶文字段落,該文本将被放置在響應主體。相反,如果狀态碼後面是一個url,該url将成為location頭補值。沒有狀态碼的url将被視為一個302狀态碼。如:
<code>location = /image404.html {</code>
<code>return 404 "image not found\n";</code>
<code>http {</code>
<code># 定義image日志格式</code>
<code>log_format imagelog '[$time_local] ' $image_file ' ' $image_type ' ' $body_bytes_sent ' ' $status;</code>
<code># 開啟重寫日志</code>
<code>rewrite_log on;</code>
<code></code>
<code>server {</code>
<code>root /home/www;</code>
<code>location / {</code>
<code># 重寫規則資訊</code>
<code>error_log logs/rewrite.log notice;</code>
<code># 注意這裡要用‘’單引号引起來,避免{}</code>
<code>rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4;</code>
<code># 注意不能在上面這條規則後面加上“last”參數,否則下面的set指令不會執行</code>
<code>set $image_file $3;</code>
<code>set $image_type $4;</code>
<code>location /data {</code>
<code># 指定針對圖檔的日志格式,來分析圖檔類型和大小</code>
<code>access_log logs/images.log main;</code>
<code>root /data/images;</code>
<code># 應用前面定義的變量。判斷首先檔案在不在,不在再判斷目錄在不在,如果還不在就跳轉到最後一個url裡</code>
<code>try_files /$arg_file /image404.html;</code>
<code># 圖檔不存在傳回特定的資訊</code>
在接到要建立新的重寫規則時,要弄清楚需求是什麼樣的,再決定怎麼做。畢竟重寫也是耗資源的有效率之分的。 下面的這些問題有些幫助的:
你的url的模式是什麼樣的?
是否有一個以上的方法來實作?
是否需要捕獲url部分作為變量?
重定向到另一個web上可以看到我的規則?
是否要替換查詢的字元串參數?
檢查網站或應用程式布局,清楚url模式。啰嗦一句:我一而再再而三的強調,運維不能與開發脫節,運維要參與到開發當中。如果有不止一種方法實作,建立一個永久重定向。同時,定義一個重寫規範,來使網址清潔,還可以幫助網站更容易被找到。
執行個體1. 要将home目錄重定向到首頁面上,目錄結構如下:
<code>/</code>
<code>/home</code>
<code>/home/</code>
<code>/home/index</code>
<code>/home/index/</code>
<code>/index</code>
<code>/index.php</code>
<code>/index.php/</code>
重寫規則如下:
<code>rewrite ^/(home(/index)?|index(\.php)?)/?$ $scheme:</code>
<code>//$host/ permanent;</code>
指定$scheme和$host變量,因為要做一個永久重定向并希望nginx使用相同的參數來構造url。
執行個體2. 如果想分别記錄各個部分的url,可以使用正規表達式來捕獲uri,然後,給變量配置設定指定位置變量,見上面的執行個體。
執行個體3. 當重寫規則導緻内部重定向或訓示用戶端調用該規則本身被定義的location時,必須采取特殊的動作來避免重寫循環。如:在server配置段定義了一條規則帶上last标志,在引用location時,必須使用break标志。
<code>rewrite ^(/images)/(.*)\.(png|jpg|gif)$ $1/$3/$2.$3 last;</code>
<code>location /images/ {</code>
<code>rewrite ^(/images)/(.*)\.(png|jpg|gif)$ $1/$3/$2.$3 break;</code>
執行個體4. 作為重寫規則的一部分,傳遞新的查詢字元串參數是使用重寫規則的目标之一。 如:
<code>rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;</code>
nginx重寫規則說起來挺簡單的,做起來就難,重點在于正規表達式,同時,還需要考慮到nginx執行順序。
原文釋出時間:2015-04-09
本文來自雲栖合作夥伴“linux中國”