Nginx架构四之七层负载均衡

负载均衡概念

负载均衡(Load balancing)在不同的领域有不同的概念。其基本概念是为了减轻某个或某些实体的负载,将任务通过某种策略分配到多个实体上去,实现负载在不同实体间的平衡。

七层负载均衡

七层就是基于URL等应用层信息的负载均衡。从第七层"应用层"开始,根据虚拟的url或IP,主机名接收请求,再转向相应的处理服务器。七层负载均衡器也称作七层交换机,即L7 switch(七层交换),OSI的最高层,应用层。此时,该Load Balancer能理解应用协议。如:HAProxy、Nginx等。

四层负载均衡

四层就是基于IP+端口的负载均衡,是在三次负载均衡的基础上,即从第四层"传输层"开始,使用"ip+port"接收请求,再转发到对应的机器。四层负载均衡器也称作四层交换机,即L4 switch(四层交换),在OSI第4层工作,此种Load Balance不理解应用协议(如HTTP/FTP/MySQL等等)。如:LVS、F5、深信服AD等。

四层负载均衡场景

以常见的TCP为例,负载均衡设备在接收到第一个来自客户端的SYN 请求时,即通过上述方式选择一个最佳的服务器,并对报文中目标IP地址进行修改(改为后端服务器IP),直接转发给该服务器。TCP的连接建立,即三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个转发动作。在某些部署情况下,为保证服务器回包可以正确返回给负载均衡设备,在转发报文的同时可能还会对报文原来的源地址进行修改。
实现四层负载均衡的有:

  • F5:硬件负载均衡器,功能很好,但是成本很高。
  • lvs:重量级的四层负载均衡软件
  • haproxy:模拟四层、七层转发,较灵活
    参考:负载均衡(四层) LVS

四层负载均衡特点

  • 四层负载均衡仅能转发TCP/IP协议、UDP协议、通常用来转发端口,如:tcp/22、udp/53;

  • 四层负载均衡可以用来解决七层负载均衡端口限制问题;(七层负载均衡最大使用65535个端口号)

  • 四层负载均衡可以解决七层负载均衡高可用问题;(多台后端七层负载均衡能同时的使用)

  • 四层的转发效率比七层的高得多,但仅支持tcp/ip协议,不支持http和https协议;

  • 通常大并发场景通常会选择使用在七层负载前面增加四层负载均衡。

四层和七层负载均衡的区别

  • 四层负载均衡数据包是在底层就进行了分发,而七层负载均衡数据包则在
    最顶端进行分发,所以四层负载均衡的效率比七层负载均衡的要高。
  • 四层负载均衡不识别域名,而七层负载均衡识别域名。

Nginx七层负载均衡

Nginx要实现七层负载均衡需要用到proxy_pass代理模块配置。Nginx默 认安装支持这个模块,我们不需要再做任何处理。Nginx的负载均衡是 在Nginx的反向代理基础上把用户的请求根据指定的算法分发到一组 【upstream虚拟服务池】。

upstream指令

该指令是用来定义一组服务器,它们可以是监听不同端口的服务器,并 且也可以是同时监听TCP和Unix socket的服务器。服务器可以指定不同 的权重,默认为1。

语法 upstream name {…}
默认值
位置 http

server指令

该指令用来指定后端服务器的名称和一些参数,可以使用域名、IP、端
口或者unix socket。

语法 server name [paramerters]
默认值
位置 upstream

实现流程

在这里插入图片描述

Nginx负载均衡

我们搭建一个测试环境,部署三个web应用,分别为serverA , serverB,serverC,并在hosts文件中配置测试域名。

127.0.0.1 www.test-nginx.com
  • serverA 的访问地址
    http://www.test-nginx.com:9091/web/hello
    输出内容:hello,I am server A !
  • serverB 的访问地址
    http://www.test-nginx.com:9092/web/hello
    输出内容:hello,I am server B !
  • serverC 的访问地址
    http://www.test-nginx.com:9093/web/hello
    输出内容:hello,I am server C !

