1、使用 Nginx + Tomcat 搭建负载均衡
nginx 一般用来做反向代理和负载均衡,将客户端请求发送到后端的 tomcat,并将 tomcat 的响应发送给客户端。后端的 tomcat 通常不止一个,nginx 根据配置来选择其中一个 tomcat,比较常见的选择策略是轮询。
Nginx 的5种负载
|------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| 负载方式 | 说明 |
| 轮询 | 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。Weight指定轮询权值,Weight值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。 |
| ip_hash | 每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。当然如果这个节点不可用了,会发到下个节点,而此时没有session同步的话就注销掉了。 |
| least_conn | least_conn :请求被发送到当前活跃连接最少的 real server 上。会考虑 weight的值。 |
| url_hash | 此方法按访问 url 的 hash结果来分配请求,使每个 url 定向到同一个后端服务器, 可以进一步提高后端缓存服务器的效率。Nginx 本身是不支持 url_hash 的,如果需 要使用这种调度算法,必须安装 Nginx 的 hash 软件包 nginx_upstream_hash |
| fair | 这是比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx 本身是不支持 fair 的,如果需要使用这种调度算法,必须下载 Nginx 的 upstream_fair 模块。 |
实验拓扑
|----------|-----------------|--------|
| 主机 | IP | 服务 |
| JClouds | 192.168.137.253 | Nginx |
| Book | 192.168.137.252 | Tomcat |
| Client01 | 192.168.137.6 | Tomcat |
安装Nginx:Nginx 安装 · 语雀
安装Tomcat:Tomcat 简介安装 · 语雀
配置 Nginx 负载均衡
vim /usr/local/nginx/conf/nginx.conf
# 在http里面添加
upstream tomcat {
server 192.168.137.252:8080 weight=1 max_fails=1 fail_timeout=10s;
server 192.168.137.6:8080 weight=2 max_fails=1 fail_timeout=10s;
}
server {
listen 8081;
server_name localhost;
location / {
root html;
index index.html index.htm;
# 在 server 的location里面调用
proxy_pass http://tomcat;
}
}
|------------------------|--------------------------------------------------------|
| 设备转态 | 说明 |
| down | 表示当前的 server 暂时不参与负载 ; |
| weight | 默认为 1,weight 越大,负载的权重就越大; |
| max_fails | 允许请求失败的次数默认为 1,当超过最大次数 proxy_next_upstream 模块定义的错误 ; |
| fail_timeout:max_fails | 请求失败后,暂停的时间 |
| backup | 其它所有的非 backup 机器 down 或者忙的时候,请求 backup 机器,所以这台机器压力会最轻。 |
# Ip_Hash实现负载均衡:每一个客户端访问时都生成一个 hash 码,来自同一客户端的定向到同一服务器
# 修改配置文件
vim /usr/local/nginx/conf/nginx.conf
upstream tomcat {
ip_hash;
server 192.168.1.12:8080 weight=1 max_fails=1 fail_timeout=10s;
server 192.168.1.13:8080 weight=2 max_fails=1 fail_timeout=10s;
}
2、后端服务器获取客户端真实ip
如何让后端 web 服务器(tomcat/apache/nginx)日志获取客户端真实的 IP,而不是 nginx 代理服务器的 IP。
Nginx代理服务器配置
# 修改 nginx 的配置文件,在 server 或者 location 中添加
vim /usr/local/nginx/conf/nginx.conf
location / {
# root html;
# index index.html index.htm;
proxy_pass http://tomcat;
proxy_set_header X-Forwarded-For $remote_addr;
}
nginx -s reload
tomcat 作为后端服务器的配置
如果 tomcat 作为后端 web,在 tomcat 日志中记录客户端的真实 ip,tomcat 需要做如下修改。
vim /usr/local/tomcat/conf/server.xml
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%{X-FORWARDED-FOR}i %a %l %u %t %r %s %b %D %q %{User-Agent}i"
resolveHosts="false" />
没有 Nginx 这一层的时候直接用%a 就可以获得客户端 IP,现在我们得用%{ X-FORWARDED-FOR }i 来获得真实的 IP 了。
|----------|---------------------------------------|
| 日志参数 | 说明 |
| %a | 远程 IP 地址 |
| %A | 本地 IP 地址 |
| %b | 发送的字节数,不包括 HTTP 头,或" - "如果没有发送字节 |
| %B | 发送的字节数,不包括 HTTP 头 |
| %h | 远程主机名 |
| %H | 请求协议 |
| %l | (小写的 L)- 远程逻辑从 identd 的用户名(总是返回' - ') |
| %m | 请求方法 |
| %p | 本地端口 |
| %q | 查询字符串(在前面加上一个"?"如果它存在,否则是一个空字符串 |
| %r | 第一行的要求 |
| %s | 响应的 HTTP 状态代码 |
| %S | 用户会话 ID |
| %t | 日期和时间,在通用日志格式 |
| %u | 远程用户身份验证 |
| %U | 请求的 URL 路径 |
| %v | 本地服务器名 |
| %D | 处理请求的时间(以毫秒为单位) |
| %T | 处理请求的时间(以秒为单位) |
| %I | (大写的 i) - 当前请求的线程名称 |
# 此外,您可以指定以下别名来设置为普遍使用的模式之一:
common - %h %l %u %t "%r" %s %b
combined - %h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"
# 重启 tomcat 服务,客户端访问。查看 tomcat 的日志
systemctl restart tomcat
tail -2 /usr/local/tomcat/logs/localhost_access_log.2024-01-25.txt
nginx 作为后端服务器的配置
如果 nginx 作为后端 web,在 nginx 日志中记录客户端的真实 ip,nginx 需要做如下修改
重启 nginx 服务,客户端访问。查看 nginx 的日志tail -2 /usr/local/nginx/logs/access.log
apache 作为后端服务器的配置
如果 apache 作为后端 web,在 apache 日志中记录客户端的真实 ip,apache 需要做如下修改:
重启 apache 服务,客户端访问。查看 apache 的日志:tail -2 /var/log/httpd/access_log
3、Nginx 的 proxy 缓存使用
启动缓存支持以后,nginx 在将请求转发到 tomcat 之前会在缓存里查找结果,如果缓存里存在匹配客户端请求的结果,则直接返回给客户端,不会将请求发送给tomcat;
如果缓存没有命中,才会将请求发送给 tomcat,接收到 tomcat 返回的结果后,会将结果进行缓存以备客户端下次的请求. 开启缓存后,缓存及时清理成为了一个问题,需要使用 ngx_cache_purge 这个模块来在过期时间未到之前,手动清理缓存。
proxy 模块中常用的指令是 proxy_pass 和 proxy_cache.
proxy_cache 指令负责反向代理缓存后端服务器的静态内容。
fastcgi_cache 主要用来处理 FastCGI 动态进程缓存。
nginx 加载 ngx_cache_purge 模块
nginx 的所有模块必须在编译的时候添加,不能再运行的时候动态加载。如果你想在已安装好的 nginx 上添加第三方模块,依然需要重新编译,但为了不覆盖你原有的配置,请不要 make install,而是直接拷贝可执行文件。
# ngx_cache_purge 模块下载地址
wget https://github.com/FRiCKLE/ngx_cache_purge/archive/refs/tags/2.3.tar.gz -O \
ngx_cache_purge-2.3.tar.gz
# 解压缩模块
tar zxvf ngx_cache_purge-2.3.tar.gz -C /usr/local/src/
# 预编译 nginx
cd /usr/local/src/nginx-1.22.0/
./configure --prefix=/usr/local/nginx --with-http_dav_module \
--with-http_stub_status_module --with-http_addition_module \
--with-http_sub_module --with-http_flv_module --with-http_mp4_module \
--with-pcre --with-http_ssl_module --with-http_gzip_static_module \
--add-module=../ngx_cache_purge-2.3
make -j 2 && make install
# 先做备份
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
[root@cong11 nginx-1.22.0]# cp objs/nginx /usr/local/nginx/sbin/nginx
配置缓存
vim /usr/local/nginx/conf/nginx.conf
#定义缓存
proxy_connect_timeout 75;
proxy_send_timeout 75;
proxy_read_timeout 75;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_buffering on;
proxy_temp_path /usr/local/nginx/proxy_temp;
proxy_cache_path /usr/local/nginx/proxy_cache levels=1:2 keys_zone=my-cache:100m max_size=1000m inactive=600m max_size=2g;
upstream tomcat {
server 192.168.137.6:8080 weight=1 max_fails=1 fail_timeout=10s;
server 192.168.137.252:8080 weight=2 max_fails=1 fail_timeout=10s;
}
server {
listen 8081;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
#ngx_cache_purge 实现缓存清除
location ~ /purge(/.*) {
allow 127.0.0.1;
allow 192.168.137.0/24;
deny all;
# 开启缓存清理及清>理权限
proxy_cache_purge my-cache $host$1$is_args$args;
}
location ~ .*\.(gif|jpg|png|html|htm|css|js|ico|swf|pdf)(.*) {
# 使用负载均衡
proxy_pass http://tomcat;
# 在 location 里定义带参数
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ignore_headers Set-Cookie;
proxy_hide_header Set-Cookie;
proxy_next_upstream error timeout invalid_header http_500
http_502 http_503 http_504;
# 在 location 里是用 my-cache 缓存
proxy_cache my-cache;
add_header Nginx-Cache $upstream_cache_status;
proxy_cache_valid 200 304 301 302 8h;
proxy_cache_valid 404 1m;
proxy_cache_valid any 1d;
proxy_cache_key $host$uri$is_args$args;
expires 30d;
}
location / {
# root html;
# index index.html index.htm;
proxy_pass http://tomcat;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 选项 | 说明 |
| proxy_buffering on | 代理的时候,开启或关闭缓冲后端服务器的响应。 当开启缓冲时,nginx 尽可能快地从被代理的服务器接收响应,再将它存入缓冲区中。 |
| proxy_temp_path | 缓存临时目录。后端的响应并不直接返回客户端,而是先写到一个临时文件中,然后被 rename 一下当做缓存放在 proxy_cache_path。0.8.9 版本 以后允许 temp 和 cache 两个目录在不同文件系统上(分区),然而为了减少性能损失还是建议把它们设成一个文件系统上。 |
| proxy_cache_path | 设置缓存目录,目录里的文件名是 cache_key 的 MD5 值。 levels=1:2 keys_zone=my-cache:100m 表示采用 2 级目录结构,第一层目录只有一个字符,是由 levels=1:2 设置,总共二层目录,子目录名字由二个字符组成。Web缓存区名称为 my-cache,内存缓存空间大小为 100MB。文件系统上看到的缓存文 件名类似于/usr/local/nginx/proxy_cache/c/29/b7f54b2df7773722d382f4809d65029c 。 inactive=600 max_size=2g 表示 600 分钟没有被访问的内容自动清除,硬盘最大缓存空间为 2GB,超过这个大小将清除最近最少使用的数据。 默认情况,nginx 不缓存从后端响应的 http 头中带有 Set-Cookie 的对象。如果客户端发送的请求带有 Cookie header,nginx 将忽略缓存,直接将请求传递到后端。 nginx 中通过 proxy_ignore_headers 设置忽略它们,设置方法如下: * proxy_ignore_headers Set-Cookie; * proxy_hide_header Set-Cookie; |
| proxy_cache | 引用前面定义的缓存区 my-cache |
| proxy_cache_key | 定义如何生成缓存的键,设置 web 缓存的 key 值,nginx 根据 key 值 md5 哈希存储缓存 |
| proxy_cache_valid | 为不同的响应状态码设置不同的缓存时间,比如 200、302 等 正常结果可以缓存的时间长点,而 404、500 等缓存时间设置短一些,这个时间到了文件就会过期,而不论是否刚被访问过。 |
| add_header | 指令来设置 response header,语法: add_header name value; $upstream_cache_status 这个变量来显示缓存的状态,我们可以在配置中添加一个 http 头来显示这一状态, $upstream_cache_status 包含以下几种状态: * MISS 未命中,请求被传送到后端 * HIT 缓存命中 * EXPIRED 缓存已经过期请求被传送到后端 * UPDATING 正在更新缓存,将使用旧的应答 * STALE 后端将得到过期的应答 |
| expires | 在响应头里设置 Expires:或 Cache-Control:max-age,返回给客户端的浏览器缓存失效时间。 |
# 重启 nginx
nginx -s reload
# 测试缓存功能
echo "server1 html" > /usr/local/tomcat/webapps/ROOT/index.html
echo "server2 html" > /usr/local/tomcat/webapps/ROOT/index.html
清除缓存
使用 ngx_cache_purge 模块清除缓存(直接删除缓存目录下的文件也可以)GET 方式请求 URL,即使用配置文件中的 location ~ /purge(/.*)
location ~ /purge(/.*) {
allow 127.0.0.1;
allow 192.168.137.0/24;
deny all;
proxy_cache_purge my-cache $host$1$is_args$args;
}
4、Nginx 实现动静分离
Nginx 的高并发能力很强,因此,可以在公司里面结合其他的 web 服务器来使用动静分离技术,在 server{}使用正则匹配的 location 来进行流量过滤,静态资源交给 Nginx 处理,动态资源交给 PHP-FPM 模块或 Apache、tomcat、IIS 处理。
vim /usr/local/nginx/conf/nginx.conf
upstream nginx {
server 192.168.137.253:8082 weight=1 max_fails=2 fail_timeout=10s;
}
upstream tomcat {
server 192.168.137.6:8080 weight=1 max_fails=1 fail_timeout=10s;
server 192.168.137.252:8080 weight=2 max_fails=1 fail_timeout=10s;
}
server{
listen 8082;
server_name www.nihao.com;
location / {
root html;
index index.html index.htm;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|rar|zip|txt|flv|mid|doc|ppt|xls|mp3|wma|html|htm|css|js|ico|swf|pdf)$ {
root html ;
expires 30d;
}
}
server {
listen 8081;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
#ngx_cache_purge 实现缓存清除
allow 192.168.137.0/24;
deny all;
location / {
# root html;
# index index.html index.htm;
proxy_pass http://tomcat;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_ignore_headers Set-Cookie;
proxy_hide_header Set-Cookie;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|rar|zip|txt|flv|mid|doc|ppt|xls|mp3|wma|html|htm|css|js|ico|swf|pdf)$ {
proxy_pass http://nginx;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_cache my-cache;
add_header Nginx-Cache $upstream_cache_status;
proxy_cache_valid 200 304 301 302 8h;
proxy_cache_valid 404 1m;
proxy_cache_valid any 1d;
proxy_cache_key $host$uri$is_args$args;
expires 30d;
}
}
5、Jemeter 压力测试
1000 个线程,每个线程循环 10 次,也就是 tomcat 会接收到 10000 个请求