入门级Tomcat集群搭建
为什么是入门?讷讷,组件—使用的都是apache社区的免费组件了。
httpd+mod_jk+tomcat。入门换句话说,企业级开发基本不用。
- 集群
- Httpd
- Mod_jk
- Tomcat配置
- Httpd配置
- 代码
- 集群高可用,热拔插
- 一定要用session同步吗
- 总结
集群
互联网,大数据的时代,软硬件集群已是常态话。tomcat集群结构如图:
负载均衡器依据配置的负载规则,将某一用户请求分配给tomcat集群中的某一tomcat服务。
下面给出集群的搭建,由于没有Linux机器用,因此该集群使用的是windows环境。
Httpd
Httpd是Apache的http服务器,用于http的转发。下载。
Httpd下载完成,安装,程序运行默认占用80端口。启动后访问http://localhost/。
It works!显示Httpd服务运行正常。
Mod_jk
Mod_jk是Apache/IIS 用来连接后台Tomcat的连接器,支持集群和负载均衡。下载。
将下载到的mod_jk-1.2.31-httpd-2.2.3.so文件放入到Httpd安装目录下/modules目录下即可。
Tomcat配置
tomcat集群,因此至少要配置两个tomcat容器才能被称为集群。
修改tomcat容器/conf/server.xml文件。
<!--为Engine标签增加jvmRoute属性并赋值,就代表这个tomcat容器的唯一名字呗-->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
并在Engine标签中增加集群的配置,session同步就是通过这个广播的。并切记是在Engine标签中。
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
另外的tomcat也按照如此配置方式。我这里默认jvmRoute=”tomcat2”了。
Httpd配置
嗯,Tomcat配置好了,就需要将tomcat的信息配置到Httpd中了。
Httpd安装目录/conf目录下新建文件:mod_jk.conf。并加如如下内容:
#加载mod_jk Module
LoadModule jk_module modules/mod_jk-1.2.31-httpd-2.2.3.so
#指定workers.properties文件路径 该文件中配置tomcat服务的信息
JkWorkersFile conf/workers.properties
#指定那些请求交给tomcat处理,"controller"为在workers.propertise里指定的负载分配控制器名 *就是将所有请求。
JkMount /* controller
也就是将请求都转发给workers。
Httpd安装目录/conf目录下新建文件:workers.propertise。并加入如下内容:
#server
worker.list = controller
#========tomcat1========
#ajp13端口号,在tomcat下server.xml中Connector标签的端口
worker.tomcat1.port=8009
#tomcat的主机地址,如不为本机,请填写ip地址
worker.tomcat1.host=localhost
#协议类型
worker.tomcat1.type=ajp13
#server的加权比重,值越高,分得的请求越多。lbfactor是负载平衡因数(Load Balance Factor)
worker.tomcat1.lbfactor=1
#========tomcat2========
worker.tomcat2.port=9009
worker.tomcat2.host= localhost
worker.tomcat2.type=ajp13
worker.tomcat2.lbfactor=1
#========controller,负载均衡控制器========
#负载均衡控制器类型,lbfactor是负载平衡因数(Load Balance Factor)
worker.controller.type=lb
#指定分担请求的tomcat列表 server.xml文件中jvmRoute制定的唯一名称。
worker.controller.balanced_workers=tomcat1,tomcat2
#粘性session(默认是打开的) 当该属性值=true(或1)时,代表session是粘性的,即同一session在集群中的同一个节点上处理,session不跨越节点。在集群环境中,一般将该值设置为false
worker.controller.sticky_session=false
#设置用于负载均衡的server的session可否共享
worker.controller.sticky_session_force=true
咦!该配置的都配置好了吗?差一小步,httpd.conf文件中需要引入刚刚定义的mod_jk.conf文件。只需要在httpd.conf末尾加入Include conf/mod_jk.conf即可。
好了tomcat集群搭建完成。
重启httpd服务即可访问,刷新http://localhost/跳转tomcat界面。至此集群搭建成功。
完了?NO!
是时候来一些代码来证明这个集群可用。
代码
代码通过meavn管理。新建meavn的web项目。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tomcat</groupId>
<artifactId>tomcat_colony_project</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>tomcat_colony_project Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-servlet-api -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>9.0.0.M25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8088</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
<finalName>colony</finalName>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
</build>
</project>
新建Servert,要实现逻辑:当session中无数据时插入session数据
package com.ccycc.tomcat.colony;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServert extends HttpServlet{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
Object content = req.getSession().getAttribute("session");
if(content == null || content.toString().trim().length() == 0) {
req.getSession().setAttribute("session", "ccycc_" + Math.random());
}
resp.sendRedirect(req.getContextPath());
}
}
修改web.xml。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.ccycc.tomcat.colony.MyServert</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/servlet/MyServlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<distributable/>
</web-app>
来一个简单的index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
server info : ${pageContext.request.localAddr}:${pageContext.request.localPort} <br>
<br>
session ID : ${pageContext.session.id} <br>
<br>
session value : ${sessionScope["session"]}
<form action="${pageContext.request.contextPath}/servlet/MyServlet?method=session" method="post">
<input type="submit" value="seesionsubmit">
</form>
</body>
</html>
OK!将应用部署到tomcat服务中即可。请求http://localhost/tomcat_colony_project-0.0.1-SNAPSHOT/。
点击按钮可以看到以轮询的方式访问,并且session不会变。说明session是共享的。
启动tomcat时可以看到日志,如下的日志就是集群之间通过udp广播并共享session。
因此如果发现session不可共享,就要查查是否时跨域等网络问题。
集群高可用,热拔插
集群本事高可用的,通过kill或启动tomcat服务,你变可以发现httpd是支持热拔插的。
一定要用session同步吗
互联网级的开发,不可能将session或者token存储于内存中吧,试想用户量上来,那可不是内存吃的消的。redis就是一个很好的选择。那还要同步session吗?仁者见仁智者见智吧!
可以试着去掉tomcat中的集群配置,请求照样正常转发,但是缺点是http请求都是短链接(集群中一个tomcat容器除外),每次请求都会新建会话。
总结
入门级的一个tomcat集群,不能说明问题,只是要多用互联网的思维取考虑问题。
技术为王