Nginx基本介紹+跨域解決方案

語言: CN / TW / HK

作者:鯨騰 FE 來源:恆生LIGHT雲社群

Nginx簡介

Nginx 是一款由俄羅斯的程式設計師 Igor Sysoev 所開發的高效能的 http 伺服器/反向代理伺服器及電子郵件(IMAP/POP3)代理伺服器,它的主要功能有:

  • 反向代理
  • 負載均衡
  • HTTP 伺服器

目前大部分執行的 Nginx 伺服器都在使用其負載均衡的功能作為服務叢集的系統架構。

功能說明

在上文中介紹了三種 Nginx 的主要功能,下面來講講 具體每個功能的作用。

一、反向代理(Reverse Proxy)

介紹反向代理前,我們先理解下正向代理的概念。

打個比方,你準備去看周杰倫的巡演,但是發現官方渠道的票已經賣完了,所以你只好託你神通廣大的朋友A去內部購票,你如願以償地得到了這張門票。在這個過程中,朋友A就起到了一個正向代理的作用,即代理了客戶端(你)去向服務端(售票方)發請求,但服務端(售票方)並不知道源頭是誰發起的請求,只知道是代理服務(朋友A)向自己請求的。

由這個例子,我們再去理解下反向代理,比如我們經常接到10086或者10000的電話,但是每次打過來的人都不一樣,這是因為10086是中國移動的總機號,分機打給使用者的時候,都是通過總機代理顯示的號碼,這個時候客戶端(你)無法知道是誰發起的請求,只知道是代理服務(總機)向自己請求的。

而官方的解釋說明就是,反向代理方式是指以代理伺服器來接受 Internet 上 的連線請求,然後將請求轉發給內部網路上的伺服器,並將從伺服器上得到的結果返回給 Internet 上請求連線的客戶端,此時代理伺服器對外就表現為一個反向代理伺服器。

下面貼一段簡單實現反向代理的 Nginx 配置程式碼:

server {  
    listen       80;                                                   
    server_name  localhost;                                         
    client_max_body_size 1024M;
  
    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host:$server_port;
    }
}

其中的 http://localhost:8080 就是反代理的目標服務端,80是 Nginx 暴露給客戶端訪問的埠。

二、負載均衡(Load Balance**)**

負載均衡,顧名思義,就是將服務負載均衡地分攤到多個伺服器單元上執行,來提高網站、應用等服務的效能和可靠性。 下面我們來對比一下兩個系統拓撲,首先是未設計負載均衡的拓撲:

1.png

下面是設計了負載均衡的拓撲:

未命名繪圖2.png

從圖二可以看到,使用者訪問負載均衡器,再由負載均衡器將請求轉發給後端伺服器,在這種情況下,服務C故障後,使用者訪問負載會分配到服務A和服務B中,避免了系統崩潰,如果這種故障出現在圖一中,該系統一定會會直接崩潰。

負載均衡演算法

負載均衡演算法決定了後端的哪些健康伺服器會被選中。幾個常用的演算法:

  • Round Robin(輪詢):為第一個請求選擇列表中的第一個伺服器,然後按順序向下移動列表直到結尾,然後迴圈。
  • Least Connections(最小連線):先選擇連線數最少的伺服器,在普遍會話較長的情況下推薦使用。
  • Source:根據請求源的 IP 的雜湊(hash)來選擇要轉發的伺服器。這種方式可以一定程度上保證特定使用者能連線到相同的伺服器。

如果你的應用需要處理狀態而要求使用者能連線到和之前相同的伺服器。可以通過 Source 演算法基於客戶端的 IP 資訊建立關聯,或者使用粘性會話(sticky sessions)。

負載均衡同時需要配合反向代理功能才能發揮其作用。

三、HTTP伺服器

除了以上兩大功能外,Nginx也可以作為靜態資源伺服器使用,例如沒有使用 SSR(Server Side Render)的純前端資源,就可以依託Nginx來實現資源託管。 下面看一段實現靜態資源伺服器的配置:

server {
    listen       80;                                                 
    server_name  localhost;                                       
    client_max_body_size 1024M;
  
    location / {
        root   e:\wwwroot;
        index  index.html;
    }
}

root 配置就是具體資源存放的根目錄,index 配置的則是訪問根目錄時預設的檔案。

