前面我们讲了Nginx的11个阶段以及各个模块的用法。现在终于到了最重要也是最常用的部分,那就是反向代理和负载均衡。今天的文章介绍了负载均衡的原理和对应的四种负载均衡算法,当然还有相应的使用说明和实战。欢迎品尝。负载均衡所谓负载均衡,就是Nginx将请求平均分配给上游的应用服务器,这样即使某台服务器宕机,也不会影响请求的处理,或者当应用服务器无法处理时,可以随时扩大。Nginx在AKFScalableCube上的应用在x轴上,应用服务器集群可以水平扩展,Nginx基于Round-Robin或Least-Connected算法分发请求。然而,横向扩张并不能解决所有问题。当数据量很大时,无论扩展多少服务,单台服务器的数据量还是很大的。在y轴上,可以根据url做不同功能的分配。需要根据URL配置Nginx的位置,开销很大。在z轴上可以根据用户信息展开。例如,将用户IP地址或其他信息映射到特定服务或集群。这就是Nginx的负载均衡功能,其主要目的是增强服务的处理能力和容灾能力。反向代理反向代理和负载均衡在某些方面是密不可分的。Nginx支持多种协议的反向代理。四层反向代理比较简单。不管是UDP还是TCP流量,向上游转发的依然是UDP或者TCP流量。到了应用层就不一样了,因为HTTP头中包含了很多业务信息,需要根据HTTP头转换成不同的协议。反向代理和缓存缓存的问题分为两类,一类是时间缓存,一类是空间缓存。时间缓存是指当用户请求一个页面时,Nginx发现没有缓存,就会从后端服务器中获取,并缓存一份,同时将响应返回给用户,以便下一次用户请求时,它会直接使用缓存作为响应而不请求上游服务器。很少使用空间缓存。主要是指当用户发出请求时,Nginx可以提前到上游服务器获取一些响应内容。你可以在后面看到它是如何使用的。upstream和server命令的名称表示负载均衡集群的名称,在{}中指定一系列server服务器后面跟服务器地址,地址后面可以加一些参数。parametersSyntax:upstreamname{...}Default:—Context:httpSyntax:serveraddress[parameters];Default:—Context:upstream功能:指定一组上游服务器地址,可以是域名、IP地址或UnixSocket地址。您可以在域名或IP地址后添加端口。如果不添加端口,默认使用80端口。通用参数:服务器备份后可以添加的参数:指定当前服务器为备份服务,只有当非备份服务器不可用时,才会将请求转发到本服务器,表示某个服务已经下线,将不再服务负载均衡算法weightedRound-Robin负载均衡算法Round-Robin(rr)负载均衡算法以循环方式向上游服务器发送请求,即所有上游服务器依次处理传入的请求。在某些情况下,上游服务器的性能是不同的。例如,4C8G和8C16G服务器都可用。这时候可以在服务器上设置一些权重,让性能好的服务器承担更多的请求。该函数以加权循环方式访问服务器指令指定的上游服务。它集成在Nginx的上游框架中,无法移除。weight:服务接入的权重,默认为1。默认为0,表示没有限制max_fails:在fail_timeout时间内,失败的最大次数。当达到最大失败次数时,在fail_timeout秒内不允许再次选择此服务器fail_timeout:单位为秒,默认为10秒,可以指定一段时间内失败的最大次数达到max_fails后无法访问服务器时。该服务使用keepalive进行长期连接。Nginx和upstream服务一般在内网,所以开启keepalive后效果更明显。功能:通过多路复用连接减少Nginx与上游服务器建立和关闭连接的消耗,提高吞吐量,降低延迟模块:ngx_http_upstream_keepalive_module默认编译到Nginx中,通过--without-http_upstream_keepalive_module设置去掉给上游服务器的HTTP头proxy_http_version1.1;proxy_set_headerConnection"";upstream_keepalive命令语法:keepaliveconnections;默认:—上下文:upstream#1.15.3不稳定版本新命令语法:keepalive_requestsnumber;默认:keepalive_requests100;上下文:upstream语法:outtimeoutalive_failure;上下文:upstreamkeepaliveconnections;指定上游服务域名解析的resolver命令。使用域名访问上游服务时,可以指定DNS解析地址,也可以设置超时时间等,这时候就需要用到resolver命令。Syntax:resolveraddress...[valid=time][ipv6=on|off];Default:—Context:http,server,locationSyntax:resolver_timeouttime;Default:resolver_timeout30s;Context:http,server,location实战这里有两个之一Nginx进程作为上游服务器监听8011和8012端口,另一个作为反向代理向上游服务器发送请求。上游服务器配置如下,当请求到达8011端口时,返回8011服务器响应。当请求到达8012端口时,返回8012服务器响应。server{listen8011;default_typetext/plain;return200'8011serverresponse.\n';}server{listen8012;default_typetext/plain;#client_body_in_single_bufferon;return200'8012serverresponse.\n';}作为反向代理的Nginx服务器配置如下所示:8011端口和8012端口的区别在于8011端口设置了权重和相应的参数。upstreamrrups{server127.0.0.1:8011weight=2max_conns=2max_fails=2fail_timeout=5;server127.0.0.1:8012;keepalive32;}server{server_namerups.ziyang.com;error_logmyerror.loginfo;location/{proxy_passhttp://rrups;proxy_http_version1.1;proxy_set_headerConnection"";}}两个Nginx都配置好后,我们来测试一下:?nginxcurlrrups.ziyang.com8011serverresponse.?nginxcurlrrups.ziyang.com8011serverresponse.?nginxcurlrrups.ziyang.com8012serverresponse.由于8011端口的权重是2,所以根据rr算法,每次先加载两个连接到8011端口,再加载到8012端口。本节讲rr负载均衡算法。rr算法是所有负载均衡算法的基础。当其他负载均衡算法失效时,Nginx也会使用rr算法进行负载均衡。负载均衡hash算法,ip_hash和hash模块rr轮询算法没有办法保证请求一定会被指定的服务器处理,只能轮询处理请求,而在AKFcube中,只能在x轴方向。如果基于z轴展开,可以使用哈希算法来保证某一类请求只被特定的服务器处理。功能:使用客户端的IP地址作为哈希算法的key,映射到特定的上游服务器。使用前3个字节作为IPv4地址的关键字,并使用IPv6的完整地址。rr算法的参数可以根据realip模块修改用于执行算法的IP地址模块:ngx_http_upstream_ip_hash_module,通过--without-http_upstream_ip_hash_module禁用模块指令比较简单,即ip_hash出现在upstream语境。Syntax:ip_hash;Default:—Context:upstream这里必须要提到的模块之一是realip模块。散列算法根据变量remote_addr的值进行散列。这个变量出现过很多次。可见,多么常用的一个变量。不熟悉的可以去Nginx的前11个阶段复习一遍。还有一个模块upstream_hash模块,可以实现基于任意关键字的哈希算法的复杂平衡。基于任意关键字实现基于哈希算法的负载均衡:upstream_hash模块功能:通过指定关键字作为哈希键,基于哈希算法,映射到特定的上游服务器。关键字可以包含变量,字符串可以使用rr算法参数module:ngx_http_upstream_hash_module,如果通过--without-http_upstream_ip_hash_module禁用模块命令,则为hash命令,后面可以跟一个关键字作为key。Syntax:hashkey[consistent];Default:—Context:upstream实战配置文件如下所示:log_formatvarups'$upstream_addr$upstream_connect_time$upstream_header_time$upstream_response_time''$upstream_response_length$upstream_bytes_received''$upstream_status$upstream_http_istat_server$upstreamhaus';upstream_cache#hashuser_$arg_username;server127.0.0.1:8011weight=2max_conns=2max_fails=2fail_timeout=5;server127.0.0.1:8012weight=1;}server{set_real_ip_from127.0.0.1;real_ip_recursiveon;real_ip_headerX-Forwarded-For.server_nameiphashziyang...IP地址,只会被一台上游服务器处理。?nginxcurl-H'X-Forwarded-For:10.200.20.20'iphash.ziyang.com8012serverresponse.?nginxcurl-H'X-Forwarded-For:1.200.20.20'iphash.ziyang.com8011serverresponse。基于IP还是基于自定义keyhash算法有一个严重的问题,就是当upstream服务器挂掉的时候,Nginx依然会向这台服务器发送请求。这是因为,如果一个不同负载的服务器上去,可能会得到一个异常的响应,也可能会引起很多路由变化。下面的一致性哈希可以解决这个问题。Consistenthashingalgorithm:刚才hash模块说了基于IP的hashing算法有一个问题,就是当一个upstream服务器宕机或者扩容的时候,会引起大量的路由变化,从而引发连锁反应并导致大量缓存失效等问题。那么为什么会这样呢?假设我们根据key做hash,现在上游有5台服务器,如果根据最简单的hash算法对key取模,key和server就会一一对应。当一台服务器宕机时,需要重新对key进行hash,最后会发现所有的对应关系都失效了,会造成缓存的大规模失效。一致性哈希算法可以解决这个问题。一致性哈希算法的原理是将一个环划分为2^32个范围,四个节点将环划分为四个范围,每个范围的请求由对应的节点处理。让我们看看放大时会发生什么。假设此时发现node4负载过高,决定再增加一个节点来分担压力,那么只有这个节点之后的请求会受到影响,缓存可能会失效,而其他三个节点不会有任何影响。这就是一致性哈希算法的原理。一致性哈希算法的使用也很简单。只需要打开上一节的参数:Syntax:hashkey[consistent];Default:—Context:upstream这里只需要指定一致的参数即可。最少连接数算法下面我们来看一下最少连接数算法。顾名思义,该算法将优先考虑连接最少的上游服务器,这是由upstream_least_conn模块提供的。功能:从所有上游服务器中找出并发连接数最少的,将请求转发给它。如果存在多个连接数相同的leastconnectedserver,使用rr算法模块:ngx_http_upstream_least_conn_module,通过--without-http_upstream_ip_hash_module禁用模块指令的用法也很简单,只需要启用upstream模块中的least_conn指令即可。Syntax:least_conn;Default:—上下文:upstream负载均衡策略对所有worker进程生效:upstream_zone模块中上面提到的所有负载均衡算法对于worker进程都是独立的,每个worker进程之间不通信,所以在很多情况下这不是我们所期望的。我们期望的应该是负载均衡算法对所有worker进程生效。作用:分配共享内存,将其他上游模块定义的负载均衡策略数据,以及各上游服务器在运行时的状态数据存储在共享内存上,对所有Nginxworker进程生效。模块:ngx_http_upstream_zone_module,通过--without-http_upstream_ip_hash_module命令禁用该模块,指定zone的名称和对应的大小:Syntax:zonename[size];Default:—Context:upstream另外,每个负载均衡模块必须遵循acertainorder:ngx_module_t*ngx_modules[]={……&ngx_http_upstream_hash_module,&ngx_http_upstream_ip_hash_module,&ngx_http_upstream_least_conn_module,&ngx_http_upstream_random_module,&ngx_http_upstream_keepalive_module,&ngx_http_upstream_zone_module,&ngx_http_upstream_least_conn_module,&ngx_http_upstream_random_module,&ngx_http_upstream_keepalive_module,&ngx_http_upstream_zone_module,…};可以看出zone模块在最后,也就是上面算法定义的参数和配置,最后zone模块会把这些配置放到共享内存中生效。本节介绍负载均衡的原理和四种负载均衡算法,或三种,即轮询、散列和最少连接数算法。每种算法都有自己的应用场景。rr算法是最基本的负载均衡算法。在某些情况下,当其他算法失败时,它会退化为rr算法。上游提供的变量首先引入一组没有缓存的变量。upstream_addr上游服务器的IP地址,格式为可读字符串,如127.0.0.1:8012upstream_connect_time与上游服务建立连接的时间,单位秒,精确到毫秒影响Nginx的性能是的,因为只有收到Header后才能决定下一步如何处理,精确到毫秒upstream_http_header上游服务返回的响应头的值upstream_bytes_received收到的响应长度上游服务,以字节为单位upstream_response_length从上游服务返回的响应主体的长度,以字节为单位upstream_status上游服务返回的HTTP响应状态代码。如果没有连接,变量值为502upstream_cookie_name从上游服务发回的响应头Set-Cookie中取的cookie值upstream_trailer_name从上游服务的响应尾部取的值,看看我们刚才在实战中做了什么的例子。在刚才的负载均衡实践中,有一个日志配置:log_formatvarups'$upstream_addr$upstream_connect_time$upstream_header_time$upstream_response_time''$upstream_response_length$upstream_bytes_received''$upstream_status$upstream_http_server$upstream_cache_status在我们上面提到的很多配置中都用到了';变量,对应实际输出的日志是这样的:127.0.0.1:80120.0010.0010.00122170200nginx/1.17.8-你可以对照日志格式看看是什么意思,这里不再赘述。那么今天的文章就给大家介绍一下什么是负载均衡。Nginx主要通过upstream模块提供相应的功能,并介绍四种负载均衡算法,最后介绍upstream提供的变量。下一节课我们来聊聊Nginx的反向代理。
