1、Tomcat为何须要与apache、nginx一块儿结合使用?css
Tomcat虽然是一个servlet和jsp容器,可是它也是一个轻量级的web服务器。它既能够处理动态内容,也能够处理静态内容。不过,tomcat的最大优点在于处理动态请求,处理静态内容的能力不如apache和nginx,而且通过测试发现,tomcat在高并发的场景下,其接受的最大并发链接数是由限制的,链接数过多会致使tomcat处于"僵死"状态,所以,在这种状况下,咱们能够利用nginx的高并发,低消耗的特色与tomcat一块儿使用。所以,tomcat与nginx、apache结合使用共有以下几点缘由:html
一、tomcat处理html的能力不如Apache和nginx,tomcat处理静态内容的速度不如apache和nginx。前端
二、tomcat接受的最大并发数有限,接链接数过多,会致使tomcat处于"僵尸"状态,对后续的链接失去响应,须要结合nginx一块儿使用。java
一般状况下,tomcat与nginx、Apache结合使用,nginx、apache既能够提供web服务,也能够转发动态请求至tomcat服务器上。但在一个高性能的站点上,一般nginx、apache只提供代理的功能,也就是转发请求至tomcat服务器上,而对于静态内容的响应,则由前端负载均衡器来转发至专门的静态服务器上进行处理。其架构相似于以下图:node
在这种架构中,当haproxy或nginx做为前端代理时,若是是静态内容,如html、css等内容,则直接交给静态服务器处理;若是请求的图片等内容,则直接交给图片服务器处理;若是请求的是动态内容,则交给tomcat服务器处理,不过在tomcat服务器上,同时运行着nginx服务器,此时的nginx做为静态服务器,它不处理静态请求,它的做用主要是接受请求,并将请求转发给tomcat服务器的,除此以外,nginx没有任何做用。linux
2、tomcat的链接器协议nginx
tomcat和前端web服务器(nginx或Apache)之间的通讯时靠链接器协议来完成的,tomcat共支持4中链接器协议,分别为:web
1) HTTP链接器数据库
2) SSL链接器apache
3) AJP 1.3链接器
4) proxy链接器(好像这个没用了)
也能够归档为支持2种链接器协议:AJP和HTTP,它们用来在Web服务器和Tomcat之间进行数据传输,并提供相应的控制命令。
AJP(Apache JServ Protocol)协议
目前正在使用的AJP协议的版本是经过JK和JK2链接器提供支持的AJP13,它基于二进制的格式在Web服务器和Tomcat之间传输数据,而此前的版本AJP10和AJP11则使用文本格式传输数据。因为这种协议是二进制的,所以在处理请求时的效率比http协议要高。目前经常使用AJP协议的版本是1.3,它主要有如下特征:
1) 在快速网络有着较好的性能表现,支持数据压缩传输;
2) 支持SSL,加密及客户端证书;
3) 支持Tomcat实例集群;
4) 支持在apache和tomcat之间的链接的重用,不适用于与nginx通讯;
HTTP协议:诚如其名称所表示,使用HTTP或HTTPS协议在Web服务器和Tomcat之间创建通讯,此时,Tomcat就是一个彻底功能的HTTP服务器,它须要监听在某端口上以接收来自于前端服务器的请求,对于动态请求须要转交给servlet容器进行处理。若是前端是nginx作代理的话,那么nginx和tomcat之间的通讯只能使用http协议了,而不能使用AJP协议。
http链接器也有3种类型
Tomcat的HTTP链接器有三种:
1) 基于java的HTTP/1.1链接器,这也是Tomcat6默认使用的链接器,即Coyote;它是Tomcat做为standalone模式工做时所用到的链接器,可直接响应来自用户浏览器的关于JSP、servlet和HTML的请求;此链接器是一个Java类,定义在server.xml当中,默认使用8080端口;
2) Java开发的高性能NIO HTTP/1.1链接器,它支持非阻塞式IO和Comnet,在基于库向tomcat发起请求时,此链接器表现不俗;但其实现不太成熟,有严重bug存在;
3) C/C++开发的native APR HTTP/1.1链接器;在负载较大的场景中,此链接器能够提供很是好的性能;APR即Apache Portable Runtime,它是一个能让开发者采用与平台无关的风格的方式来开发C/C++代码本地库,它可以很好的跨Windows,;
默认tomcat的采用的是第1种链接器类型。
实验前的准备步骤:
1)、因为我是将tomcat和nginx在不一样的服务器上配置的,所以,tomcat的ip地址为192.168.1.103,nginx的ip地址为172.16.1.100
2)、关闭系统上的防火墙和selinux
3)、nginx系统上的ip地址和网关配置省略了
3、tomcat与nginx一块儿结合使用
tomcat与nginx为何须要一块儿使用的缘由就不在阐述了,前面已经说得很清楚了,这里直接给出操做步骤。
一、安装nginx
# yum -y install nginx
二、编辑并配置nginx的配置文件/etc/nginx/nginx.conf
在该配置文件中添加以下配置便可
upstream tomcat {
server 172.16.1.100 weight=1 max_fails=3 fail_timeout =10s;
location ~* \.(jsp|do)$ {
proxy_pass http://tomcat;
}
nginx上只须要添加转发动态请求的配置便可。
三、启动nginx服务
# service nginx start
四、安装配置tomcat
tomcat的安装这里就不在详述了。这里只给出tomcat的配置
编辑tomcat的配置文件server.xml,添加以下内容:
<Host name="www.xsl.com" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Context docBase="app1" path="a" reloadable="true" />
</Host>
建立相应的目录及其文件
# mkdir -pv /usr/local/tomcat7/app1/a
# cd /usr/local/tomcat7/app1/a
# vim index.jsp
添加以下测试代码
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
<head>
<title>JSP test page.</title>
</head>
<body>
<% out.println("Hello,world!"); %>
</body>
</html>
其实整个内容也就是显示"hello world"。
启动tomcat
# service tomcat start
五、测试访问http://192.168.1.103/app1/a/index.jsp
4、tomcat结合apache一块儿使用
apache与tomcat结合使用时,有2种模块能够与tomcat进行通讯,这两种模块分别是mod_proxy模块和mod_jk模块。mod_proxy模块是apache自带的模块,所以要使用该模块,只须要启用该模块就好了。而mod_jk模块,须要下载并进行手动编译,使其成为apache的一个模块。
mod_proxy模块能够基于http协议与tomcat进行通讯,也能够基于ajp协议与tomcat进行通讯;而mod_jk模块只能基于ajp协议与tomcat进行通讯。
apache使用mod_proxy模块与tomcat进行通讯
其步骤以下:
(1)、apache使用mod_proxy模块与tomcat通讯时,须要加载proxy_module、proxy_http_module、proxy_ajp_module、proxy_balancer_module(负载均衡时须要使用该模块)等这些模块。
# yum -y install httpd
查看apache是否已经启用该模块了。
# httpd -M | grep proxy
httpd: Could not reliably determine the server's fully qualified domain name, using localhost.localdomain for ServerName
proxy_module (shared)
proxy_balancer_module (shared)
proxy_ftp_module (shared)
proxy_http_module (shared)
proxy_ajp_module (shared)
proxy_connect_module (shared)
(2)、编辑httpd的配置文件/etc/httpd/conf.d/proxy.conf文件,添加以下内容:
proxyvia off
proxypreservehost on
proxyrequests off
proxypass / ajp://172.16.1.100/
proxypa***everse / ajp://172.16.1.100/
若是你的http版本是2.4的话,其配置以下:
proxyvia off
proxypreservehost on
proxyrequests off
<Proxy *>
Require all granted
</Proxy>
proxypass / ajp://172.16.1.100/
proxypa***everse / ajp://172.16.1.100/
<Location / >
Require all granted
</Location>
若是要想基于http链接器协议与tomcat工做的话,只须要将ajp改成http便可。其余的配置都不变。
关于如上apache指令的说明:
ProxyPreserveHost {On|Off}:若是启用此功能,代理会将用户请求报文中的Host:行发送给后端的服务器,而再也不使用ProxyPass指定的服务器地址。若是想在反向代理中支持虚拟主机,则须要开启此项,不然就无需打开此功能。
ProxyVia {On|Off|Full|Block}:用于控制在http首部是否使用Via:,主要用于在多级代理中控制代理请求的流向。默认为Off,即不启用此功能;On表示每一个请求和响应报文均添加Via:;Full表示每一个Via:行都会添加当前apache服务器的版本号信息;Block表示每一个代理请求报文中的Via:都会被移除。
ProxyPa***everse:用于让apache调整HTTP重定向响应报文中的Location、Content-Location及URI标签所对应的URL,在反向代理环境中必须使用此指令避免重定向报文绕过proxy服务器。
ProxyRequests {On|Off}:是否开启apache正向代理的功能;启用此项时为了代理http协议必须启用proxy_http_module模块。同时,若是为apache设置了ProxyPass,则必须将ProxyRequests设置为Off。
ProxyPass [path] !|url [key=value key=value ...]]:将后端服务器某URL与当前服务器的某虚拟路径关联起来做为提供服务的路径,path为当前服务器上的某虚拟路径,url为后端服务器上某URL路径。使用此指令时必须将ProxyRequests的值设置为Off。须要注意的是,若是path以“/”结尾,则对应的url也必须以“/”结尾,反之亦然。
另外,mod_proxy模块在httpd 2.1的版本以后支持与后端服务器的链接池功能,链接在按需建立在能够保存至链接池中以备进一步使用。链接池大小或其它设定能够经过在ProxyPass中使用key=value的方式定义。经常使用的key以下所示:
◇ min:链接池的最小容量,此值与实际链接个数无关,仅表示链接池最小要初始化的空间大小。
◇ max:链接池的最大容量,每一个MPM都有本身独立的容量;都值与MPM自己有关,如Prefork的老是为1,而其它的则取决于ThreadsPerChild指令的值。
◇ loadfactor:用于负载均衡集群配置中,定义对应后端服务器的权重,取值范围为1-100。
◇ retry:当apache将请求发送至后端服务器获得错误响应时等待多长时间之后再重试。单位是秒钟。
(3)、启动httpd服务
# service httpd start
(4)、安装配置tomcat
tomcat的安装这里就不在详述了。这里只给出tomcat的配置
编辑tomcat的配置文件server.xml,添加以下内容:
<Host name="www.xsl.com" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Context docBase="app1" path="a" reloadable="true" />
</Host>
建立相应的目录及其文件
# mkdir -pv /usr/local/tomcat7/app1/a
# cd /usr/local/tomcat7/app1/a
# vim index.jsp
添加以下测试代码
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
<head>
<title>JSP test page.</title>
</head>
<body>
<% out.println("Hello,world!"); %>
</body>
</html>
其实整个内容也就是显示"hello world"。
启动tomcat
# service tomcat start
(5)、测试访问http://192.168.1.103/app1/a/index.jsp
apache使用mod_jk模块与tomcat进行通讯
mod_jk是ASF的一个项目,是一个工做于apache端基于AJP协议与Tomcat通讯的链接器,它是apache的一个模块,是AJP协议的客户端(服务端是Tomcat的AJP链接器)。
(1)、编译安装mod_jk
下载地址:http://tomcat.apache.org/connectors-doc/
# yum -y install httpd
# yum -y install httpd-devel
# tar xf tomcat-connectors-1.2.41-src.tar.gz
# cd /root/tomcat-connectors-1.2.41-src/native
# ./configure --with-apxs (若是没有apxs程序,须要安装httpd-devel包)
# make && make install
(2)、编辑httpd的配置文件/etc/httpd/conf.d/httpd-jk.conf
apache要使用mod_jk链接器,须要在启动时加载此链接器模块。为了便于管理与mod_jk模块相关的配置,这里使用一个专门的配置文件/etc/httpd/conf.d/httpd-jk.conf来保存相关指令及其设置。其内容以下:
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /etc/httpd/conf.d/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel debug
JkMount /* TomcatA
JkMount /status/ stat1
除了须要使用LoadModule指令在apache中装载模块外,mod_jk还须要在apache的主配置文件中设置其它一些指令来配置其工做属性。如JkWorkersFile则用于指定保存了worker相关工做属性定义的配置文件,JkLogFile则用于指定mod_jk模块的日志文件,JkLogLevel则可用于指定日志的级别(info, error, debug),此外还可使用JkRequestLogFormat自定义日志信息格式。而JkMount(格式: JkMount <URL to match> <Tomcat worker name>)指定则用于控制URL与Tomcat workers的对应关系。
/etc/httpd/conf.d/workers.properties的内容以下:
worker.list=TomcatA,stat1
worker.TomcatA.port =8009
worker.TomcatA.host=172.16.1.100
worker.TomcatA.type=ajp13
worker.TomcatA.lbfactor=1
worker.stat1.type=status
根据其工做机制的不一样,worker有多种不一样的类型,这是须要为每一个worker定义的一项属性woker.<work name>.type。常见的类型以下:
◇ ajp13:此类型表示当前worker为一个运行着的Tomcat实例。
◇ lb:lb即load balancing,专用于负载均衡场景中的woker;此worker并不真正负责处理用户请求,而是将用户请求调度给其它类型为ajp13的worker。
◇status:用户显示分布式环境中各实际worker工做状态的特殊worker,它不处理任何请求,也不关联到任何实际工做的worker实例。具体示例如请参见后文中的配置。
worker其它常见的属性说明:
◇ host:Tomcat 7的worker实例所在的主机;
◇ port:Tomcat 7实例上AJP1.3链接器的端口;
◇ connection_pool_minsize:最少要保存在链接池中的链接的个数;默认为pool_size/2;
◇ connection_pool_timeout:链接池中链接的超时时长;
◇ mount:由当前worker提供的context路径,若是有多个则使用空格格开;此属性能够由JkMount指令替代;
◇ retries:错误发生时的重试次数;
◇ socket_timeout:mod_jk等待worker响应的时长,默认为0,即无限等待;
◇ socket_keepalive:是否启用keep alive的功能,1表示启用,0表示禁用;
◇ lbfactor:worker的权重,能够在负载均衡的应用场景中为worker定义此属性
(3)、启动httpd服务
# service httpd start
(4)、安装配置tomcat
tomcat的安装这里就不在详述了。这里只给出tomcat的配置
编辑tomcat的配置文件server.xml,添加以下内容:
<Host name="www.xsl.com" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Context docBase="app1" path="a" reloadable="true" />
</Host>
而且将<Engine />这一行改成以下行:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatA">
这里的TomcatA要与此前的保持一致。
建立相应的目录及其文件
# mkdir -pv /usr/local/tomcat7/app1/a
# cd /usr/local/tomcat7/app1/a
# vim index.jsp
添加以下测试代码
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
<head>
<title>JSP test page.</title>
</head>
<body>
<% out.println("Hello,world!"); %>
</body>
</html>
其实整个内容也就是显示"hello world"。
启动tomcat
# service tomcat start
(5)、测试访问http://192.168.1.103/app1/a/index.jsp
5、tomcat的session集群
对于web服务器集群技术而言,最重要的环节就是如何保证多个节点间的session信息一致性。要实现这一点,大致有二种方式:
一是将全部的session信息放置在一台独立的服务器或者数据库中,集群中全部的节点均可以经过这台服务器获取其相关数据信息。不过若是这台服务器挂了的话,那么全部的节点都不能获取其session信息了,所以,最好对这台服务器作高可用。可是这无疑增长了企业的成本。
二是在多台节点间实现session信息复制,任何一个节点都保存了全部的session数据。只要不是全部的节点都挂掉的话,那么仍然能够响应请求,这种方式比较可靠,可是节点之间的相互复制,会增长服务器的负载,而且会增长网络带宽。常见的平台或中间件如microsoft asp.net和IBM WAS都会提供对两种共享方式的支持,tomcat也是这样,可是通常采用第二种方式。
tomcat的session集群架构
实验前提:
nginx所在服务器地址为:192.168.1.103
TomcatA所在服务器地址为:172.16.1.100
TomcatB所在服务器地址为:172.16.1.200
关闭全部服务器上iptables和selinux
一、实验拓扑图以下:
本实验中前端采用的是nginx,其实后端tomcat主机上还应该加上apache或nginx的,只不过以前已经将该过程实现了,所以,这里就不在给出tomcat如何与nginx、apche一块儿集合使用的步骤了。
二、配置安装nginx
# yum -y install nginx
编辑nginx的配置文件/etc/nginx/nginx.conf,并添加以下内容:
# vim /etc/nginx/nginx.conf
upstream tomcat {
server 172.16.1.100:8080 weight=1 max_fails=3 fail_timeout=10s;
server 172.16.1.200:8080 weight=1 max_fails=3 fail_timeout=10s;
}
server {
listen 80;
location / {
root /htdoc/www;
index index.html;
}
location ~* \.(jsp|do)$ {
proxy_pass http://tomcat;
}
}
最后,启动nginx服务
# service nginx start
三、配置安装tomcatA
tomcat和jdk的安装过程再也不给出,这里只给出修改配置文件的步骤。
修改tomcat的配置文件server.xml(我这里的路径为/usr/local/tomcat7/conf/server.xml)
将<Engine>这一行改成以下行:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatA">
其实就是添加了一个jvmRoute参数。
还须要在配置文件中添加一个Host容器
<Host name="node1.xsl.com" appbase="webapps" unpackWARs="true" autoDeploy="true" >
<Context docBase="node" path="app" reloadable="true" />
</Host>
建立相应的目录及文件
# mkdir /usr/local/tomcat7/webapps/node/app
# vim /usr/local/tomcat7/webapps/node/app/index.jsp
<%@ page language="java" %>
<html>
<head><title>TomcatA</title></head>
<body>
<h1><font color="red">TomcatA.magedu.com</font></h1>
<table align="centre" border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("magedu.com","magedu.com"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>
最后,在启动tomcat服务便可
# service tomcat start
四、配置安装tomcatB
tomcat和jdk的安装过程再也不给出,这里只给出修改配置文件的步骤。
修改tomcat的配置文件server.xml(我这里的路径为/usr/local/tomcat7/conf/server.xml)
将<Engine>这一行改成以下行:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatB">
其实就是添加了一个jvmRoute参数。
还须要在配置文件中添加一个Host容器
<Host name="node2.xsl.com" appbase="webapps" unpackWARs="true" autoDeploy="true" >
<Context docBase="node" path="app" reloadable="true" />
</Host>
建立相应的目录及文件
# mkdir /usr/local/tomcat7/webapps/node/app
# vim /usr/local/tomcat7/webapps/node/app/index.jsp
<%@ page language="java" %>
<html>
<head><title>TomcatB</title></head>
<body>
<h1><font color="blue">TomcatB.magedu.com</font></h1>
<table align="centre" border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("magedu.com","magedu.com"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>
最后,在启动tomcat服务便可
# service tomcat start
五、测试访问nginx的负载功能是否成功
访问http://192.168.1.103/node/app/index.jsp,显示结果以下:
再一次刷新访问,显示结果以下:
由此,nginx的负载均衡功能已经实现。注意观察两次访问的结果,其session信息是不相同的,这也就是说其中任何一台tomcat服务器宕机了,则该服务器上的session信息都会丢失。所以,还须要将session信息保持下来,这里我采用将两台tomcat服务器上的session信息相互进行复制来实现session保持功能。
六、tomcat的session(复制)集群实现过程
在TomcatA和TomcatB上的server.xml文件中,同时添加以下信息,这段信息添加到<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"
//TomcatA address="172.16.1.100"
//TomcatB address="172.16.1.200"
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>
注意:将TomcatA和TomcatB上的<Receiver >中的address由"auto"改成能够接受心跳信息的地址。这里我将TomcatA上的address改成"172.16.1.100";TomcatB上的地址改成"172.16.1.200"。
而后在TomcatA和TomcatB上相应的应用程序目录(即Context中的docBase目录)下建立WEB-INF目录,且建立一个web.xml文件(直接将CATALINA_HOME/conf目录下的web.xml文件复制过来便可)。
# mkdir /usr/local/tomcat7/webapps/node/WEB-INF
# cp /usr/local/tomcat7/conf/web.xml /usr/local/tomcat7/webapps/node/WEB-INF
编辑/usr/local/tomcat7/webapps/node/WEB-INF/web.xml文件,添加以下一行信息:
<distributable/>
这行信息添加到<web-app>容器下。
最后,再次访问http://192.168.1.103/node/app/index.jsp
再次刷新访问,显示结果以下:
观察两次访问的SESSION ID是同样的,由此,tomcat的session集群已经实现了。