在前一篇博客中我们聊了下用 Nginx 和 httpd 对后端 tomcat 服务做反代相关配置, 回顾请参考 https://www.cnblogs.com/qiuhom-1874/p/13334180.html; 今天我们来聊一聊用 Nginx 和 httpd 对 tomcat 集群做负载均衡的配置以及需要注意的点; 在前边的演示和配置都是以单台 tomcat 来配置使用; 但是在生产中单台 tomcat 实在支撑不了大规模的访问, 这个时候我们就需要考虑把多台 tomcat 做成集群对外提供服务; 多台 tomcat 做成集群对外提供服务就必然要有一个调度器来对客户端的请求做调度, 常用的调度器有 nginx httpd haproxy lvs 等等; 用这些调度器来对 tomcat 做负载均衡的配置和对其他 web 服务器做负载均衡的配置没有本质的不同; 我们都可以把 tomcat 当作 Web 服务器来配置就好;
1, 环境准备
运行 docker 启动两个 tomcat 容器当作后端 tomcat server 并且把两台 tomcat 容器的网页目录分别用存储卷的方式映射到 / tomcat/doc/tomcat1 和 tomcat1 目录
提示: 以上就运行了两个 tomcat 容器, 分别是 tct1 和 tc2, 并且我们把 / usr/local/tomcat/webapps/myapp 映射到宿主机的 / tomcat/doc/tomcat1 和 tomcat2, 这样做我们就可以直接把网页脚本放到宿主机上的这个目录从而实现把网页部署到 tomcat 的默认虚拟主机上;
编辑两个容器的主页文件内容
提示: 以上分别给 tomcat1 和 tomcat2 提供了一个测试主页;
现在分别放 tomcat1 和 tomcat2 看看对应主页是否能够访问到
提示: 可以看到 tomcat1 和 tomcat2 都能够访问到, 到此后端 tomcat 的环境就准备好了; 接下来我们来配置 nginx 来对他们做负载均衡;
2, 配置 nginx 对 tomcat 做负载均衡
提示: 以上配置就是把两台 tomcat 容器归并为 tcsevs 组, 然后反代 / 的访问到这个组上即可. 这样配置默认是轮询的;
验证: 访问宿主机上的 80 端口看看是否分别能够访问到后端两台 tomcat 容器提供的主页?
检查 nginx 配置文件语法格式并启动 nginx
访问宿主机的 80 端口, 看看是否能够访问到后端 tomcat 提供的页面?
提示: 可以看到访问宿主机的 80 端口是能够正常访问到后端 tomcat 服务器上的, 并且也看出了默认轮询调度的效果; 但是这里存在一个问题, 同一用户访问宿主机的 80 端口, 给我们响应的结果 session id 都不同, 这意味着 nginx 并没有追踪到用户的状态信息, 原因是因为 http 请求本来就是无状态的, 为了让服务记录用户的状态信息, 在 nginx 上我们可以基于源 ip 做调度, 什么意思呢, 就是同一源 ip 地址, nginx 都把该请求调度到同一台后端 server 上, 使得同一用户访问的状态信息始终调度到同一后端 server 上;
nginx 基于源 ip 做会话保持
提示: ip_hash 和 hash $remote_addr 都表示对源 ip 进行哈希计算, 然后把取得到结果和总权重做模运算, 结果落到那个节点, 就调度到那个节点; 什么意思呢, 如上所示, 后端 server 有两个, 且权重都为 1, 那么他们的权重和就是 2,ip_hash 和 hash $remote_addr 就是把客户端的 ip 地址的前三段进行 hash 计算, 然后把得到的值再和权重和做取模运算, 很显然取模后端结果要么是 0 要么是 1, 如果取模后的结果是 1, 那么 nginx 基于它内部的对应关系, 把该请求就调度到 tomcatB 或者 tomcatA;
测试: 重启 niginx , 访问宿主机的 80 端口看看是否都把请求调度到同一后端 server 上?
提示: 可以看到现在访问宿主机的 80 端口就没有在轮询了, 而是始终调度到 tomcatA 这台 server 上进行响应; 但是我们访问 127.0.0.1 的 80 端口它又调度到 tomcatB 上去了, 这是因为 nginx 的调度算法中 hash $remote_addr 和 ip_hash 是把 IP 地址的前 24 位做 hash, 所以如果你的 IP 前三段相同时, nginx 它会认为是和 nginxserver 是同一局域网, 所以它会把请求调度到同一局域网之前来请求过的后端 server 上进行响应; 当然除了我们可以对源地址做 hash, 我们也可以对其他首部做 hash 计算, 原理都是类似的, 都是把对应首部的值做 hash 计算, 然后同权重和做取模运算; 然后根据 nginx 内部的对应关系, 把取模后端结果相同的请求调度到同一后端 server, 就是基于这样的原理, 把客户端和后端 server 绑定到一起实现了会话绑定;
httpd 对 tomcat 做负载均衡
httpd 做负载均衡器, 需要确认 httpd 是否开启了 proxy_http_module,proxy_module ,proxy_balancer_module 如果需要用到 ajp 还需要确定 proxy_ajp_module 模块是否启用, 以及调度算法的三个模块 lbmethod_bybusyness_module ,lbmethod_byrequests_module,lbmethod_bytraffic_module; 以上模块对于调度算法来说用到那个启用那个也行, 对于 http 或者 ajp 也是一样的; 用得到就启用, 用不上不启用也没关系;
提示: 可以看到我们需要用的模块都是启用了的;
配置 httpd 对后端 tomcat 做负载均衡
提示: 从上面的配置, 其实感觉和 nginx 的配置逻辑很相似, 首先把后端 server 归并成一个组, 然后反代时把请求代理到定义的组上即可; 这里说一下调度算法吧, proxyset lbmethod 用来指定调度算法的, 默认不写是使用 byrequests, 这个算法就是 httpd 里的轮询调度算法, 当然在每个 balancermember 后面加上权重, 就成了加权轮询了; 除此调度算法, 我们还可以使用 bytraffic, 这个调度算法是根据和后端 server 的传输流量来调度, 如果某个服务器传输流量很大, 那么他会把请求往传输流量相对小的服务器上调度; bybusyness 这个调度算法是根据后端 server 的繁忙程度来调度; 类似 nginx 里的 least_conn 最少连接算法; 对 balancermember 我们也可以向 nginx 那样设置单独属性, 只需要在后面写上对应的属性即可; 常用的属性有 status 这个属性表示表示对应 balancermember 是处于什么状态, 其中对 status 有 6 种取值; D 表示禁用对应 server, 不提供任何请求; S 表示人工手动标识为不可用; I 表示强制上线模式 (强制忽略错误模式);H 表示热备模式 (相当于 nginx 里的 backup, 只有组里的其他 server 都不可用时, 它才会被激活, 用于 say sorry);E 表示强制处于错误模式 (即便没有错误也要让他处于有错误);N 表示排干模式; 除了 status 来指定 balancermember 的状态, 还可以使用 loadfactor 来指定权重, 类似于 nginx 里的 weight;
停掉 nginx, 检查 httpd 的配置文件语法, 如果没有问题就启动 httpd
访问 httpd 提供的服务, 看看是否访问到后端 tomcat 的页面
提示: 可以看到和 nginx 的访问一样, 都可以实现轮询;
httpd 基于 cookie 对后端 tomcat 做会话粘性
提示: 以上配置表示给客户端请求 cookie 首部添加一个标识, ROUTEID=%{BALANCER_WORKER_ROUTE}e 表示, 我们指定的 ROUTEID 标识的值为 balancermember 后面的 route 属性指定的值; env=BALANCER_ROUTE_CHANGED 表示, 如果我们指定的 route 的值发生变化时, 它需要重新调度; 简单讲就是给 cookie 信息打标签; proxyset stickysession=ROUTEID 表示给该组所有成员设置会话粘性 KEY 的名称为 ROUTEID, 这个值通常要和上面的 set-cookie 后面的 KEY 对应; 如果把它写到每个 balancermember 后面表示单独给某个 server 设置会话粘性 KEY 的名称; 如果写在 proxy 配置段里需要用 proxyset 指令来设置, 表示给该组的所有 member 设置 stickysession; 简单讲就是声明以那个 key 来当做会话粘性的基准来做调度; 这个逻辑和 haproxy 里面的会话保持设定类似; 有关 haproxy 配置会话保持可以参考 https://www.cnblogs.com/qiuhom-1874/p/12776261.html;
测试: 检查 httpd 的配置文件语法, 如果没有问题就重启 httpd, 然后访问 httpd 看看会有什么变化
用 curl 来模拟第一次访问 httpd 服务器, 看看响应首部有什么变化?
提示: 可以看到访问 httpd 服务器, 在响应首部会多一个 set-cookie 首部, 并且该首部的的值就是我们之前在配置文件中配置的 KEY 和 value;set-cookie 首部主要是在浏览器下次请求时, 它会把 set-cookie 首部的值用 cookie 首部携带去访问服务器, 这样一来, 服务器就可根据客户端请求报文的 cookie 的值, 来分析本次请求是那个客户端发送过来, 后续服务端该怎么调度;
用浏览器访问, 看看客户端后续的请求, 是不是把第一次访问中的 set-cookie 的值拿上去请求服务端?
提示: 可以看到浏览器第一次访问, 服务器会在响应首部中添加一个 set-cookie 的首部; 这个首部的值就是 ROUTEID 是目前响应我们的后端 server 上的 route 的值;
提示: 可以看到客户端在请求首部 cookie 中, 把之前 set-cookie 中的值都携带过去了; 此时 httpd 收到客户端请求就可以根据设置的 stickysession 指定的 KEY 来判断该把对应请求发送到那个后端 server 上进行响应了; 这样一来, 只要客户端的 cookie 不变, 那么它每次访问服务端都会以 cookie 首部的值去告诉服务端该调度到那台后端 server 上;
用 curl 模仿客户端请求携带 cookie 访问服务端
- [root@docker_node01 ~]# curl -I --cookie "ROUTEID=.TOMCAT1" http://192.168.0.22/myapp/
- HTTP/1.1 200
- Date: Mon, 20 Jul 2020 14:26:02 GMT
- Server: Apache/2.4.6 (CentOS)
- Content-Type: text/HTML;charset=ISO-8859-1
- Transfer-Encoding: chunked
- Set-Cookie: JSESSIONID=F03BABD6CC4905066B3BF78947D024CC; Path=/myapp; HttpOnly
- Via: 1.1 www.test1.com
- [root@docker_node01 ~]# curl --cookie "ROUTEID=.TOMCAT1" http://192.168.0.22/myapp/
- <HTML>
- <head><title>TomcatA</title></head>
- <body>
- <h1><font color="blue">TomcatA</font></h1>
- <table align="centre" border="1">
- <tr>
- <td>Session ID</td>
- <td>FDD5095817FBEE6B58054666B64E46C2</td>
- </tr>
- <tr>
- <td>Created on</td>
- <td>1595255172651</td>
- </tr>
- </table>
- </body>
- </HTML>
- [root@docker_node01 ~]# curl --cookie "ROUTEID=.TOMCAT2" http://192.168.0.22/myapp/
- <HTML>
- <head><title>TomcatB</title></head>
- <body>
- <h1><font color="green">TomcatB</font></h1>
- <table align="centre" border="1">
- <tr>
- <td>Session ID</td>
- <td>BC5EAC6B1B75E54F4C92CCB0B5018808</td>
- </tr>
- <tr>
- <td>Created on</td>
- <td>1595255213765</td>
- </tr>
- </table>
- </body>
- </HTML>
[root@docker_node01 ~]#
提示: 可以看到当我们使用 curl 模仿客户端访问携带 cookie 时, 在响应首部就不会在给我们发 set-cookie 首部 (这里的 set-cookie 是指和我们在服务器设定相关的首部), 并且我们携带不同 ROUTEID 的 cookie, 它会根据我们携带的 ROUTEID 的值把我们调度到不同的后端 server 上进行响应; 对于 httpd 负载均衡代理后端 tomcat 用 ajp 的配置方式和 http 的配置方式一样的, 不同的只是把后端 server 的 http 协议修改成 ajp, 后端 tomcat 的端口修改成 ajp 协议监听的端口即可, 默认 tomcatajp 协议监听在 8009 端口;
配置 httpd 后端管理界面页
提示: 以上配置表示启动 httpd 管理页面, 并绑定到 / manager-page 这个 uri 上, 对于 / manager-page 这个 uri 不做任何代理, 并且该 rui 只能允许 ip 地址为 192.168.0.232 的主机访问, 其他主机都没有权限, 包括服务器本身;
验证: 用非 192.168.0.232 的主机访问 192.168.0.22/manager-page 看看是否能够访问到?
- [root@docker_node01 ~]# ip a l ens33
- 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
- link/ether 00:0c:29:22:36:7f brd ff:ff:ff:ff:ff:ff
- .NET 192.168.0.22/24 brd 192.168.0.255 scope global ens33
- valid_lft forever preferred_lft forever
- inet6 fe80::20c:29ff:fe22:367f/64 scope link
- valid_lft forever preferred_lft forever
- [root@docker_node01 ~]# httpd -t
- Syntax OK
- [root@docker_node01 ~]# systemctl restart httpd
- [root@docker_node01 ~]# curl http://192.168.0.22/manager-page
- <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
- <HTML>
- <head>
- <title>
- 403 Forbidden
- </title>
- </head>
- <body>
- <h1>
- Forbidden
- </h1>
- <p>
- You don't have permission to access /manager-page on this server.
- </p>
- </body>
- </HTML>
- [root@docker_node01 ~]#
提示: 可以看到用 192.168.0.22 去访问, 提示 403 没有权限;
用 192.168.0.232 去访问, 看看是否能够访问到管理页面?
提示: 用 192.168.0.232 上的浏览器上可以正常访问到 httpd 的管理页面的;
动态修改 tomcat1 的权重
提示: 正因为这个页面可以动态的更改后端服务器的属性, 所以通常需要做访问限制;
来源: https://www.cnblogs.com/qiuhom-1874/p/13337003.html