0%

干货分享 生产环境中如何为 Tomcat 配置反向代理和负载均衡

关于 Tomcat 系列其他文章的传送门

  1. 《 如何在 10 分钟内学会 Tomcat 8 》

  2. 《 干货分享 生产环境中如何为 Tomcat 配置反向代理和负载均衡 》     您当前所在位置

  3. 《 你一定不知道 如何在 Tomcat 集群中实现三种会话绑定 》



balancer

一、使用 Nginx 和 Apache 反向代理 Tomcat

在生产环境中,Tomcat 不应该直接对客户端开放访问,而应该使用 Apache 或 Nginx 来反向代理客户端的请求。
Tomcat 支持使用 httpajp 两种连接协议,Apache 两种都支持,而 Nginx 目前只能使用 http 协议

1. 使用 Nginx 反代示例

nginx

使用 Nginx 作为反代可以组成 LNMT,即 LinuxNginxMySQLTomcat

请求报文走向

  • Client (http) -> nginx (reverse proxy)(http) -> tomcat (http connector)

Nginx 中反代的配置示例

1
2
3
4
5
6
7
location / {
proxy_pass http://tomcat1.monster.com:8080;
}

location ~* \.(jsp|do)$ {
proxy_pass http://tomcat1.monster.com:8080;
}

2. 使用 Apache 反代

使用 Nginx 作为反代可以组成 LAMT,即 LinuxApache(httpd)MySQLTomcat

httpd 支持使用两种协议代理 Tomcat,对应使用的代理模块为:

  • proxy_module
    • proxy_http_module:适配 http 协议;
    • proxy_ajp_module:适配 ajp 协议;

请求报文走向

  • Client (http) -> httpd (proxy_http_module)(http) -> tomcat (http connector)
  • Client (http) -> httpd (proxy_ajp_module)(ajp) -> tomcat (ajp connector)
  • Client (http) -> httpd (mod_jk)(ajp) -> tomcat (ajp connector)

使用 http 协议代理配置示例

使用 proxy_http_module 模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<VirtualHost *:80>
ServerName tc1.monster.com
ProxyRequests Off # 表示使用的正向代理还是反向代理,Off 表示使用的是反响代理
ProxyVia On # 添加头部响应信息,表示经过了此 apache 服务
ProxyPreserveHost On # 把客户端请求的主机名也发给后端服务器,可以实现转发到不同的 tomcat 虚拟主机
<Proxy *>
Require all granted
</Proxy>
ProxyPass / http://tc1.monster.com:8080/ # 表示反代到后端的哪个主机,当使用感叹号的时候,表示不反代
ProxyPassReverse / http://tc1.monster.com:8080/ # 设置重定向时的反代地址
<Location />
Require all granted
</Location>
</VirtualHost>

使用 ajp 协议代理配置示例

使用 proxy_ajp_module 模块。

只需要将 http:// 换成 ajp:// 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<VirtualHost *:80>
ServerName node1.monster.com
ProxyRequests Off
ProxyVia On
ProxyPreserveHost On
<Proxy *>
Require all granted
</Proxy>
ProxyPass / ajp://node1.monster.com:8009/
ProxyPassReverse / ajp://node1.monster.com:8009/
<Location />
Require all granted
</Location>
</VirtualHost>

二、使用 Nginx 和 Apache 负载均衡 Tomcat

1. 环境准备

使用 Docker 容器模拟两个 Tomcat 后端:

  • 网站目录为:/myapp/
  • IP 为:
    • 10.0.0.2:8080
    • 10.0.0.3:8080
1
2
3
4
5
6
7
8
9
docker run --name tc1 --hostname tc1.monster.com \
-d --network mybr0 \
-v /data/tc1:/usr/local/tomcat/webapps/myapp \
tomcat:8.5-slim

docker run --name tc2 --hostname tc2.monster.com \
-d --network mybr0 \
-v /data/tc2:/usr/local/tomcat/webapps/myapp \
tomcat:8.5-slim