服务端设置

server {
    
    
       listen 9091;
       server_name localhost;
       default_type "text/plain;charset=utf-8";
       return 200 "hello,I am server A !";
    }
server {
    
    
       listen 9092;
       server_name localhost;
       default_type "text/plain;charset=utf-8";
       return 200 "hello,I am server B !";
    }
  server {
    
    
       listen 9093;
       server_name localhost;
       default_type "text/plain;charset=utf-8";
       return 200 "hello,I am server C !";
    }

负载均衡设置

    upstream myweb {
    
    
      server 127.0.0.1:9091;
      server 127.0.0.1:9092;
      server 127.0.0.1:9093;
    }

负载均衡状态

代理服务器在负责均衡调度中的状态有以下几个:

状态 概述
down 当前的server暂时不参与负载均衡
backup 预留的备份服务器
max_fails 允许请求失败的次数
fail_timeout 经过max_fails失败后, 服务暂停时间
max_conns 限制最大的接收连接数

down

将该服务器标记为永久不可用,那么该代理服务器将不参与负载 均衡。

   upstream myweb {
    
    
      server 127.0.0.1:9091 down;
      server 127.0.0.1:9092;
      server 127.0.0.1:9093;
    }
    server {
    
    
        listen       80;
        server_name  localhost;
        location /proxy/ {
    
    
           proxy_pass http://myweb/web/;
        }
  }

发送10次请求访问地址:http://www.test-nginx.com/proxy/hello,输出如下:

hello,I am server B !
hello,I am server C !
hello,I am server B !
hello,I am server C !
hello,I am server B !
hello,I am server C !
hello,I am server B !
hello,I am server C !
hello,I am server B !
hello,I am server C !

backup

将该服务器标记为备份服务器,当主服务器不可用时,将用来传 递请求。

upstream myweb {
    
    
      server 127.0.0.1:9091 down;
      server 127.0.0.1:9092 backup;
      server 127.0.0.1:9093;
    }
    server {
    
    
        listen       80;
        server_name  localhost;
        location /proxy/ {
    
    
           proxy_pass http://myweb/web/;
        }
  }

max_conns

max_conns=number:用来设置代理服务器同时活动链接的最大数量, 默认为0,表示不限制,使用该配置可以根据后端服务器处理请求的并发 量来进行设置,防止后端服务器被压垮。

max_fails和fail_timeout

max_fails=number:设置允许请求代理服务器失败的次数,默认为1。
fail_timeout=time:设置经过max_fails失败后,服务暂停的时间,默认 是10秒。

   upstream myweb {
    
    
      server 127.0.0.1:9091 down;
      server 127.0.0.1:9092 backup;
      server 127.0.0.1:9093 max_fails=1 fail_timeout=10;
    }
  server {
    
    
        listen       80;
        server_name  localhost;
        location /proxy/ {
    
    
           proxy_pass http://myweb/web/;
        }
  }

负载均衡策略

介绍完Nginx负载均衡的相关指令后,我们已经能实现将用户的请求分 发到不同的服务器上,那么除了采用默认的分配方式以外,我们还能采 用什么样的负载算法?
Nginx的upstream支持如下六种方式的分配算法,分别是:轮询,加权轮询,Ip hash。下面我们对其一一进行演示。
1. 轮询(默认)
是upstream模块负载均衡默认的策略。每个请求会按时间顺序逐个分配 到不同的后端服务器。轮询不需要额外的配置。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。

worker_processes  4;
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;
    keepalive_timeout  65;
    
	#设置虚拟主机的基本信息
    upstream myweb {
    
    
	    server 127.0.0.1:9091;
        server 127.0.0.1:9092;
        server 127.0.0.1:9093;
    }
    server {
    
    
        listen       80;
        server_name  localhost;

      location /proxy/{
    
    
          proxy_pass http://myweb/web/;
	}
        location / {
    
    
           root   html;
           index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
    
    
            root   html;
        }
    }
}