動靜分離

動靜分離也是Nginx作為Http伺服器使用的一個重要概念,要搞清楚動靜分離,首先要弄明白什麼是動態資源,什麼是靜態資源:

  • 動態資源:需要從伺服器中實時獲取的資源內容,如 JSP, SSR 渲染頁面等,不同時間訪問,資源內容會發生變化。
  • 靜態資源:如 JS、CSS、Img 等,不同時間訪問,資源內容不會發生變化。

由於Nginx可以作為靜態資源伺服器,但無法承載動態資源,因此出現需要動靜分離的場景時,我們需要拆分靜態、動態資源的訪問策略:

upstream test{  
    server localhost:8080;  
    server localhost:8081;  
}   

server {  
    listen       80;  
    server_name  localhost;  
  
    location / {  
        root   e:\wwwroot;  
        index  index.html;  
    }  
  
    # 所有靜態請求都由nginx處理,存放目錄為html  
    location ~ \.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {  
        root    e:\wwwroot;  
    }  
  
    # 所有動態請求都轉發給tomcat處理  
    location ~ \.(jsp|do)$ {  
        proxy_pass  http://test; 
    }  
  
    error_page   500 502 503 504  /50x.html;  
    location = /50x.html {  
        root   e:\wwwroot;  
    }  
}  

從這段配置可以大概理解到,當客戶端訪問不同型別的資源時,Nginx 會自動按照型別分配給自己的靜態資源服務或者是遠端的動態資源服務上,這樣就能滿足一個完整的資源伺服器的功能了。

配置介紹

一、基本介紹

說完 Nginx 的功能,我們來簡單進一步介紹下 Nginx 的配置檔案。作為前端人員來講,使用 Nginx 基本上就是修改配置 -> 啟動/熱重啟 Nginx,就能搞定大部分日常和 Nginx 相關的工作了。

這裡我們看下一份 Nginx 的預設配置,即安裝 Nginx 後,預設的 nginx.conf 檔案的內容:


#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
  
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
  
    #access_log  logs/access.log  main;
  
    sendfile        on;
    #tcp_nopush     on;
  
    #keepalive_timeout  0;
    keepalive_timeout  65;
  
    #gzip  on;
  
    server {
        listen       80;
        server_name  localhost;
  
        #charset koi8-r;
  
        #access_log  logs/host.access.log  main;
  
        location / {
            root   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   html;
        }
  
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}
  
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}
  
        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }
  
  
    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;
  
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
  
  
    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;
  
    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;
  
    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;
  
    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;
  
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
  
}

對應的結構大致是:

...              #全域性塊

events {         #events塊
    ...
}

http      #http塊
{
    ...   #http全域性塊
        server        #server塊
        { 
        ...       #server全域性塊
            location [PATTERN]   #location塊
            {
            ...
        }
        location [PATTERN] 
            {
            ...
        }
    }
    server
        {
        ...
    }
    ...     #http全域性塊
}

以上幾個程式碼塊對應功能是:

  • 全域性塊:配置影響 Nginx 全域性的指令。一般有執行 Nginx 伺服器的使用者組,Nginx 程序 pid 存放路徑,日誌存放路徑,配置檔案引入,允許生成 worker process 數等。
  • events塊:配置影響 Nginx 伺服器或與使用者的網路連線。有每個程序的最大連線數,選取哪種事件驅動模型處理連線請求,是否允許同時接受多個網路連線,開啟多個網路連線序列化等。
  • http塊:可以巢狀多個 server,配置代理,快取,日誌定義等絕大多數功能和第三方模組的配置。如檔案引入,mime-type 定義,日誌自定義,是否使用 sendfile 傳輸檔案,連線超時時間,單連線請求數等。
  • server塊:配置虛擬主機的相關引數,一個 http 中可以有多個 server。
  • location塊:配置請求的路由,以及各種頁面的處理情況。

各程式碼塊詳細的配置方式可以參考 Nginx 文件

二、Nginx 解決跨域問題

下面展示一段常用於處理前端跨域問題的 location程式碼塊,方面各位讀者瞭解及使用 Nginx 去解決跨域問題。

