在上一篇博客咱们介绍了 Nginx 一个很重要的功能——代理,包括正向代理和反向代理。这两个代理的核心区别是:正向代理代理的是客户端,而反向代理代理的是服务器。其中咱们又重点介绍了反向代理,以及如何经过 Nginx 来实现反向代理。那么了解了Nginx的反向代理以后,咱们要经过Nginx的反向代理实现另外一个重要功能——负载均衡。html
早期的系统架构,基本上都是以下形式的:nginx
客户端发送多个请求到服务器,服务器处理请求,有一些可能要与数据库进行交互,服务器处理完毕后,再将结果返回给客户端。web
这种架构模式对于早期的系统相对单一,并发请求相对较少的状况下是比较适合的,成本也低。可是随着信息数量的不断增加,访问量和数据量的飞速增加,以及系统业务的复杂度增长,这种架构会形成服务器相应客户端的请求日益缓慢,并发量特别大的时候,还容易形成服务器直接崩溃。很明显这是因为服务器性能的瓶颈形成的问题,那么如何解决这种状况呢?算法
咱们首先想到的多是升级服务器的配置,好比提升CPU执行频率,加大内存等提升机器的物理性能来解决此问题,可是咱们知道摩尔定律的日益失效,硬件的性能提高已经不能知足日益提高的需求了。最明显的一个例子,天猫双十一当天,某个热销商品的瞬时访问量是极其庞大的,那么相似上面的系统架构,将机器都增长到现有的顶级物理配置,都是不可以知足需求的。那么怎么办呢?数据库
上面的分析咱们去掉了增长服务器物理配置来解决问题的办法,也就是说纵向解决问题的办法行不通了,那么横向增长服务器的数量呢?这时候集群的概念产生了,单个服务器解决不了,咱们增长服务器的数量,而后将请求分发到各个服务器上,将原先请求集中到单个服务器上的状况改成将请求分发到多个服务器上,将负载分发到不一样的服务器,也就是咱们所说的负载均衡。浏览器
负载均衡完美的解决了单个服务器硬件性能瓶颈的问题,可是随着而来的如何实现负载均衡呢?客户端怎么知道要将请求发送到那个服务器去处理呢?服务器
Nginx 服务器是介于客户端和服务器之间的中介,经过上一篇博客讲解的反向代理的功能,客户端发送的请求先通过 Nginx ,而后经过 Nginx 将请求根据相应的规则分发到相应的服务器。网络
主要配置指令为上一讲的 pass_proxy 指令以及 upstream 指令。负载均衡主要经过专门的硬件设备或者软件算法实现。经过硬件设备实现的负载均衡效果好、效率高、性能稳定,可是成本较高。而经过软件实现的负载均衡主要依赖于均衡算法的选择和程序的健壮性。均衡算法又主要分为两大类:session
静态负载均衡算法:主要包括轮询算法、基于比率的加权轮询算法或者基于优先级的加权轮询算法。架构
动态负载均衡算法:主要包括基于任务量的最少链接优化算法、基于性能的最快响应优先算法、预测算法及动态性能分配算法等。
静态负载均衡算法在通常网络环境下也能表现的比较好,动态负载均衡算法更加适用于复杂的网络环境。
例子:
这是Nginx 默认的轮询算法。
例子:两台相同的Tomcat服务器,经过 localhost:8080 访问Tomcat1,经过 localhost:8081访问Tomcat2,如今咱们要输入 localhost 这个地址,能够在这两个Tomcat服务器之间进行交替访问。
1、分别修改两个Tomcat服务器的端口为8080和8081。而后再修改Tomcat的首页,使得访问这两个页面时可以区分。以下:
修改端口号文件为 server.xml :
修改首页的路径为:webapps/ROOT/index.jsp
修改完成以后,分别启动这两个Tomcat服务器,而后分别输入相应的地址端口号:
输入地址:localhost:8081
输入地址:localhost:8080
2、修改 nginx 的配置文件 nginx.conf
1 upstream OrdinaryPolling { 2 server 127.0.0.1:8080; 3 server 127.0.0.1:8081; 4 } 5 server { 6 listen 80; 7 server_name localhost; 8 9 location / { 10 proxy_pass http://OrdinaryPolling; 11 index index.html index.htm index.jsp; 12 13 } 14 }
3、启动 nginx。而后在浏览器输入localhost 地址,观看页面变化:
上述两台Tomcat服务器基本上是交替进行访问的。可是这里咱们有个需求:
因为Tomcat1服务器的配置更高点,咱们但愿该服务器接受更多的请求,而 Tomcat2 服务器配置低,但愿其处理相对较少的请求。
那么这时候就用到了加权轮询机制了。
nginx.conf 配置文件以下:
1 upstream OrdinaryPolling { 2 server 127.0.0.1:8080 weight=5; 3 server 127.0.0.1:8081 weight=2; 4 } 5 server { 6 listen 80; 7 server_name localhost; 8 9 location / { 10 proxy_pass http://OrdinaryPolling; 11 index index.html index.htm index.jsp; 12 13 } 14 }
其实对比上面不加权的轮询方式,这里在 upstream 指令中多了一个 weight 指令。该指令用于配置前面请求处理的权重,默认值为 1。
也就是说:第一种不加权的普通轮询,其实其加权值 weight 都为 1。
下面咱们看页面相应结果:
明显 8080 端口号出现的次数更多,试验的次数越多越接近咱们配置的比例。
咱们知道一个请求在通过一个服务器处理时,服务器会保存相关的会话信息,好比session,可是该请求若是第一个服务器没处理完,经过nginx轮询到第二个服务器上,那么这个服务器是没有会话信息的。
最典型的一个例子:用户第一次进入一个系统是须要进行登陆身份验证的,首先将请求跳转到Tomcat1服务器进行处理,登陆信息是保存在Tomcat1 上的,这时候须要进行别的操做,那么可能会将请求轮询到第二个Tomcat2上,那么因为Tomcat2 没有保存会话信息,会觉得该用户没有登陆,而后继续登陆一次,若是有多个服务器,每次第一次访问都要进行登陆,这显然是很影响用户体验的。
这里产生的一个问题也就是集群环境下的 session 共享,如何解决这个问题?
一般由两种方法:
一、第一种方法是选择一个中间件,将登陆信息保存在一个中间件上,这个中间件能够为 Redis 这样的数据库。那么第一次登陆,咱们将session 信息保存在 Redis 中,跳转到第二个服务器时,咱们能够先去Redis上查询是否有登陆信息,若是有,就能直接进行登陆以后的操做了,而不用进行重复登陆。
二、第二种方法是根据客户端的IP地址划分,每次都将同一个 IP 地址发送的请求都分发到同一个 Tomcat 服务器,那么也不会存在 session 共享的问题。
而 nginx 的基于 IP 路由负载的机制就是上诉第二种形式。大概配置以下:
1 upstream OrdinaryPolling { 2 ip_hash; 3 server 127.0.0.1:8080 weight=5; 4 server 127.0.0.1:8081 weight=2; 5 } 6 server { 7 listen 80; 8 server_name localhost; 9 10 location / { 11 proxy_pass http://OrdinaryPolling; 12 index index.html index.htm index.jsp; 13 14 } 15 }
注意:咱们在 upstream 指令块中增长了 ip_hash 指令。该指令就是告诉 nginx 服务器,同一个 IP 地址客户端发送的请求都将分发到同一个 Tomcat 服务器进行处理。
根据服务器处理请求的时间来进行负载,处理请求越快,也就是响应时间越短的优先分配。
1 upstream OrdinaryPolling { 2 server 127.0.0.1:8080 weight=5; 3 server 127.0.0.1:8081 weight=2; 4 fair; 5 } 6 server { 7 listen 80; 8 server_name localhost; 9 10 location / { 11 proxy_pass http://OrdinaryPolling; 12 index index.html index.htm index.jsp; 13 14 } 15 }
经过增长了 fair 指令。
经过配合location 指令块咱们还能够实现对不一样域名实现负载均衡。
1 upstream wordbackend { 2 server 127.0.0.1:8080; 3 server 127.0.0.1:8081; 4 } 5 6 upstream pptbackend { 7 server 127.0.0.1:8082; 8 server 127.0.0.1:8083; 9 } 10 11 server { 12 listen 80; 13 server_name localhost; 14 15 location /word/ { 16 proxy_pass http://wordbackend; 17 index index.html index.htm index.jsp; 18 19 } 20 location /ppt/ { 21 proxy_pass http://pptbackend; 22 index index.html index.htm index.jsp; 23 24 } 25 }