发送10次请求访问地址:http://www.test-nginx.com/proxy/hello,输出如下:

hello,I am server A !
hello,I am server B !
hello,I am server C !
hello,I am server A !
hello,I am server B !
hello,I am server C !
hello,I am server A !
hello,I am server B !
hello,I am server C !
hello,I am server A !

在这里插入图片描述

2. 加权轮询
weight=number:用来设置服务器的权重,默认为1,权重数据越大,被 分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的 后端服务器硬件配置进行调整的,此策略比较适合服务器的硬件配 置差别比较大的情况。

    upstream myweb {
    
    
		server 127.0.0.1:9091 weight=1;
        server 127.0.0.1:9092 weight=2;
        server 127.0.0.1:9093 weight=2;
    } 

发送10次请求访问地址:http://www.test-nginx.com/proxy/hello,输出如下:

hello,I am server A !
hello,I am server B !
hello,I am server C !
hello,I am server B !
hello,I am server C !
hello,I am server A !
hello,I am server B !
hello,I am server C !
hello,I am server B !
hello,I am server C !

在这里插入图片描述

3. Ip hash 算法
当对后端的多台动态应用服务器做负载均衡时,ip_hash指令能够将某个 客户端IP的请求通过哈希算法定位到同一台后端服务器上。这样,当来 自某一个IP的用户在后端Web服务器A上登录后,在访问该站点的其他 URL,能保证其访问的还是后端web服务器A。每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。

    upstream myweb {
    
    
		server 127.0.0.1:9091;
        server 127.0.0.1:9092;
        server 127.0.0.1:9093;
        ip_hash;
    }

发送10次请求访问地址:http://www.test-nginx.com/proxy/hello,输出如下:

hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !

在这里插入图片描述
4. least_conn
最少连接,把请求转发给连接数较少的后端服务器。轮询算法是把请求 平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用 的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn 这种方式就可以达到更好的负载均衡效果。

    upstream myweb {
    
    
      least_conn;
      server 127.0.0.1:9091;
      server 127.0.0.1:9092;
      server 127.0.0.1:9093;
    }
    server {
    
    
        listen       80;
        server_name  localhost;
        location /proxy/ {
    
    
           proxy_pass http://myweb/web/;
        }
    ...
    }

5. fair(第三方)
fair采用的不是内建负载均衡使用的轮换的均衡算法,而是可以根据页面 大小、加载时间长短智能的进行负载均衡。那么如何使用第三方模块的 fair负载均衡策略。需要使用nginx-upstream-fair-master 插件。

下载:nginx-upstream-fair-master。
使用./configure命令将资源添加到Nginx模块中

./configure --add-module=/Users/yangyanping/Downloads/soft/nginx-upstream-fair-master

编译,编译可能会出现如下错误,ngx_http_upstream_srv_conf_t结构中缺少 default_port

make

解决方案:
在Nginx的源码中 src/http/ngx_http_upstream.h,找到 ngx_http_upstream_srv_conf_s ,在模块中添加添加default_port属性

struct ngx_http_upstream_srv_conf_s {
    
    
    ngx_http_upstream_peer_t         peer;
    void                           **srv_conf;

    ngx_array_t                     *servers;  /* ngx_http_upstream_server_t */

    ngx_uint_t                       flags;
    ngx_str_t                        host;
    u_char                          *file_name;
    ngx_uint_t                       line;
    in_port_t                        port;
    in_port_t                        default_port;
    ngx_uint_t                       no_port;  /* unsigned no_port:1 */

#if (NGX_HTTP_UPSTREAM_ZONE)
    ngx_shm_zone_t                  *shm_zone;
#endif
};

fair 配置

    upstream myweb {
    
    
		server 127.0.0.1:9091;
        server 127.0.0.1:9092;
        server 127.0.0.1:9093;
        fair;
    }
    server {
    
    
        listen       80;
        server_name  localhost;
        location /proxy/ {
    
    
           proxy_pass http://myweb/web/;
        }
    ...
    }