location /cross-server/ {
    set $corsHost $http_origin;
    set $allowMethods "GET,POST,OPTIONS";
    set $allowHeaders "broker_key,X-Original-URI,X-Request-Method,Authorization,access_token,login_account,auth_password,user_type,tenant_id,auth_code,Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, usertoken";
  
    if ($request_method = 'OPTIONS'){
        add_header 'Access-Control-Allow-Origin' $corsHost always;
        add_header 'Access-Control-Allow-Credentials' true always;
        add_header 'Access-Control-Allow-Methods' $allowMethods always;
        add_header 'Access-Control-Allow-Headers' $allowHeaders;
        add_header 'Access-Control-Max-Age' 90000000;
        return 200;
    }
  
    proxy_hide_header Access-Control-Allow-Headers;
    proxy_hide_header Access-Control-Allow-Origin;
    proxy_hide_header Access-Control-Allow-Credentials;
    add_header Access-Control-Allow-Origin $corsHost always;
    add_header Access-Control-Allow-Methods $allowMethods always;
    add_header Access-Control-Allow-Headers $allowHeaders;
    add_header Access-Control-Allow-Credentials true always;
    add_header Access-Control-Expose-Headers *;
    add_header Access-Control-Max-Age 90000000;
  
    proxy_pass http://10.117.20.54:8000/;
    proxy_set_header        Host   $host:443;
    proxy_set_header        X-Forwarded-For         $remote_addr;
    proxy_redirect http:// $scheme://; 
  
}     

可以看到,前段使用 set 設定了 location 中的區域性變數,然後分別在下方的各處指令配置中使用了這些變數,以下是各指令的作用:

  • add_header:用於給請求新增返回頭欄位,當且僅當狀態碼為以下列出的那些時有效:200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), or 308 (1.13.0)
  • **proxy_hide_heade:**可以隱藏響應頭中的資訊。
  • **proxy_redirect:**指定修改被代理伺服器返回的響應頭中的location頭域跟refresh頭域數值。
  • **proxy_set_header:**重定義發往後端伺服器的請求頭。
  • **proxy_pass:**被代理的轉發服務路徑。

以上這段配置可以直接複製到 nginx.conf 中,然後修改 /cross-server/ (Nginx 暴露給客戶端訪問的路徑)和 http://10.117.20.54:8000/(被轉發的服務路徑)即可實避免服務跨域問題。

跨域技巧補充

開發環境下,如果不想使用 Nginx 來處理跨域除錯問題,也可以採用修改 Chrome 配置的方式來實現跨域除錯,本質上跨域是一種瀏覽器的安全策略,所以從瀏覽器出發去解決這個問題反而更加方便。

Windows 系統:

1、複製chrome瀏覽器快捷方式,對快捷方式圖示點右鍵開啟“屬性” 如圖:

未命名繪圖3.png

2、在“目標”後新增 --disable-web-security --user-data-dir,例如圖中修改完成後為:"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir

3、點選確定後重新開啟瀏覽器,出現:

未命名繪圖4.png

此時,遮蔽跨域設定修改完畢,點開此快捷方式訪問的頁面均會忽略跨域規則,避免了開發環境下,服務端配置跨域的麻煩。

Mac 系統:

以下內容轉載自:Mac上解決Chrome瀏覽器跨域問題

首先建立一個資料夾,這個資料夾是用來儲存關閉安全策略後的使用者資訊的,名字可以隨意取,位置也可以隨意放。

5.png

建立一個資料夾

然後開啟控制檯,輸入下面這段程式碼 open -n /Applications/Google\ Chrome.app/ --args --disable-web-security --user-data-dir=/Users/LeoLee/Documents/MyChromeDevUserData

6.png

關閉安全策略程式碼

大家需要根據自己存放剛剛建立的資料夾的地址來更改上面的程式碼,也就是下面圖中的紅框區域,而網上大多數的教程中也正是缺少了這部分的程式碼導致很多使用者在關閉安全策略時失敗

7.png

使用者需要根據自己的資料夾地址修改程式碼

輸入程式碼,敲下回車,接下來Chrome應該會彈出一個視窗

8.png

Chrome彈窗

點選啟動Google Chrome,會發現與之前的Chrome相比,此時的Chrome多了上方的一段提示,告訴你現在使用的模式並不安全

9.png

瀏覽器上方會多出一行提示

其原理和 Windows 版本差不多,都是通過修改配置來繞過安全策略。