【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
**开源地址:https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB **
反向代理与缓存
缓存这个问题分为两类,一类是时间缓存,一类是空间缓存。
-
时间缓存是指,当用户请求一个页面的时候,Nginx 发现没有缓存,就会到后端服务器去取,在返回给用户响应的同时还会缓存一份,这样当下一个用户去请求的时候就会直接用缓存作为响应而不会再去请求上游的服务器。
-
空间缓存这种用的比较少,主要是指当用户发来请求的时候,Nginx 可以提前去上游服务器获取一些响应的内容,这个后面可以看到是怎么用的。
upstream 与 server 指令
指令name 表示负载均衡集群的名字,而 {} 内指定了一系列的服务器server 后跟服务器地址,地址后还可以加一些参数 parameters
Syntax: upstream name { … }
Default: —
Context: http
Syntax: server address [parameters];
Default: —
Context: upstream
-
功能:指定一组上游服务器地址,地址可以是域名、IP 地址或者 Unix Socket 地址。可以在域名或者 IP 地址后加端口,如果不加端口,那么默认使用 80 端口。
-
通用参数:server 后可以添加的参数backup:指定当前 server 为备份服务,仅当非备份 server 不可用时,请求才会转发到该 server表示某台服务已经下线,不再服务
负载均衡算法
加权 Round-Robin 负载均衡算法
Round-Robin(rr) 负载均衡算法发给上游服务器的请求是轮询发送的,相当于所有上游服务器根据顺序依次处理发来的请求。
有些情况下上游服务器性能不同,比如 4C8G 和 8C16G 的服务器都有,那么这时候就可以对服务器设置一些权重,让性能好的承担更多的请求。
功能在加权轮询的方式访问 server 指令指定的上游服务集成在 Nginx 的 upstream 框架中,无法移除
指令weight:服务访问的权重,默认是 1max_conns:server 的最大并发连接数,仅作用于单 worker 进程。默认是 0,表示没有限制max_fails:在 fail_timeout 时间段内,最大的失败次数。当达到最大失败时,会在 fail_timeout 秒内这台 server 不允许再次被选择fail_timeout:单位是秒,默认 10 秒,可以指定一段时间内最大失败次数 max_fails 以及到达 max_fails 之后该 server 不能访问的时间
对上游服务使用 keepalive 长连接
Nginx 与上游服务一般是在内网中的,所以开启 keepalive 后效果后更明显。
-
功能:通过复用连接,降低 Nginx 与上游服务器建立、关闭连接的消耗,提升吞吐量的同时降低时延
-
模块: ngx_http_upstream_keepalive_module 默认编译进 Nginx,通过 --without-http_upstream_keepalive_module 移除
对上游服务器的 HTTP 头部设定
Syntax: keepalive connections;
Default: —
Context: upstream
1.15.3 非稳定版本新增命令
Syntax: keepalive_requests number;
Default: keepalive_requests 100;
Context: upstream
Syntax: keepalive_timeout timeout;
Default: keepalive_timeout 60s;
Context: upstream
keepalive connections;
指定上游服务域名解析的 resolver 指令
当使用域名访问上游服务时,可以指定一个 DNS 解析的地址,还可以设置超时等,这个时候就要用到 resolver 指令。
Syntax: resolver address … [valid=time] [ipv6=on|off];
Default: —
Context: http, server, location
Syntax: resolver_timeout time;
Default: resolver_timeout 30s;
Context: http, server, location
实战
下面我起了两个 Nginx 的进程,一个作为上游服务器,监听 8011 和 8012 端口,另一个作为反向代理向上游服务器发请求。
上游服务器的配置如下,当请求是到达 8011 端口就返回 8011 server response. ,当请求到达 8012 端口返回 8012 server response. 。
server {
listen 8011;
default_type text/plain;
return 200 ‘8011 server response.\n’;
}
server {
listen 8012;
default_type text/plain;
client_body_in_single_buffer on;
return 200 ‘8012 server response.\n’;
}
作为反向代理的 Nginx 服务器配置是这个样子的:
这里面 8011 端口和 8012 端口的区别在于 8011 端口设置了权重和对应的参数。
upstream rrups {
server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
server 127.0.0.1:8012;
keepalive 32;
}
server {
server_name rrups.ziyang.com;
error_log myerror.log info;
location /{
proxy_pass http://rrups;
proxy_http_version 1.1;
proxy_set_header Connection “”;
}
两个 Nginx 都配置好之后,来测试一下:
➜ nginx curl rrups.ziyang.com
8011 server response.
➜ nginx curl rrups.ziyang.com
8011 server response.
➜ nginx curl rrups.ziyang.com
8012 server response.
由于 8011 端口的权重设置的是 2,所以根据 rr 算法,每次都是先两个连接负载到 8011 端口上然后是 8012 端口。
这一节讲了 rr 负载均衡算法,rr 算法是所有负载均衡算法的基础,在其他负载均衡算法失效的情况下,Nginx 也会使用 rr 算法进行负载均衡。
负载均衡哈希算法,ip_hash 与 hash 模块
rr 轮询算法没有办法保证请求由某一台指定的服务器去处理,只能轮询处理请求,在 AKF 立方体中只能在 x 轴方向上进行水平扩展。如果基于 z 轴扩展,就可以采用哈希算法保证某一类请求只由特定的服务器处理。
-
功能:以客户端的 IP 地址作为 hash 算法的关键字,映射到特定的上游服务器中对 IPv4 地址使用前 3 个字节作为关键字,对 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 模块,这个模块可以基于任意的关键字实现 hash 算法的复杂均衡。
基于任意关键字实现 hash 算法的负载均衡:upstream_hash 模块
-
功能:通过指定关键字作为 hash key,基于 hash 算法映射到特定的上游服务器中关键字可以含有变量、字符串可以使用 rr 算法的参数
-
模块: ngx_http_upstream_hash_module ,通过 --without-http_upstream_ip_hash_module 禁用模块
指令的话就是 hash 指令,后面可以跟关键字作为 key。
Syntax: hash key [consistent];
Default: —
Context: upstream
实战
配置文件如下所示:
log_format varups '$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’;
upstream iphashups {
ip_hash;
#hash user_$arg_username;
server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
server 127.0.0.1:8012 weight=1;
}
server {
set_real_ip_from 127.0.0.1;
real_ip_recursive on;
real_ip_header X-Forwarded-For;
server_name iphash.ziyang.com;
listen 80;
error_log myerror.log info;
access_log logs/upstream_access.log varups;
location /{
proxy_pass http://iphashups;
proxy_http_version 1.1;
proxy_set_header Connection “”;
}
}
实际验证一下,会发现不同的 ip 地址实际上是会被不同的上游服务器处理的,如果是同一个 ip 地址,那么只会被一个上游服务器处理。
➜ nginx curl -H ‘X-Forwarded-For: 10.200.20.20’ iphash.ziyang.com
8012 server response.
➜ nginx curl -H ‘X-Forwarded-For: 1.200.20.20’ iphash.ziyang.com
8011 server response.
基于 IP 或者基于自定义 key 的 hash 算法有一个严重的问题,那就是当上游服务器挂掉的话,Nginx 依然会向这台服务器发请求,这是因为,如果负载的不同的服务器上去,可能会得到异常的响应,同时还可能导致大量的路由变更。下面的一致性哈希可以解决这个问题。
一致性哈希算法:hash 模块
刚才说了基于 IP 的哈希算法存在一个问题,那就是当有一个上游服务器宕机或者扩容的时候,会引发大量的路由变更,进而引发连锁反应,导致大量缓存失效等问题。那么为什么会造成这种情况呢?
假设我们基于 key 来做 hash,现在有 5 台上游服务器,如果基于最简单的 hash 算法对 key 取模,会将 key 和 server 一一对应起来。