实现负载均衡
动态服务器的问题,往往就是并发能力太弱,往往需要多台动态服务器一起提供服务。如何把并发的压力分摊,这就需要调度,采用一定的调度策略,将请求分发给不同的服务器,这就是Load Balance负载均衡。
当单机的Tomcat,演化出多机多级部署的时候,一个问题便凸显出来,这就是Session。而这个问题的由来,都是由于HTTP协议在设计之初没有想到未来的发展。
HTTP的无状态,有连接和短连接
- 无状态:指的是服务器端无法知道2次请求之间的联系,即使是前后2次请求来自同一个浏览器,也没有任何数据能够判断出是同一个浏览器的请求。后来可以通过cookie、session机制来判断。
- 浏览器端第一次HTTP请求服务器端时,在服务器端使用session这种技术,就可以在服务器端产生一个随机值即SessionID发给浏览器端,浏览器端收到后会保持这个SessionID在Cookie当中,这个Cookie值一般不能持久存储,浏览器关闭就消失。浏览器在每一次提交HTTP请求的时候会把这个SessionID传给服务器端,服务器端就可以通过比对知道是谁了
- Session通常会保存在服务器端内存中,如果没有持久化,则易丢失
- Session会定时过期。过期后浏览器如果再访问,服务端发现没有此ID,将给浏览器端重新发新的SessionID
- 更换浏览器也将重新获得新的SessionID
- 有连接:是因为它基于TCP协议,是面向连接的,需要3次握手、4次断开。
- 短连接:Http 1.1之前,都是一个请求一个连接,而Tcp的连接创建销毁成本高,对服务器有很大的影响。所以,自Http 1.1开始,支持keep-alive,默认也开启,一个连接打开后,会保持一段时间(可设置),浏览器再访问该服务器就使用这个Tcp连接,减轻了服务器压力,提高了效率。
服务器端如果故障,即使Session被持久化了,但是服务没有恢复前都不能使用这些SessionID。
如果使用HAProxy或者Nginx等做负载均衡器,调度到了不同的Tomcat上,那么也会出现找不到SessionID的情况。
会话保持方式
session sticky会话黏性
Session绑定
- nginx:source ip
- HAProxy:cookie
优点:简单易配置
缺点:如果目标服务器故障后,如果没有做sessoin持久化,就会丢失session
session复制集群
Tomcat自己的提供的多播集群,通过多播将任何一台的session同步到其它节点。
缺点
- Tomcat的同步节点不宜过多,互相即时通信同步session需要太多带宽
- 每一台都拥有全部session,内存损耗太多
session server
session 共享服务器,使用memcached、redis做共享的Session服务器,此 为推荐方式
负载均衡规划和准备
负载均衡主机和网络地址规划
IP | 主机名 | 服务 | 软件 |
---|---|---|---|
10.0.0.100 | proxy | 调度器 | Nginx、HTTPD |
10.0.0.101 | t1 | tomcat1 | JDK8、Tomcat8 |
10.0.0.102 | t2 | tomcat2 | JDK8、Tomcat8 |
只在10.0.0.100的nginx主机上实现域名解析
vim /etc/hosts
#添加以下三行
10.0.0.100 proxy.magedu.org proxy
10.0.0.101 t1.magedu.org t1
10.0.0.102 t2.magedu.org t2
负载均衡tomcat主机准备
t1虚拟主机配置conf/server.xml
<Engine name="Catalina" defaultHost="t1.magedu.org">
<Host name="t1.magedu.org" appBase="/data/webapps" autoDeploy="true" >
</Host>
</Engine>
t2虚拟主机配置conf/server.xml
<Engine name="Catalina" defaultHost="locatlhost">
<Host name="tomcat-server" appBase="/data/webapps" autoDeploy="true" >
</Host>
</Engine>
负载均衡规划测试用jsp文件
t1和t2节点的/data/webapps/index.jsp
#项目路径配置
mkdir -pv /data/webapps/ROOT
#编写测试jsp文件,内容在下面
vim /data/webapps/index.jsp
<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lbjsptest</title>
</head>
<body>
<div>On <%=request.getServerName() %></div>
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div>
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>
<%=new Date()%>
</body>
</html>
Nginx实现后端tomcat的负载均衡调度
nginx配置如下
vim /etc/nginx/nginx.conf
#注意名称不要用下划线
upstream tomcat-server {
#ip_hash; # 先禁用看看轮询,之后开启开黏性
server t1.magedu.org:8080;
server t2.magedu.org:8080;
}
server {
location ~* /.(jsp|do)$ {
proxy_pass http://tomcat-server;
}
}
测试http://proxy.magedu.com/index.jsp,可以看到轮询调度效果
[root@proxy ~]#curl http://proxy.magedu.org/index.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lbjsptest</title>
</head>
<body>
<div>On tomcat-server</div>
<div>10.0.0.101:8080</div>
<div>SessionID = <span style="color:blue">1A5B0811ADA103936F2E9A50D303DFD3</span></div>
Tue Feb 11 20:14:53 CST 2020
</body>
</html>
[root@proxy ~]#curl http://proxy.magedu.org/index.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lbjsptest</title>
</head>
<body>
<div>On tomcat-server</div>
<div>10.0.0.102:8080</div>
<div>SessionID = <span style="color:blue">7EB9ACEEE6FD99E3471CCFC0AAA7F37C</span></div>
Tue Feb 11 20:14:54 CST 2020
</body>
</html>
使用抓包wireshark工具可以看到下面信息
在upstream中使用ip_hash指令,使用客户端IP地址Hash。
[root@proxy ~]#vim /etc/nginx/nginx.conf
#只添加ip_hash;这一行
upstream tomcat-server {
ip_hash; #启动源地址hash
server t1.magedu.org:8080;
server t2.magedu.org:8080;
}
配置完reload nginx服务。测试一下看看效果。
[root@proxy ~]#curl http://proxy.magedu.org/index.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lbjsptest</title>
</head>
<body>
<div>On tomcat-server</div>
<div>10.0.0.102:8080</div>
<div>SessionID = <span style="color:blue">F95A1F77FC476D6B1092A9336599CE86</span></div>
Tue Feb 11 20:20:10 CST 2020
</body>
</html>
[root@proxy ~]#curl http://proxy.magedu.org/index.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lbjsptest</title>
</head>
<body>
<div>On tomcat-server</div>
<div>10.0.0.102:8080</div>
<div>SessionID = <span style="color:blue">EC923AF799697155CF6F74B4677C07BE</span></div>
Tue Feb 11 20:20:11 CST 2020
</body>
</html>
#每次都调度到10.0.0.102主机上,但SessionID每次都在变化
通过浏览器看到主机不变,sessionID也不变
关闭Session对应的Tomcat服务,再重启启动它,看看Session的变化。
[root@t2 ~]#systemctl restart tomcat
通过浏览器看到主机不变,但sessionID和上一次变化,但后续刷新不再变化
Httpd实现后端tomcat的负载均衡调度
httpd的负载均衡配置说明
使用httpd -M
可以看到proxy_balancer_module,用它来实现负载均衡。
方式 | 依赖模块 |
---|---|
http负载均衡 | mod_proxy mod_proxy_http mod_proxy_balancer |
ajp负载均衡 | mod_proxy mod_proxy_ajp mod_proxy_balancer |
负载均衡配置说明
配置代理到balancer
ProxyPass [path] !|url [key=value [key=value ...]]
Balancer成员
BalancerMember [balancerurl] url [key=value [key=value ...]]
设置Balancer或参数
ProxySet url key=value [key=value ...]
ProxyPass和BalancerMember指令参数
参数 | 缺省值 | 说明 |
---|---|---|
min | 0 | 连接池最小容量 |
max | 1 – n | 连接池最大容量 |
retry | 60 | apache请求发送到后端服务器错误后等待的时间秒数。0表示立即重试 |
Balancer参数
参数 | 缺省值 | 说明 |
---|---|---|
loadfactor | 定义负载均衡后端服务器权重,取值范围1 – 100 | |
lbmethod | byrequests | 负载均衡调度方法。 byrequests 基于权重的统计请求个数进行调度 bytrafficz 执行基于权重的流量计数调度 bybusyness 通过考量每个后端服务器当前负载进行调度 |
maxattempts | 1 | 放弃请求前实现故障转移的次数,默认为1,其最大值不应该大于总的节点数 |
nofailover | Off | 如果后端服务器没有Session副本,可以设置为On不允许故障转移。 Off故障可以转移 |
stickysession | 调度器的sticky session名字,根据web后台编程语言不通,可以设置为JSESSIONID或PHPSESSIONID |
RroxySet指令也可以使用上面的参数。
启用httpd的负载均衡
在tomcat的配置中Engine使用jvmRoute属性
#t1的conf/server.xml配置如下:
<Engine name="Catalina" defaultHost="t1.magedu.org" jvmRoute="Tomcat1">
<Host name="t1.magedu.org" appBase="/data/webapps" autoDeploy="true" > </Host>
#t2的conf/server.xml配置如下:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="Tomcat2">
<Host name="proxy.magedu.org" appBase="/data/webapps" autoDeploy="true" > </Host>
这样SessionID,就变成了示例形式:SessionID = 9C949FA4AFCBE9337F5F0669548BD4DF.Tomcat2
httpd配置如下
[root@proxy ~]#vim /etc/httpd/conf.d/tomcat.conf
[root@proxy ~]#cat /etc/httpd/conf.d/tomcat.conf
<VirtualHost *:80>
ServerName proxy.magedu.org
ProxyRequests Off
ProxyVia On
ProxyPreserveHost On #off时不向后端转发原请求host首部,而转发采用BalancerMember指向名称为首部
ProxyPass / balancer://tomcat-server/
ProxyPassReverse / balancer://tomcat-server/
</VirtualHost>
<Proxy balancer://tomcat-server>
BalancerMember http://t1.magedu.org:8080 loadfactor=1
BalancerMember http://t2.magedu.org:8080 loadfactor=2
</Proxy>
loadfactor设置为1:2,便于观察。观察调度的结果是轮询的。
使用抓包wireshark工具可以看到下面信息
使用session黏性
官方文档:http://httpd.apache.org/docs/2.4/mod/mod_proxy_balancer.html
[root@proxy ~]#vim /etc/httpd/conf.d/tomcat.conf
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<VirtualHost *:80>
ServerName proxy.magedu.org
ProxyRequests Off
ProxyVia On
ProxyPreserveHost On
ProxyPass / balancer://tomcat-server/
ProxyPassReverse / balancer://tomcat-server/
</VirtualHost>
<Proxy balancer://tomcat-server>
BalancerMember http://t1.magedu.org:8080 loadfactor=1 route=Tomcat1
BalancerMember http://t2.magedu.org:8080 loadfactor=2 route=Tomcat2
ProxySet stickysession=ROUTEID
</Proxy>
发现Session不变了,一直找的同一个Tomcat服务器
关闭tomcat2的tomcat服务,再查看,转向另一台tomcat服务器
[root@t2 ~]#systemctl stop tomcat
ajp调度
在上面基础上修改httpd的配置文件
[root@proxy ~]#cat /etc/httpd/conf.d/tomcat.conf
#Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<VirtualHost *:80>
ServerName proxy.magedu.org
ProxyRequests Off
ProxyVia On
ProxyPreserveHost On
ProxyPass / balancer://tomcat-server/
ProxyPassReverse / balancer://tomcat-server/
</VirtualHost>
<Proxy balancer://tomcat-server>
BalancerMember ajp://t1.magedu.org:8009 loadfactor=1 route=Tomcat1
BalancerMember ajp://t2.magedu.org:8009 loadfactor=2 route=Tomcat2
#ProxySet stickysession=ROUTEID
</Proxy>
ProxySet stickysession=ROUTEID
先禁用看看切换效果,开启后看看黏住效果。
抓包可以可看AJP13协议
开启后,发现Session不变了,一直找的同一个Tomcat服务器。
[root@proxy ~]#vim /etc/httpd/conf.d/tomcat.conf
#只修改下面一行
ProxySet stickysession=ROUTEID
[root@proxy ~]#systemctl restart httpd
多次刷新页面,不再变化
虽然,上面的做法实现客户端在一段时间内找同一台Tomcat,从而避免切换后导致的Session丢失。但是如果Tomcat节点挂掉,那么Session依旧丢失。
[root@t2 ~]#systemctl stop tomcat
假设有A、B两个节点,都将Session持久化。如果Tomcat A服务下线期间用户切换到了Tomcat B上,就获得了Tomcat B的Session,原有Sesssion将丢失,就算将持久化Session的Tomcat A上线了,也没用了。因此需要实现Session的高可用性来解决上述问题。
本文链接:http://www.yunweipai.com/35159.html
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/52746.html