code学习

Nginx基本介绍+跨域解决方案

​​​​

作者:鲸腾 FE​

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**)**

负载均衡,顾名思义,就是将服务负载均衡地分摊到多个服务器单元上执行,来提高网站、应用等服务的性能和可靠性。 下面我们来对比一下两个系统拓扑,首先是未设计负载均衡的拓扑:

Nginx基本介绍+跨域解决方案

下面是设计了负载均衡的拓扑:

Nginx基本介绍+跨域解决方案

从图二可以看到,用户访问负载均衡器,再由负载均衡器将请求转发给后端服务器,在这种情况下,服务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浏览器快捷方式,对快捷方式图标点右键打开“属性” 如图:

Nginx基本介绍+跨域解决方案

2、在“目标”后添加 ​

​--disable-web-security --user-data-dir​

​​,例如图中修改完成后为:​

​"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir​

​ 。

3、点击确定后重新打开浏览器,出现:

Nginx基本介绍+跨域解决方案

此时,屏蔽跨域设置修改完毕,点开此快捷方式访问的页面均会忽略跨域规则,避免了开发环境下,服务端配置跨域的麻烦。

​Mac 系统:​

以下内容转载自:​​Mac上解决Chrome浏览器跨域问题​​

首先创建一个文件夹,这个文件夹是用来保存关闭安全策略后的用户信息的,名字可以随意取,位置也可以随意放。

Nginx基本介绍+跨域解决方案
Nginx基本介绍+跨域解决方案

创建一个文件夹

然后打开控制台,输入下面这段代码 ​

​open -n /Applications/Google\ Chrome.app/ --args --disable-web-security --user-data-dir=/Users/LeoLee/Documents/MyChromeDevUserData ​

Nginx基本介绍+跨域解决方案
Nginx基本介绍+跨域解决方案

关闭安全策略代码

大家需要根据自己存放刚刚创建的文件夹的地址来更改上面的代码,也就是下面图中的红框区域,而网上大多数的教程中也正是缺少了这部分的代码导致很多用户在关闭安全策略时失败

Nginx基本介绍+跨域解决方案
Nginx基本介绍+跨域解决方案

用户需要根据自己的文件夹地址修改代码

输入代码,敲下回车,接下来Chrome应该会弹出一个窗口

Nginx基本介绍+跨域解决方案
Nginx基本介绍+跨域解决方案

Chrome弹窗

点击启动Google Chrome,会发现与之前的Chrome相比,此时的Chrome多了上方的一段提示,告诉你现在使用的模式并不安全

Nginx基本介绍+跨域解决方案