「Nginx是一款輕量級的HTTP服務器,採用事件驅動的異步非阻塞處理方式框架,這讓其具備極好的IO性能,時經常使用於服務端的反向代理和負載均衡。」javascript
做爲前端開發,即便沒用過Nginx,但必定據說過上面這句話。這句經典的話,基本構成了全部人對Nginx的第一印象。css
Nginx發佈於2004年,通過初期幾年的沉澱以後,迅速躥升爲「網紅」,成爲了當年互聯網技術圈最火的詞彙和技術。然而通過多年的發展,到如今,當年的網紅早已「過氣」。由於現在基本上全部的大型網站都搭建在Nginx之上,Nginx再也不是一個什麼新詞,而是互聯網網站搭建的必選技術之一。看到這裏,「HTTP服務器」、「事件驅動」、「異步非阻塞」以及Nginx的網紅經歷,是否是讓前端童鞋們想到了Nodejs?html
在工做上,因爲工做平臺和語言的緣由,對於大部分前端童鞋,更傾向於用Nodejs來搭建服務器,進而實現一些需求,對Nginx有自然的抗拒感。的確,Nginx中的絕大部分功能,若是單純的使用Node.js也能夠知足和實現。但實際上,Nginx和Node.js並不衝突,都有本身擅長的領域:Nginx更擅長於底層服務器端資源的處理(靜態資源處理轉發、反向代理,負載均衡等),Node.js更擅長於上層具體業務邏輯的處理。二者能夠實現完美組合,助力前端開發。前端
首章最後要說幾句。本文的目的是經過對Nginx的簡單介紹,來讓前端童靴瞭解其實經過Nginx能夠強有力地助力前端開發:徹底能夠把以前Node.js的一些工做放到Nginx上,而不是痛苦地在npm中找包或者造輪子。但實際上,Nginx種看似簡單的配置,實則學問深深。在Nginx實現一個一樣的功能,不一樣的配置編寫寫法,效率上可能差上好幾倍。而這些徹底是在創建在對Nginx原理的深刻理解和常年的配置運維經驗上,哪怕是大家公司的後端均可能對Nginx的瞭解並不深刻。若是真的想深刻學習Nginx,仍是找專業的SA或者PE請教吧。java
互聯網應用基本都基於CS基本結構,即client端和server端。代理其實就是在client端和真正的server端以前增長一層提供特定服務的服務器,即代理服務器。node
反向代理很差理解,正向代理你們總有用過,FQ工具其實就是一個正向代理工具。它會把
們訪問牆外服務器server的網頁請求,代理到一個能夠訪問該網站的代理服務器proxy,這個代理服務器proxy把牆外服務器server上的網頁內容獲取,再轉發給客戶。具體的流程以下圖。
nginx
歸納說:就是客戶端和代理服務器能夠直接互相訪問,屬於一個LAN(局域網);代理對用戶是非透明的,即用戶須要本身操做或者感知獲得本身的請求被髮送到代理服務器;代理服務器經過代理用戶端的請求來向域外服務器請求響應內容。git
反向代理則正好相反,先看流程圖圖。
github
在反向代理中(事實上,這種狀況基本發生在全部的大型網站的頁面請求中),客戶端發送的請求,想要訪問server服務器上的內容。但將被髮送到一個代理服務器proxy,這個代理服務器將把請求代理到和本身屬於同一個LAN下的內部服務器上,而用戶真正想得到的內容就儲存在這些內部服務器上。看到區別了嗎,這裏proxy服務器代理的並非客戶,而是服務器,即向外部客戶端提供了一個統一的代理入口,客戶端的請求,都先通過這個proxy服務器,至於在內網真正訪問哪臺服務器內容,由這個proxy去控制。通常代理是指代理客戶端,而這裏代理的對象是服務器,這就是「反向」這個詞的意思。Nginx就是來充當這個proxy的做用。
歸納說:就是代理服務器和真正server服務器能夠直接互相訪問,屬於一個LAN(服務器內網);代理對用戶是透明的,即無感知。不論加不加這個反向代理,用戶都是經過相同的請求進行的,且不須要任何額外的操做;代理服務器經過代理內部服務器接受域外客戶端的請求,並將請求發送到對應的內部服務器上。web
使用反向代理最主要的兩個緣由:
1)安全及權限。能夠看出,使用反向代理後,用戶端將沒法直接經過請求訪問真正的內容服務器,而必須首先經過Nginx。能夠經過在Nginx層上將危險或者沒有權限的請求內容過濾掉,從而保證了服務器的安全。
2)負載均衡。例如一個網站的內容被部署在若干臺服務器上,能夠把這些機子當作一個集羣,那麼Nginx能夠將接收到的客戶端請求「均勻地」分配到這個集羣中全部的服務器上(內部模塊提供了多種負載均衡算法),從而實現服務器壓力的負載均衡。此外,nginx還帶有健康檢查功能(服務器心跳檢查),會按期輪詢向集羣裏的全部服務器發送健康檢查請求,來檢查集羣中是否有服務器處於異常狀態,一旦發現某臺服務器異常,那麼在之後代理進來的客戶端請求都不會被髮送到該服務器上(直到後面的健康檢查發現該服務器恢復正常),從而保證客戶端訪問的穩定性。
下面的內容創建在對Nginx配置有基本認知的狀況下。若是沒有的話,請先從網上查閱資料(例如基本配置)作簡單瞭解。若是你想本地安裝Nginx,強烈建議採用源碼編譯安裝,這樣後續添加模塊更爲方便。
常常會遇到但願網站讓某些特定用戶的羣體(好比只讓公司內網)訪問,或者控制某個uri不讓人訪問。Nginx配置以下:
nginx location / { deny 192.168.1.100; allow 192.168.1.10/200; allow 10.110.50.16; deny all; }
複製代碼其實deny和allow是ngx_http_access_module模塊(已內置)中的語法。採用的是從上到下匹配方式,匹配到就跳出再也不繼續匹配。上述配置的意思就是,首先禁止192.168.1.100訪問,而後容許192.168.1.10-200 ip段內的訪問(排除192.168.1.100),同時容許10.110.50.16這個單獨ip的訪問,剩下未匹配到的所有禁止訪問。實際生產中,常常和ngx_http_geo_module模塊(能夠更好地管理ip地址表,已內置)配合使用。
在衆多的解決跨域方式中, 都不可避免的都須要服務端進行支持, 使用Nginx能夠純前端解決請求跨域問題。 特別是在先後端分離調試時, 常常須要在本地起前端工程, 接口但願拉取服務端的實際數據而不是本地的mock。 而若是本地程序直接訪問遠程接口, 確定會遇到跨域問題。如今前端成熟的作法,通常是把node proxy server集成進來。事實上,用Nginx一樣能夠解決問題,甚至能夠應用於線上。
本地起一個nginx server。server_name是mysite-base.com,好比如今須要請求線上www.kaola.com域下的線上接口 https://www.kaola.com/getPCBannerList.html 的數據,當在頁面裏直接請求,瀏覽器會報錯:
爲了繞開瀏覽器的跨域安全限制,如今須要將請求的域名改爲mysite-base.com。同時約定一個url規則來代表代理請求的身份,而後Nginx經過匹配該規則,將請求代理回原來的域。Nginx配置以下:
#請求跨域,這裏約定代理請求url path是以/apis/開頭 location ^~/apis/ { # 這裏重寫了請求,將正則匹配中的第一個()中$1的path,拼接到真正的請求後面,並用break中止後續匹配 rewrite ^/apis/(.*)$ /$1 break; proxy_pass https://www.kaola.com/; }
在頁面代碼裏,把請求url換成http://mysite-base.com/apis/getPCBannerList.html 。這樣就能夠正常請求到數據。
這樣實際上是經過nginx,用相似於hack的方式規避掉了瀏覽器跨域限制,實現了跨域訪問。
如今不少網站都存在PC站和H5站兩個站點,所以根據用戶的瀏覽環境自動切換站點是很常見的需求。Nginx能夠經過內置變量$http_user_agent,獲取到請求客戶端的userAgent,從而知道用戶處於移動端仍是PC,進而控制重定向到H5站仍是PC站。
以筆者本地爲例,pc端站點是mysite-base.com,H5端是mysite-base-H5.com。pc端Nginx配置以下:
location / { # 移動、pc設備適配 if ($http_user_agent ~* '(Android|webOS|iPhone|iPod|BlackBerry)') { set $mobile_request '1'; } if ($mobile_request = '1') { rewrite ^.+ http://mysite-base-H5.com; } }
這樣當瀏覽設備切換成移動模式,再次刷新頁面後,站點被自動切換到H5站。以下:
前端性能優化中重要一點就是儘可能減小http資源請求的數量。經過nginx-http-concat模塊(淘寶開發的第三方模塊,須要單獨安裝)用一種特殊的請求url規則(例子:example.com/??1.js,2.js,3.js ),前端能夠將多個資源的請求合併成一個請求,後臺Nginx會獲取各個資源並拼接成一個結果進行返回。例如上面的例子經過一個請求將1.js,2.js,3js三個js資源合併成一個請求,減小了瀏覽器開銷。
本地server mysite-base.com爲例,static/js文件夾下有三個文件,文件內容很簡單,分別爲:
Nginx配置以下:
# js資源http-concat # nginx-http-concat模塊的參數遠不止下面三個,剩下的請查閱文檔 location /static/js/ { concat on; # 是否打開資源合併開關 concat_types application/javascript; # 容許合併的資源類型 concat_unique off; # 是否容許合併不一樣類型的資源 concat_max_files 5; # 容許合併的最大資源數目 }
當在瀏覽器請求http://mysite-base.com/static/js/??a.js,b.js,c.js 時,發現三個js被合併成一個返回了,以下圖:
在前端開發中,常常須要不一樣尺寸的圖片。如今的雲儲存基本對圖片都提供有處理服務(通常是經過在圖片連接上加參數)。其實用Nginx,能夠經過幾十行配置,搭建出一個屬於本身的本地圖片處理服務,徹底可以知足平常對圖片的裁剪/縮放/旋轉/圖片品質等處理需求。要用到ngx_http_image_filter_module模塊。這個模塊是非基本模塊,須要安裝。
下面是圖片縮放功能部分的Nginx配置:
# 圖片縮放處理 # 這裏約定的圖片處理url格式:以 mysite-base.com/img/路徑訪問 location ~* /img/(.+)$ { alias /Users/cc/Desktop/server/static/image/$1; #圖片服務端儲存地址 set $width -; #圖片寬度默認值 set $height -; #圖片高度默認值 if ($arg_width != "") { set $width $arg_width; } if ($arg_height != "") { set $height $arg_height; } image_filter resize $width $height; #設置圖片寬高 image_filter_buffer 10M; #設置Nginx讀取圖片的最大buffer。 image_filter_interlace on; #是否開啓圖片圖像隔行掃描 error_page 415 = 415.png; #圖片處理錯誤提示圖,例如縮放參數不是數字 }
這裏只是最基本的配置。此外,能夠經過proxy_cache配置Nginx緩存,避免每次請求都從新處理圖片,減小Nginx服務器處理壓力;還以能夠經過和nginx-upload-module一塊兒使用加入圖片上傳的功能等。
Nginx能夠經過向頁面底部或者頂部插入額外的css和js文件,從而實現修改頁面內容。這個功能須要額外模塊的支持,例如:nginx_http_footer_filter或者ngx_http_addition_module (都須要安裝)。
工做中,常常須要切換各類測試環境,而經過switchhosts等工具切換後,有時還須要清理瀏覽器dns緩存。能夠經過頁面內容修改+Nginx反向代理來實現輕鬆快捷的環境切換。
這裏首先在本地編寫一段js代碼(switchhost.js),裏面的邏輯是:在頁面插入hosts切換菜單以及點擊具體某個環境時,將該host的ip和hostname儲存在cookie中,最後刷新頁面;接着編寫一段css代碼(switchhost.css)用來設置該hosts切換菜單的樣式。
而後Nginx腳本配置:
server { listen 80; listen 443 ssl; expires -1; # 想要代理的域名 server_name m-element.kaola.com; set $root /Users/cc/Desktop/server; charset utf-8; ssl_certificate /usr/local/etc/nginx/m-element.kaola.com.crt; ssl_certificate_key /usr/local/etc/nginx/m-element.kaola.com.key; # 設置默認$switch_host,通常默認爲線上host,這裏的1.1.1.1隨便寫的 set $switch_host '1.1.1.1'; # 設置默認$switch_hostname,通常默認爲線上'online' set $switch_hostname ''; # 從cookie中獲取環境ip if ($http_cookie ~* "switch_host=(.+?)(?=;|$)") { set $switch_host $1; } # 從cookie中獲取環境名 if ($http_cookie ~* "switch_hostname=(.+?)(?=;|$)") { set $switch_hostname $1; } location / { expires -1; index index.html; proxy_set_header Host $host; #把html頁面的gzip壓縮去掉,否則sub_filter沒法替換內容 proxy_set_header Accept-Encoding ''; #反向代理到實際服務器ip proxy_pass http://$switch_host:80; #所有替換 sub_filter_once off; #ngx_http_addition_module模塊替換內容。 # 這裏在頭部插入一段css,內容是hosts切換菜單的css樣式 sub_filter '</head>' '</head><link rel="stylesheet" type="text/css" media="screen" href="/local/switchhost.css" />'; #將頁面中的'網易考拉'文字後面加上環境名,便於開發識別目前環境 sub_filter '網易考拉' '網易考拉:${switch_hostname}'; #這裏用了另外一個模塊nginx_http_footer_filter,其實上面的模塊就行,只是爲了展現用法 # 最後插入一段js,內容是hosts切換菜單的js邏輯 set $injected '<script language="javascript" src="/local/switchhost.js"></script>'; footer '${injected}'; } # 對於/local/請求,優先匹配本地文件 # 因此上面的/local/switchhost.css,/local/switchhost.js會從本地獲取 location ^~ /local/ { root $root; } }
這個功能其實爲Nginx在前端開發中的應用提供了無限可能。例如,能夠經過區分本地、測試和線上環境,爲本地/測試環境頁面增長不少開發輔助功能:給本地頁面加一個常駐二維碼便於手機端掃碼調試;本地調試線上頁面時,在js文件底部塞入sourceMappingURL,便於本地debug等等。
上述只是經過一些簡單的小例子,但願可以引發廣大前端童靴對Niginx的興趣。事實上,Nginx不只僅侷限於這些微小的工做,在實際生產中做用其實更加巨大。對於有志於「大前端」的童靴,瞭解和熟悉Nginx絕對是必修技能之一。