<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          難道只有我懂Nginx/OpenResty詳解,Nginx的rewrite模塊指令?

          共 7661字,需瀏覽 16分鐘

           ·

          2022-03-02 11:27


          Nginx的rewrite模塊指令

          Nginx的rewrite模塊即ngx_http_rewrite_module標(biāo)準(zhǔn)模塊,主要功能是重寫(xiě)請(qǐng)求URI,也是Nginx默認(rèn)安裝的模塊。rewrite模塊會(huì)根據(jù)PCRE正則匹配重寫(xiě)URI,然后根據(jù)指令參數(shù)或者發(fā)起內(nèi)部跳轉(zhuǎn)再一次進(jìn)行l(wèi)ocation匹配,或者直接進(jìn)行30x重定向返回客戶端。

          rewrite模塊的指令就是一門(mén)微型的編程語(yǔ)言,包含set、rewrite、break、if、return等一系列指令。


          set指令

          set指令是由ngx_http_rewrite_module標(biāo)準(zhǔn)模塊提供的,用于向變量存放值。在Nginx配置文件中,變量只能存放一種類型的值,因?yàn)橹淮嬖谝环N類型的值,那就是字符串。

          set指令的配置項(xiàng)格式如下:

          set $variable value;

          注意:在Nginx配置文件中,變量定義和使用都要以$開(kāi)頭。Nginx變量名前面有一個(gè)$符號(hào),這是記法上的要求。所有的Nginx變量在引用時(shí)必須帶上$前綴。另外,Nginx變量不能與Nginx服務(wù)器預(yù)設(shè)的全局變量同名。比如,我們的nginx.conf文件中有下面這一行配置:

          set $a "hello world";

          上面的語(yǔ)句中,set配置指令對(duì)變量$a進(jìn)行了賦值操作,把字符串hello world賦給了它。也可以直接把變量嵌入字符串常量中以構(gòu)造出新的字符串:

          set $a "foo";
          set $b "$a, $a";

          這個(gè)例子通過(guò)前面定義的變量$a的值來(lái)構(gòu)造變量$b的值,于是這兩條指令順序執(zhí)行完之后,$a的值是"foo",而$b的值則是"foo,foo"。把變量嵌入字符串常量中以構(gòu)造出新的字符串,這種技術(shù)在Linux Shell腳本中常常用到,并且被稱為“變量插值”(VariableInterpolation)。

          set指令不僅有賦值的功能,還有創(chuàng)建Nginx變量的副作用,即當(dāng)作為賦值對(duì)象的變量尚不存在時(shí),它會(huì)自動(dòng)創(chuàng)建該變量。比如在上面這個(gè)例子中,若$a這個(gè)變量尚未創(chuàng)建,則set指令會(huì)自動(dòng)創(chuàng)建$a這個(gè)用戶變量。

          Nginx變量一旦創(chuàng)建,其變量名的可見(jiàn)范圍就是整個(gè)Nginx配置,甚至可以跨越不同虛擬主機(jī)的server配置塊。但是,對(duì)于每個(gè)請(qǐng)求,所有變量都有一份獨(dú)立的副本,或者說(shuō)都有各變量用來(lái)存放值的容器的獨(dú)立副本,彼此互不干擾。Nginx變量的生命期是不可能跨越請(qǐng)求邊界的。

          ?rewrite指令

          rewrite指令是由ngx_http_rewrite_module標(biāo)準(zhǔn)模塊提供的,主要功能是改寫(xiě)請(qǐng)求URI。rewrite指令的格式如下:

          rewrite regrex replacement [flag];

          如果regrex匹配URI,URI就會(huì)被替換成replacement的計(jì)算結(jié)果,replacement一般是一個(gè)“變量插值”表達(dá)式,其計(jì)算之后的字符串就是新的URI。

          下面的例子有兩個(gè)重新配置項(xiàng),具體如下:

          location /download/ {
          rewrite ^/download/(.*)/video/(.*)$ /view/$1/mp3/$2.mp3 last;
          rewrite ^/download/(.*)/audio/(.*)*$ /view/$1/mp3/$2.rmvb last;
          return 404;
          }
          location /view {
          echo "uri: $uri ";
          }

          在瀏覽器中請(qǐng)求
          http://crazydemo.com/download/1/video/10
          ,地址發(fā)生了重寫(xiě),并且發(fā)生了location的跳轉(zhuǎn),結(jié)果如圖7-17所示。

          圖7-17 輸出結(jié)果

          在這個(gè)演示例子中,replacement中的占位變量$1、$2的值是指令參數(shù)regrex正則表達(dá)式從原始URI中匹配出來(lái)的子字符串,也叫正則捕獲組,編號(hào)從1開(kāi)始。

          rewrite指令可以使用的上下文為:server、location、if inlocation。

          如果rewrite同一個(gè)上下文中有多個(gè)這樣的rewrite重新指令,匹配就會(huì)依照rewrite指令出現(xiàn)的順序先后依次進(jìn)行下去,匹配成功之后并不會(huì)終止,而是繼續(xù)往下匹配,直到返回最后一個(gè)匹配的為止。如果想要中途中止,不再繼續(xù)往下匹配,可以使用第3個(gè)指令參數(shù)flag。flag參數(shù)的值有l(wèi)ast、break、redirect、permanent。

          如果flag參數(shù)使用last值,并且匹配成功,那么停止處理任何rewrite相關(guān)的指令,立即用計(jì)算后的新URI開(kāi)始下一輪的location匹配和跳轉(zhuǎn)。前面的例子使用的就是last參數(shù)值。

          如果flag參數(shù)使用break值,就如同break指令的字面意思一樣,停止處理任何rewrite的相關(guān)指令,但是不進(jìn)行l(wèi)ocation跳轉(zhuǎn)。

          將上面的rewrite例子中的last參數(shù)值改成break,代碼如下:

          location /view {
          echo " view : $uri ";
          }
          location /download_break/ {
          rewrite ^/download_break/(.*)/video/(.*)$ /view/$1/mp3/$2.mp3 break;
          rewrite ^/download_break/(.*)/audio/(.*)*$ /view/$1/mp3/$2.rmvb break;
          echo " download_break new uri : $uri ";
          }

          在瀏覽器中請(qǐng)求
          http://crazydemo.com/download_break/1/video/10
          ,地址發(fā)生了重寫(xiě),但是location并沒(méi)有跳轉(zhuǎn),而是直接結(jié)束了,結(jié)果如圖7-18所示。

          圖7-18 顯示結(jié)果

          在location上下文中,last和break是有區(qū)別的:last其實(shí)就相當(dāng)于一個(gè)新的URL,Nginx進(jìn)行了一次新的location匹配,通過(guò)last獲得一個(gè)可以轉(zhuǎn)到其他location配置中處理的機(jī)會(huì)(內(nèi)部的重定向);而break在一個(gè)location中將原來(lái)的URL(包括URI和args)改寫(xiě)之后,再繼續(xù)進(jìn)行后面的處理,這個(gè)重寫(xiě)之后的請(qǐng)求始終都是在同一個(gè)location上下文中,并沒(méi)有發(fā)生內(nèi)部跳轉(zhuǎn)。

          這里要注意:last和break的區(qū)別僅僅發(fā)生在location上下文中;如果發(fā)生在server上下文,那么last和break的作用是一樣的。

          還要注意:在location上下文中的rewrite指令使用last指令參數(shù)會(huì)再次以新的URI重新發(fā)起內(nèi)部重定向,再次進(jìn)行l(wèi)ocation匹配,而新的URI極有可能和舊的URI一樣再次匹配到相同的目標(biāo)location中,這樣死循環(huán)就發(fā)生了。當(dāng)循環(huán)到第10次時(shí),Nginx會(huì)終止這樣無(wú)意義的循環(huán)并返回500錯(cuò)誤。這一點(diǎn)需要特別注意。

          如果rewrite指令使用的flag參數(shù)的值是permanent,就表示進(jìn)行外部重定向,也就是在客戶端進(jìn)行重定向。此時(shí),服務(wù)器將新URI地址返回給客戶端瀏覽器,并且返回301(永久重定向的響應(yīng)碼)給客戶端??蛻舳藢⑹褂眯碌闹囟ㄏ虻刂吩侔l(fā)起一次遠(yuǎn)程請(qǐng)求。

          永久重定向permanent的使用示例如下:

           #rewrite指令permanent參數(shù)演示
          location /download_permanent/ {
          rewrite ^/download_permanent/(.*)/video/(.*)$ /view/$1/mp3/$2.mp3 permanent;
          rewrite ^/download_permanent/(.*)/audio/(.*)*$ /view/$1/mp3/$2.rmvb permanent; return 404;
          }

          在瀏覽器中請(qǐng)求
          http://crazydemo.com/download_permanent/1/video/10
          ,輸出的結(jié)果如圖7-19所示。

          圖7-19 輸出的結(jié)果

          從以上結(jié)果可以看出,永久重定向有兩個(gè)比較大的特點(diǎn):

          (1)瀏覽器的地址欄地址變成了重定向地址

          http://crazydemo.com/view/1/mp3/10.mp3。

          (2)從Fiddler抓包工具可以看到,第一個(gè)請(qǐng)求地址的響應(yīng)狀態(tài)碼為301,如圖7-20所示。

          圖7-20 永久重定向的響應(yīng)碼示意圖

          外部重定向與內(nèi)部重定向是有本質(zhì)區(qū)別的。從數(shù)量上說(shuō),外部重定向有兩次請(qǐng)求,內(nèi)部重定向只有一次請(qǐng)求。通過(guò)上面的幾個(gè)示例,大家應(yīng)該體會(huì)得相當(dāng)深刻了。

          如果rewrite指令使用的flag參數(shù)的值是redirect,就表示進(jìn)行外部重定向,表現(xiàn)的行為與permanent參數(shù)值完全一樣,不同的是返回302(臨時(shí)重定向的響應(yīng)碼)給客戶端。

          有關(guān)redirect參數(shù)值的實(shí)例這里不進(jìn)行演示,大家可自行下載和運(yùn)行本文的源碼并細(xì)細(xì)體會(huì)。

          rewrite能夠利用正則捕獲組設(shè)置變量,作為實(shí)驗(yàn),我們可以在Nginx的配置文件中加入這么一條location規(guī)則:

          location /capture_demo {
          rewrite ^/capture_demo/(.*)/video/(.*)$ /view/$1/mp3/$2.mp3 break;
          rewrite ^/capture_demo/(.*)/audio/(.*)*$ /view/$1/mp3/$2.rmvb break;
          捕獲組
          捕獲組 echo " 捕獲組1:$1;捕獲組2:$2";
          }

          在瀏覽器中請(qǐng)求
          http://crazydemo.com/capture_demo/group1/video/group2
          ,輸出的結(jié)果如圖7-21所示。

          圖7-21 輸出的結(jié)果

          if條件指令

          if條件指令配置項(xiàng)的格式如下:

          if (condition) {...}

          當(dāng)if條件滿足時(shí),執(zhí)行配置塊中的配置指令。if的配置塊相當(dāng)于引入了一個(gè)新的上下文作用域。if條件指令適用于server和location兩個(gè)上下文。

          condition條件表達(dá)式可以用到一系列比較操作符,大致如下:

          (1)==:相等。

          (2)!=:不相等。

          (3)~:區(qū)分字母大小寫(xiě)模式匹配。

          (4)~*:不區(qū)分字母大小寫(xiě)模式匹配。

          (5)還有其他幾個(gè)專用比較符號(hào),比如判斷文件及目錄是否存在的符號(hào),等等。

          下面是一個(gè)簡(jiǎn)單的演示程序,根據(jù)內(nèi)置變量$http_user_agent的值判斷客戶端的類型,代碼如下:

          #if指令的演示程序
          location /if_demo {
          if ($http_user_agent ~*"Firefox") { #匹配Firefox瀏覽器
          return 403;
          }

          匹配谷歌瀏覽器

           if ($http_user_agent ~*"Chrome") { #匹配Chrome谷歌瀏覽器
          return 301;
          }
          if ($http_user_agent ~*"iphone") { #匹配iPhone手機(jī)
          return 302;
          }
          if ($http_user_agent ~*"android") { #匹配安卓手機(jī)
          return 404;
          }
          return 405; #其他瀏覽器默認(rèn)訪問(wèn)規(guī)則
          }

          在火狐瀏覽器中訪問(wèn)
          http://crazydemo.com/if_demo
          ,結(jié)果如圖7-22所示。

          圖7-22 火狐瀏覽器的訪問(wèn)結(jié)果

          在谷歌瀏覽器中訪問(wèn)
          http://crazydemo.com/if_demo
          ,結(jié)果如圖7-23所示。

          圖7-23 谷歌瀏覽器的訪問(wèn)結(jié)果

          在演示代碼中使用到了return指令,用于返回HTTP的狀態(tài)碼。

          return指令會(huì)停止同一個(gè)作用域的剩余指令處理,并返回給客戶端指定的響應(yīng)碼。

          return指令可以用于server、location、if上下文中,執(zhí)行階段是rewrite階段。其指令的格式如下:

          #格式一:返回響應(yīng)的狀態(tài)碼和提示文字,提示文字可選
          return code [text];
          #格式二:返回響應(yīng)的重定向狀態(tài)碼(如301)和重定向URL
          return code URL;
          #格式三:返回響應(yīng)的重定向URL,默認(rèn)的返回狀態(tài)碼是臨時(shí)重定向302
          return URL;

          add_header指令

          response header一般是以key:value的形式,例如Content-Encoding:

          gzip、Cache-Control:no-store,設(shè)置的命令如下:

          add_header Cache-Control no-store
          add_header Content-Encoding gzip

          但是,有一個(gè)十分常用的response header為Content-Type,可以在它設(shè)置了類型的同時(shí)指定charset,例如text/html;charset=utf-8,由于其存在分號(hào),而分號(hào)在配置文件中作為結(jié)束符,因此在配置時(shí)需要用引號(hào)把其引起來(lái),配置如下:

          add_header Content-Type 'text/html; charset=utf-8';

          另外,由于沒(méi)有單獨(dú)設(shè)置charset的key,因此要設(shè)置響應(yīng)的charset就需要使用Content-Type來(lái)指定charset。

          使用AJAX進(jìn)行跨域請(qǐng)求時(shí),瀏覽器會(huì)向跨域資源的服務(wù)端發(fā)送一個(gè)OPTIONS請(qǐng)求,用于判斷實(shí)際請(qǐng)求是否安全或者判斷服務(wù)端是否允許跨域訪問(wèn),這種請(qǐng)求也叫作預(yù)檢請(qǐng)求。跨域訪問(wèn)的預(yù)檢請(qǐng)求是瀏覽器自動(dòng)發(fā)出的,用戶程序往往不知情,如果不進(jìn)行特別的配置,那么客戶端發(fā)出一次請(qǐng)求,在服務(wù)端往往會(huì)收到兩個(gè)請(qǐng)求;一個(gè)是預(yù)檢請(qǐng)求;另一個(gè)是正式的請(qǐng)求。后端的服務(wù)器(PHP或者Tomcat)如果不經(jīng)過(guò)特殊的過(guò)濾,那么很容易將OPTIONS預(yù)檢請(qǐng)求當(dāng)成正式的數(shù)據(jù)請(qǐng)求。

          對(duì)于客戶端而言,只有預(yù)檢請(qǐng)求返回成功,客戶端才開(kāi)始正式請(qǐng)求。在實(shí)際的使用場(chǎng)景中,預(yù)檢請(qǐng)求比較影響性能,用戶往往會(huì)有兩倍請(qǐng)求的感覺(jué),所以一般會(huì)在Nginx代理服務(wù)端對(duì)預(yù)檢請(qǐng)求進(jìn)行提前攔截,同時(shí)對(duì)預(yù)檢請(qǐng)求設(shè)置比較長(zhǎng)時(shí)間的有效期。

          upstream zuul {
          #server 192.168.233.1:7799;
          server "192.168.233.128:7799";
          keepalive 1000;
          }
          server {
          listen 80;
          server_name nginx.server *.nginx.server;
          default_type 'text/html';
          charset utf-8;
          #轉(zhuǎn)發(fā)到上游服務(wù)器,但是 'OPTIONS' 請(qǐng)求直接返回空
          location / {
          if ($request_method = 'OPTIONS') {
          add_header Access-Control-Max-Age 1728000;
          add_header Access-Control-Allow-Origin *;
          add_header Access-Control-Allow-Credentials true;
          add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
          add_header Access-Control-Allow-Headers 'Keep-Alive,User-Agent,X-Requested-With,\
          If-Modified-Since,Cache-Control,Content-Type,token'
          ;
          return 204;
          }
          proxy_pass http://zuul/ ;
          }
          }

          配置Nginx,加入Access-Control-Max-Age請(qǐng)求頭,用來(lái)指定本次預(yù)檢請(qǐng)求的有效期,單位為秒。上面結(jié)果中的有效期是20天(1 728 000秒),即允許緩存該條回應(yīng)1 728 000秒,在此期間客戶端不用發(fā)出另一條預(yù)檢請(qǐng)求。

          ?指令的執(zhí)行順序

          大多數(shù)Nginx新手都會(huì)頻繁遇到這樣一個(gè)困惑:當(dāng)同一個(gè)location配置塊使用了多個(gè)Nginx模塊的配置指令時(shí),這些指令的執(zhí)行順序很可能會(huì)跟它們的書(shū)寫(xiě)順序大相徑庭?,F(xiàn)在就來(lái)看這樣一個(gè)令人困惑的例子:

          location /sequence_demo_1 {
          set $a foo;
          echo $a;
          set $a bar;
          echo $a;
          }

          上面的代碼先給變量$a賦值foo,隨后輸出,再給變量$a賦值bar,隨后輸出。如果這是一段Java代碼,毫無(wú)疑問(wèn),最終的輸出結(jié)果一定為“foo bar”。然而不幸的是,事實(shí)并非如此,在瀏覽器中訪問(wèn)
          http://crazydemo.com/sequence_demo_1
          ,結(jié)果如圖7-24所示。

          圖7-24 輸出的結(jié)果

          為什么出現(xiàn)了這種不合常理的現(xiàn)象呢?

          前面講到,Nginx的請(qǐng)求處理階段共有11個(gè),分別是post-read、server-rewrite、find-config、rewrite、post-rewrite、preaccess、access、post-access、try-files、content以及l(fā)og。其中3個(gè)比較常見(jiàn)的按照?qǐng)?zhí)行時(shí)的先后順序依次是rewrite階段、access階段以及content階段。

          Nginx的配置指令一般只會(huì)注冊(cè)并運(yùn)行在其中的某一個(gè)處理階段,比如set指令就是在rewrite階段運(yùn)行的,而echo指令只會(huì)在content階段運(yùn)行。在一次請(qǐng)求處理流程中,rewrite階段總是在content階段之前執(zhí)行。因此,屬于rewrite階段的配置指令(示例中的set)總是會(huì)無(wú)條件地在content階段的配置指令(示例中的echo)之前執(zhí)行,即便是echo配置項(xiàng)出現(xiàn)在set配置項(xiàng)的前面。

          上面例子中的指令按照請(qǐng)求處理階段的先后次序排序,實(shí)際的執(zhí)行次序如下:

          location /sequence_demo_1 {
          #rewrite階段的配置指令,執(zhí)行在前面
          set $a foo;
          set $a bar;
          #content階段的配置指令,執(zhí)行在后面
          echo $a;
          echo $a;
          }

          所以,輸出的結(jié)果就是bar bar了。

          本文給大家講解的內(nèi)容是Nginx/OpenResty詳解,Nginx的rewrite模塊指令

          1. 下篇文章給大家講解的是 Nginx/OpenResty詳解,反向代理與負(fù)載均衡配置;

          2. 覺(jué)得文章不錯(cuò)的朋友可以轉(zhuǎn)發(fā)此文關(guān)注小編;

          3. 感謝大家的支持!

          本文就是愿天堂沒(méi)有BUG給大家分享的內(nèi)容,大家有收獲的話可以分享下,想學(xué)習(xí)更多的話可以到微信公眾號(hào)里找我,我等你哦。



          瀏覽 72
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  丝袜乱伦超碰 | 西西人体WW大胆无码 | 日韩高清无码专区 | 美日韩无码 | 性大香蕉|