创建 tc1 的测试页
tc1 提供如下示例页面,演示效果会显示 TomcatA。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" %>
<html>
<head><title>TomcatA</title></head>
<body>
<h1><font color="red">TomcatA.monster.com</font></h1>
<table align="centre" border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("monster.com","monster.com"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>

创建 tc2 的测试页
tc1 提供如下示例页面,演示效果会显示 TomcatB。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" %>
<html>
<head><title>TomcatB</title></head>
<body>
<h1><font color="blue">TomcatB.monster.com</font></h1>
<table align="centre" border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("monster.com","monster.com"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>

实验环境就准备好了,下面开始实验

2. 使用 Nginx 负载均衡 Tomcat

Nginx 配置文件示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http {
...
...
upstream tomcat {
server 10.0.0.2:8080 weight=1;
server 10.0.0.3:8080 weight=1;
}
server {
...
...
location /myapp/ {
proxy_pass http://tomcat;
}
}
}

访问 Nginx 的监听地址 http://192.168.50.11/myapp/,可以看出轮询效果。

1
2
3
4
5
-> # while true ;  do curl -s 192.168.50.11/myapp/ | grep head; sleep .1  ;  done
<head><title>TomcatA</title></head>
<head><title>TomcatA</title></head>
<head><title>TomcatB</title></head>
<head><title>TomcatB</title></head>

3. 使用 Apache 负载均衡 Tomcat

负载均衡调度算法
Apache 的负载均衡必须由 proxy_balancer_module 模块来实现。

使用 httpd -M 查看模块,支持 3 种负载均衡:

  • lbmethod_bybusyness_module:等同于 wlc
  • lbmethod_byrequests_module:等同于 wrr
  • lbmethod_bytraffic_module:根据与后端 Server 的链接的繁忙程度调度;

还有一个 lbmethod_heartbeat_module 模块,主要是检测后端 Server 心跳,一般用做健康状态检测。

使用 lbmethod 参数,当所有后端的调度算法一致时,可以统一配置。

  • 如:ProxySet lbmethod=byrequests # 定义调度算法为 wrr

httpd.conf 配置文件示例,使用 http 协议代理 Tomcat。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<proxy balancer://tomcat>                 # 定义组名
BalancerMember http://10.0.0.2:8080 # 等于 nginx 中的 server,需要加上协议和端口
BalancerMember http://10.0.0.3:8080
ProxySet lbmethod=byrequests # 为多个后端统一定义调用算法为 wrr
</Proxy>

<VirtualHost *:80>
ServerName node1.monster.com
ProxyVia On
ProxyRequests Off
ProxyPreserveHost On
<Proxy *>
Require all granted
</Proxy>
ProxyPass / balancer://tomcat/ # 调度到定义的后端组
ProxyPassReverse / balancer://tomcat/
<Location />
Require all granted
</Location>
</VirtualHost>

如果需要 使用 ajp 协议代理 Tomcat,只需要将上述配置中的 http 协议改为 ajp 即可。

访问 Apache 的监听地址 http://192.168.50.11/myapp/,可以看出轮询效果。

1
2
3
4
5
-> # while true ;  do curl -s 192.168.50.11/myapp/ | grep head; sleep .1  ;  done
<head><title>TomcatA</title></head>
<head><title>TomcatA</title></head>
<head><title>TomcatB</title></head>
<head><title>TomcatB</title></head>

BalancerMember 参数

BalancerMember 其实有许多参数可用。

语法:BalancerMember [balancerurl] url [key=value [key=value ...]]

key 可用的参数:

  • status:定义此后端的状态,如 status=D
    • D:定义为 disabled;
    • S:定义为 stopped;
    • I:定义为强制使用此后端,忽略一切错误;
    • H:定义为 Sorry Server;
    • E:强制为错误状态;
    • N:定义为排干模式。
  • loadfactor:负载因子,即权重;

启用管理接口

Aoache 也有可以管理后端 Server 的管理接口,添加如下配置即可激活。

1
2
3
4
5
<Location /balancer-manager>
SetHandler balancer-manager # 启用管理接口
ProxyPass ! # 设置不对管理接口进行反向代理
Require all granted
</Location>

tom