这是Tomcat群集系列的第二部分。 在第一部分中,我们讨论了如何设置简单的负载均衡器。 我们看到了负载均衡器如何以循环方式将请求分配给tomcat实例。 在本文中,我们讨论在Web应用程序中引入会话时,简单负载均衡器中会出现什么问题。 我们将看到如何解决此问题。
Session如何在Servlet / Tomcat中工作?
在解决问题之前,让我们看一下Tomcat中的会话管理 。
如果任何页面/ servlet创建了会话,那么Tomcat将创建会话对象并将其附加到会话组中(类似于HashMap的结构),并且可以使用session-id标识该会话,该ID只是一个随机数通过任何一种哈希算法生成。 然后使用Cookie标头字段响应客户端。 Cookie标头字段是键值对。 因此,tomcat创建了jsessioid这是密钥,而随机session-id是值。
当响应到达客户端(Web浏览器)时,它将更新cookie值。 如果已经存在,则它将覆盖cookie值。 从现在开始,浏览器将与请求一起附加的cookie发送到该服务器。
HTTP是无状态协议。 因此服务器无法轻松找到客户端会话。 因此,服务器读取请求的标头并提取cookie值和随机会话ID。 然后,它搜索由Tomcat维护的会话组。 然后,tomcat获取该特定客户端的会话(Web浏览器)。
如果客户端cookie值在会话组中不匹配,则Tomcat将创建一个全新的会话并将新的cookie发送到浏览器。 然后浏览器更新它。
该index.jsp代码将部署所有tomcat实例。
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.Date"%>
<%@page import="java.util.List"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
<font size="5" color="#0000FF">
Instance 1
</font>
<hr>
<font size="5" color="#CC0000">
Session Id : <%=request.getSession().getId()%>
Is it New Session : <%=request.getSession().isNew()%>
Session Creation Date : <%=new Date(request.getSession().getCreationTime())%>
Session Access Date : <%=new Date(request.getSession().getLastAccessedTime())%>
</font>
<b>Cart List </b>
<hr>
<ul>
<%
String bookName = request.getParameter("bookName");
List<string> listOfBooks = (List<string>) request.getSession().getAttribute("Books");
if (listOfBooks == null) {
listOfBooks = new ArrayList<string>();
request.getSession().setAttribute("Books", listOfBooks);
}
if (bookName != null) {
listOfBooks.add(bookName);
request.getSession().setAttribute("Books", listOfBooks);
}
for (String book : listOfBooks) {
out.println("<li>"+book + "</li>
");
}
%>
</string></string></string></ul>
<hr>
<form action="index.jsp" method="post">
Book Name <input type="text" name="bookName">
<input type="submit" value="Add to Cart">
</form>
<hr>
Simple Load Balancer中的问题是什么?
如果我们部署使用会话的Web应用程序,则实际上会出现问题。
我用顺序描述
- 用户请求一个网页,在该网页中它使用了会话(例如购物车)。
- 负载均衡器拦截请求并使用循环方式将其发送到其中之一的tomcat。 假设这次将其发送到tomcat1。
- tomcat1创建会话,并向客户端发送cookie标头。
- 负载平衡器仅充当中继器。 将其发送回客户端。
- 下次用户再次向服务器请求购物车时。 这次用户还发送了Cookie标头
- 负载均衡器拦截请求并使用循环方式将其发送到其中之一的tomcat。 这次将其发送到tomcat2。
- Tomcat 2收到请求并提取会话ID。 并且此会话ID与他们的托管会话不匹配。 因为此会话仅在tomcat1中可用。 所以tomcat 2是创建新会话并将新的cookie发送给客户端
- 客户端收到响应并更新cookie(它会覆盖旧的cookie)。
- 客户端再发送一次请求该页面并将cookie发送到服务器。
- 负载均衡器拦截请求并使用循环方式将其发送到其中之一的tomcat。 这次将其发送到tomcat3。
- Tomcat 3接收请求并提取会话ID。 并且此会话ID与他们的托管会话不匹配。 因为此会话仅在tomcat2中可用。 因此,tomcat3将创建新会话并将新的cookie发送给客户端
- 客户端收到响应并更新cookie。(它将覆盖旧的cookie)。
- 客户端再发送一次请求该页面并将cookie发送到服务器。
- 负载均衡器拦截请求并使用循环方式将其发送到其中之一的tomcat。 这次将其发送到tomcat1。
- Tomcat 1收到请求并提取会话ID。 并且此会话ID与他们的托管会话不匹配。 因为客户端会话ID是上次由Tomcat 3更新的。 因此,即使tomcat 1拥有一个由该客户端创建的会话对象。 但是客户端会话ID错误。因此,tomcat3创建了新会话并将新的Cookie发送给客户端(有关更多信息,请观看下面的视频)
- 客户端收到响应并更新cookie。
此顺序继续……
结果,在每个请求上都会创建一个新会话。 而不是继续旧的。
根本原因是负载均衡器 。 如果负载均衡器正确地重定向了请求,则此问题已解决。 但是负载均衡器如何在特定的Tomcat实例处理该客户端之前预先知道该客户端。
HTTP是无状态协议。 因此,HTTP无法解决这种情况。 其他信息是jsessionid cookie。 很好,但这只是一个随机值。 因此,我们无法根据此随机值做出决定。
例如:
Cookie:JSESSIONID = 40025608F7B50E42DFA2785329079227
会话亲和力/粘性会话
会话亲缘关系通过将会话中的所有请求定向到特定的Tomcat服务器来覆盖负载平衡算法。 因此,当我们设置会话亲和力时,我们的问题就解决了。 但是如何设置它,问题是会话值是随机值。 因此,我们需要生成会话值,以某种方式确定哪个Tomcat实例生成响应。
jvmRoute
Tomcat配置文件(server.xml)包含<Engine>标记,该标记为此具有jvmRoute属性。 因此,编辑配置文件并像这样更新<Engine>标记
<Engine name='Catalina' defaultHost='localhost“ jvmRoute=“tomcat1” >
这里我们提到jvmRoute ='tomcat1'
这里,tomcat1是此Tomcat实例的工作程序名称。 检查最后一篇文章中的worker.properties文件。
将此行添加到所有Tomcat实例conf / server.xml文件中,然后重新启动Tomcat实例。
现在所有的Tomcat实例都生成如下的session-id模式
<与之前一样的随机值>。<jvmRoute值>
例如:
Cookie:JSESSIONID = 40025608F7B50E42DFA2785329079227.tomcat1
在此值的末尾,我们可以看到哪个Tomcat实例已生成此特定会话。 因此,负载平衡器可以轻松找出我们需要在哪里委派请求。 在这种情况下,它是tomcat1。
因此,更新所有tomcat实例conf / server.xml文件,以将jvmRoute属性添加到适当的工作程序名称值。 并重新启动实例。 解决了所有问题,即使在基于会话的应用程序中,整个负载平衡也可以正常工作。 但是仍然有一个缺点
假设有5位用户正在访问该网站。 会话亲和力已设置。 这里
tomcat 1服务2位用户,
tomcat 2服务2位用户,
tomcat 2为1个用户服务,然后突然其中一个实例失败。 那会怎样
假设实例1(tomcat1)失败,则这2个用户丢失了会话。 但是他们的请求被重定向到剩余的tomcat实例之一(tomcat2,tomcat3)。 因此他们仍然可以访问网页。 但是他们输掉了以前的比赛。 这是缺点之一。 但与上一个后期负载均衡器相比,它也可以在基于会话的Web应用程序中工作。
在下一篇文章中,我们将看到如何在负载均衡器中设置会话复制。
视频
http://www.youtube.com/watch?feature=player_embedded&v=-9C2ZtdAAFY
参考: Tomcat群集系列第2部分:来自Ramki Java Blog博客的JCG合作伙伴 Rama Krishnan提供的会话亲和力负载均衡器 。