艾瑞
艾瑞
新闻资讯
中心动态 学习技巧
Nginx在生产中的应用
2022-05-23

反向代理

在很多企业项目中,nginx都会被拿来做反向代理,那么究竟什么是反向代理呢?我们通过一张图来理解一下:

1.png

当用户发起网络请求时,首先会由系统的网关接收到该请求,网关将请求转发给nginx服务器,nginx服务器再将请求转发给tomcat服务器,注意这里的网关是无法直接访问tomcat服务器的,这样做的好处是什么呢?好处在于外网是无法访问到tomcat的,它只能访问到nginx服务器,再由nginx去访问,这样就保证了tomcat服务器的安全。那么与之相反的是正向代理,正向代理就比较简单了,比如你想访问Google,你就需要搭建一个代理服务器进行访问:

2.png

乍一看,正向代理和反向代理好像没有什么区别,事实上,它们两者只是角度上的不同,对于正向代理,它是对客户端的代理,即:客户端需要访问一个无法直接访问的资源,则通过代理服务器进行一个转发,最终访问到资源服务器;而反向代理是对服务端的代理,即:客户端在访问一个资源时,先将请求发到服务器,再由内部的代理服务器进行一个转发。对于反向代理,客户端是无感知的,因为这个代理过程是在服务端内部进行的,而正向代理是由客户端主动配置的,这些就是它们的一些区别。那么在nginx中如何进行反向代理的配置呢?

server {
  listen 80;
  server_name localhost;
  
  location / {
    proxy_pass http://www.baidu.com;
  }
}

现在我们若是访问http://192.168.33.10,则请求会被转发到百度首页。

负载均衡

负载均衡指的是对某个服务进行多个备份,每个备份都运行在一台服务器上,当请求来到时,nginx可以选择将请求转发到某个服务器上,这样做的好处是每个服务器能够均衡地处理请求数,不会发生某个服务器请求量非常多,而某个服务器请求量又非常小的情况,如图:

3.png

这里共有三台机器部署了服务A,此时nginx可以选择将请求转发给某台服务器,比如将第一个请求转发给第一台服务器,将第二个请求转发给第二台服务器,将第三个请求转发给第三台服务器,将第四个请求又重新转发给第一台服务器。以此类推,我们可以得到一个规律:请求需要转发到哪台服务器上 = 第几次请求 % 服务器台数,这是负载均衡中比较简单的轮询算法。那么负载均衡在nginx中又该如何配置呢?

http {

  upstream alls {
    server 192.168.33.10:80;
    server 192.168.34.10;
    server 192.168.35.10;
  }

  server {
    listen 80;
    server_name localhost;
  
    location / {
      proxy_pass http://alls;
    }
  }
}