6. url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务 器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的 服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时 间的浪费。而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

 upstream myweb {
    
    
      hash $request_uri;
      server 127.0.0.1:9091;
      server 127.0.0.1:9092;
      server 127.0.0.1:9093;
    }
server {
    
    
        listen       80;
        server_name  localhost;
        location /proxy/ {
    
    
           proxy_pass http://myweb/web/;
        }
        .....
}

发送10次请求访问地址:http://www.test-nginx.com/proxy/hello,输出如下:

hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !
hello,I am server A !

Nginx实现热备

负载均衡的server中常见配置

upstream myweb {
    
     
	ip_hash; 
	server 127.0.0.1:9091;
	server 127.0.0.1:9092 down; (down 表示当前的server暂时不参与负载) 
	server 127.0.0.1:9093 weight=2; (weight 默认为1.weight越大,负载的权重就越大) 
	server 127.0.0.1:9094 backup; (其它所有的非backup机器down或者忙的时候,请求backup机器) 
} 

我们配置serverC端口为备份服务器,当serverA 和 serverB 都挂掉后 会自动使用备份服务器server C(9093端口),但当主服务器serverA 或serverB又启动后,在继续使用主服务器serverA 或serverB,nginx.conf配置如下:

upstream myweb {
    
    
		server 127.0.0.1:9091;
        server 127.0.0.1:9092;
        server 127.0.0.1:9093 backup;
    }

    server {
    
    
        listen       80;
        server_name  localhost;

      location /proxy/{
    
    
          proxy_pass http://myweb/web/;
	}

访问地址:http://www.test-nginx.com/proxy/hello 10次输出结果如下,从中可以看出请求未访问到serveC (server 127.0.0.1:9093)。

hello,I am server A !
hello,I am server B !
hello,I am server A !
hello,I am server B !
hello,I am server A !
hello,I am server B !
hello,I am server A !
hello,I am server B !
hello,I am server A !
hello,I am server B !

主动停掉server A(9091端口),再次发送10次请求,10次输出结果如下:

hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !

再主动停掉serverB(9092端口),再次发送10次请求,10次输出结果如下,主服务器serverA和serverB均已宕机,客户端请求都访问到备份服务器serverC。

hello,I am server C !
hello,I am server C !
hello,I am server C !
hello,I am server C !
hello,I am server C !
hello,I am server C !
hello,I am server C !
hello,I am server C !
hello,I am server C !
hello,I am server C !

启动主服务器 serverB后,再次发送10次请求,10次输出结果如下,主服务器serverB已启动,客户端请求会优选访问主服务器,所以请求都会访问到主服务器serverB。

hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !
hello,I am server B !

proxy_pass详解

在Nginx中配置proxy_pass代理转发时,如果在proxy_pass后面的url加/,表示绝对根路径;如果没有/,表示相对路径,把匹配的路径部分也给代理走。
假设下面四种情况分别用 http://127.0.0.1/proxy/hello 进行访问。

  • 第一种配置,代理URL:http://127.0.0.1:9091/hello
    location /proxy/ {
    proxy_pass http://127.0.0.1:9091/
    }
  • 第二种配置,代理URL:http://127.0.0.1:9091/proxy/hello
    location /proxy/ {
    proxy_pass http://127.0.0.1:9091
    }
  • 第三种配置,代理URL:http://127.0.0.1:9091/web/hello
    location /proxy/ {
    proxy_pass http://127.0.0.1:9091/web/
    }
  • 第三种配置,代理URL:http://127.0.0.1:9091/webhello
    location /proxy/ {
    proxy_pass http://127.0.0.1:9091/web
    }
    MVC测试代码
@RestController
public class HelloController {
    
    
    @GetMapping({
    
    "/web/hello", "webhello"})
    public String hello() {
    
    
        return "hello,I am server A!";
    }
}

猜你喜欢

转载自blog.csdn.net/yangyanping20108/article/details/124793170
今日推荐