Tomcat 动静分离

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 个请求

相关推荐
P.H. Infinity22 分钟前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天25 分钟前
java的threadlocal为何内存泄漏
java
caridle37 分钟前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^42 分钟前
数据库连接池的创建
java·开发语言·数据库
苹果醋31 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花1 小时前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端1 小时前
第六章 7.0 LinkList
java·开发语言·网络
Wx-bishekaifayuan1 小时前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
全栈开发圈1 小时前
新书速览|Java网络爬虫精解与实践
java·开发语言·爬虫