首先通过upstream alls配置一组服务器,这里的alls是这组服务器的别名,然后再借助proxy_pass [http://alls](http://alls)进行反向代理,因为alls是一组服务器,所以nginx会进行负载均衡,默认为轮询的方式。

负载均衡策略

事实上, 除了轮询方式,负载均衡还有很多种策略,分别如下:

  1. 权重分配
  2. IP哈希
  3. 最少连接
  4. 响应时间
  5. 定向流量转发

权重分配

当服务器集群中的服务器出现性能不同的情况时,比如某台服务器性能很好,能够支撑更高的请求量,而有些服务器性能比较差,能接受的请求量也有限,此时就不能使用轮询策略,而是想办法将多一点的请求转发给性能好的服务器,让性能差的服务器少处理一点请求。

4.png

以上就是基于权重分配的负载均衡策略,配置方式如下:

http {

  upstream alls {
    server 192.168.33.10:80 weight=8;
    server 192.168.34.10 weight=5;
    server 192.168.35.10 weight=2;
  }

  server {
    listen 80;
    server_name localhost;
  
    location / {
      proxy_pass http://alls;
    }
  }
}

权重分配方式是按照比例来进行负载的,比如有90个请求,则大约有48个请求会转发到192.168.33.10服务器;大约有30个请求会转发到192.168.34.10服务器,大约有12个请求会转发到192.168.35.10服务器。若是不想让某台服务器参与负载均衡的选择,则可以使用down参数进行配置:

upstream alls {
  server 192.168.33.10:80 weight=8 down;
  server 192.168.34.10 weight=5;
  server 192.168.35.10 weight=2;
}

upstream中还有一个backup参数:

upstream alls {
  server 192.168.33.10:80 weight=8;
  server 192.168.34.10 weight=5;
  server 192.168.35.10 weight=2 backup;
}

它的作用是将某台服务器设置为备用服务器,一般情况下负载均衡是不会将请求转发给备用服务器的,只有当其它服务器都挂了,没有其它服务器可用了,只剩下备用服务器自己了,请求才会被负载到它的身上(比如当192.168.33.10:80、192.168.34.10服务器都挂掉时,能够负载均衡的选择就只剩下192.168.35.10了)。

IP哈希

负载均衡带来的问题是无法保证会话,比如当你进行登录时,请求被转发到了某台服务器,登录完成后,当你进行一些需要登录的操作时,比如下单,此时下单的请求被转发到了另外一台服务器,因为这台服务器是没有用户登录的cookie、session等信息的,所以登录状态就消失了,这是不被允许的。通过IP哈希的方式,可以让某一个IP的请求都转发到同一台服务器,这样便能够维持用户会话的状态,然而随着互联网的发展,这种方式已经无法保证维持会话了,因为IP随时可能发生变化。

最少连接

最少连接方式指的是负载均衡会尽量将请求转发给连接量更少的服务器上,目的是使服务器上的连接数更加均衡,这种方式一般来说也是不太常用的,因为服务器存在性能上的差异,更加均衡势必会导致一些问题的产生。

响应时间

响应时间方式是根据后端服务器的响应时间来转发请求,当某台服务器的响应时间更短时,那么更多的请求将转发给该服务器。

定向流量转发

定向流量转发方式指的是对请求的某个资源url进行哈希得到待转发的服务器,这种方式也是无法维护会话状态的,不过在对某些无状态的资源进行请求时,而这些资源分布在不同的服务器上,定向流量转发方式还是适用的,它可以通过url哈希得到资源具体所在的服务器。

动静分离

在访问一个网站的首页时,通常会伴随着非常多的请求,包括图片、mp3等等资源:

5.png



仅仅是访问一个淘宝的首页请求就有100多个,这些内容都需要经过后台服务器的映射:


6.png

而对于这些静态资源,我们完全可以由nginx代为管理,不用tomcat再去操心了,如下所示:

7.png


这就是nginx动静分离所做的事情。我们假设现在有一个项目运行在192.168.33.10服务器上的tomcat中,那么首先通过nginx配置反向代理:

server {
  listen 80;
  server_name localhost;

  location / {
    proxy_pass http://192.168.33.10:8080;
  }
}

然后将静态资源(css、js、img)放在nginx的html目录下,并进行配置:

server {
  listen 80;
  server_name localhost;

  location / {
    proxy_pass http://192.168.33.10:8080;
  }
  
  location /css{
    root html;
  }
  
  location /js{
    root js;
  }
  
  location /img{
    root img;
  }
}

这三个location的配置其实可以借助正则表达式写成一个:

server {
  listen 80;
  server_name localhost;

  location / {
    proxy_pass http://192.168.33.10:8080;
  }
  
  location ~*/(css|js|img){
    root html;
  }
}

URL Rewrite

先来看一个现象:

8.png


这是京东商城关于手机方面的商品:


9.png

我们随便点击一个进去观察url:

10.png


你会发现,对于每一个商品,都有一个html页面与之对应,难道京东真的提供了这么多的html页面吗?可以想象一下京东有多少商品,要为每个商品提供一个html显然是不可能的。事实上,它用的其实是URL Rewrite技术,将url地址进行了改写,让用户看起来以为每个商品都是一个html页面。配置如下:

server {
  listen 80;
  server_name localhost;

  location / {
    rewrite ^/([0-9]+).html$ /shop?shopId=$1 break;
    proxy_pass http://192.168.33.10:8080;
  }
}

重点来解读一下这段配置:rewrite ^/([0-9]+).html$ /shop?shopId=$1 break;,首先^/([0-9]+).html$是正则匹配规则,也就是说以任意数字为前缀的.html页面才能匹配成功,如:1.html、12121.html等;其次/shop?shopId=$1表示转发到的地址,而$1表示引用第一个规则,如果写$2就表示引用第二个规则,这里我们只写了一个正则表达式规则;最后的break表示本条规则匹配成功后即结束,不再匹配后续的规则。

所以当我们访问http://192.168.33.10:8080/123.html时,实际上的url地址是http://192.168.33.10:8080/shop?shopId=123。