Tomcat

Tomcat

JAVA基础概念

编程语言

编程语言,是用来定义计算机程序的形式语言。他是一种被标准化的交流技巧,用来向计算机发出指令。一种计算机语言让程序员能够准确定义计算机所需要使用的数据,并准确的定义在不同情况下所应采取的行动,这里简单将编程语言分为一下三类:
硬件级:微码编程
系统级:C,C++
应用级:Java, PHP, Python, Ruby, Perl, C#, ...

程序由指令加数据组成,编程主要有两种形式:
过程式编程:面向过程的编程,以指令为中心,数据服务与代码。shell为代表。
对象式编程:面向对象的编程,以数据为中心,指令服务与数据;Java就是面向对象的编程语言。

Java体系结构包含了四个独立的相关技术:

Java程序设计语言
Java class文件格式
Java API类库
Java VM 运行时区域:方法区、堆、Java栈、PC寄存器、本地方法栈;

用java语言编译源代码,把他编译成java class文件,然后在Java VM中运行class文件;但编写程序时,通过调用类库(Java API)中的方法来访问系统资源,也满足Java API的调用。Java VM和Java API一起组成了一个平台,所有的Java程序都在其上编译和运行,因此,他们有时也被称作是Java运行时环境。
Java VM的主要任务是装载class文件,并且执行其中的字节码。Java VM包含一个类装载器(class loader),他可以从程序和API装载class文件;而Java API的类只在程序执行中需要时才会被装载。

Java字节码有执行引擎来执行。而不同的Java VM 中,其执行引擎的实现可能不同,最简单的执行引擎不是一次性解释字节码,而另一种成为“即时编译器(just-in-time complier)”的执行引擎执行速度更快。但要消耗更多的内存资源,即时编译模式下,第一次被执行的字节码会被编译成成本地机器代码并缓存下以实现复用。第三中至此那个引擎是所谓的自适应优化器,此种方法中,虚拟机开始的时候解释字节码, 届时会监视运行中程序的活动,并且记录下使用最频繁的代码。程序运行时,虚拟机只把那些活动最频繁的代码编译成本地代码,而不频繁的代码则仍然保留为字节码由虚拟机解释执行。自适应优化器可以使得Java VM在80%-90%的时间里执行被优化过的本地代码,而只需要编译10%-20%对性能有影响的代码。最后一种虚拟机由硬件芯片构成,它用本地方法执行Java字节码,其执行引擎内嵌于芯片中。

Java代码的每一个源代码通常以.java结尾,java程序运行时首先通过编译器将.java的代码编译成.class结尾的类文件。要注意的是这些类文件需要遵守一定的规则,java源码编译完成以后生成类文件,这些类文件需要在JVM上运行,然而市面上各种公司、组织所开发了JVM又都有所不同。要实现这些类文件可以在各种JVM的环境下顺利运行,就需要无论是JAVA的类文件还是JVM的开发都遵守一定的规则,在一定的协议框架之下实现各种类文件在不同的JVM中运行。
另一方面,类文件在运行过程中必然会涉及到各种通用代码,为了简化编写代码的复杂程度Java集成了类文件在运行过程中所依赖的各种通用代码的类文件库,也就是java API的类文件库。JVM在逻辑上由两部分组成:class loader类文件加载器,以及执行引擎。Java程序能够得以在JVM中运行不仅要依赖类文件加载器去加载java源代码的类文件,同时也要加载源代码类文件运行依赖的其他类库文件。
class loader类加载器不仅需要将java代码的类文件加载到执行引擎中,同时还要负责java代码运行所依赖的类库文件,加载完这两者以后,java代码才会在java虚拟机中的执行引擎上运行。而执行引擎是由C语言开发的,所以最终java代码的运行还是会转化成标准C库的运行以及系统调用上的某写功能的运行。

Java代码的运行:
*.java(source code) --> javac --> *.class(bytecode)
loader,加载程序的类文件,及程序的类文件依赖到的其它的类文件而后运行; 整个运行表现为一个jvm进程;

三个技术流派

Java 2 SE

SE包含了Java二进制程序(如JVM和Java字节码编译器)和Java的核心代码库,而Jave EE标准则包含了一组适用于创建企业级Web应用程序的API。Jave EE建立在Java SE的基础上,并依赖于Java SE才能正常工作。当然,任何级别的应用程序均能从Java EE中获益,但Jave EE却更适合解决大型软件系统设计中的问题。
Java SE APIs:
JNDI(Java Naming and Directory Interface):用于与LDAP服务交互的API;
JAXP(Java API for XML Processing):用于分析及转换XML(基于XSLT实现);

Java 2 ME

Mobile Edition用途有限,不做介绍

Java 2 EE

任何的Java 2 EE的开发都是基于 Java 2 SE的。
JAVA EE包含多个独立的API,Servlet和JSP就是其中的两个。

而JAVA EE中著名的API中还包含如下的几个:
EJB(Enterprise JavaBeans):JAVA相关的诸多高级功能的实现,如RMI(Remote Method Invocation), 对象/关系映射,跨越多个数据源的分布式事务等;
JMS(Java Message Service):高性能异步消息服务,实现JAVA EE应用程序与非JAVA程序的“透明”通信;
JMX(Java Management Extensions):在程序运行时对其进行交互式监控和管理的机制;
JTA(Java Transaction API):允许应用程序在自身的一个或多个组件中平滑地处理错误的机制;
JavaMail:通过工业标准的POP/SMTP/IMAP协议发送和接收邮件的机制;

Servlet:服务端小程序文件,这种类使得Java代码可以借助与CGI技术在服务器端运行,并基于HTTP协议或者其他协议跟客户端进行交互响应客户端请求。servlet程序运行时加载了servlet类本身的各种应用程序,并且基于CGI技术或其他技术实现与客户端进行交流。这种运行环境就叫做servlet Container(servlet 容器)。

servlet 容器事实上所实现的功能就是当客户端发送一个URL请求时服务器端Java编写的应用程序代码在servlet容器中运行,并将运行结果以一个HTML格式组织的页面返回给客户端。但是应用程序原本是只能识别程序代码的。既然返回给客户端的页面是以HTML格式组织的,就必然要求该应用程序有打印页面标题的功能。servlet在这一功能的实现中仅仅提供了一个规范,早期程序员基于servlet开发网络页面时是需要将页面标题等文本信息硬编码到页面程序中的。这种页面开发方式极其繁琐。而Java 2 EE中另一技术JSP则解决了这一问题。

JSP: Java Server Page,换种说法JSP可以理解为servlet的前端,JSP实现了HTML格式的文本可以直接嵌入在Java代码中。JSP代码内嵌在Java代码中以后会被程序代码自动识别并翻译成servlet语言在servlet容器中运行使用程序进行输出实现与客户端的交互。

<html>
				<title>TITLE</title>
				<body>
					<h1>...</h1>
					<%
						... java code...
					%>
				</body>
			</html>
#.jsp -->jasper--> .java --> javac --> .class --> jvm 
#注意:基于jasper将静态输出的数据转为java代码进行输出,结果为servlet规范的代码

因此要部署任何以JSP所研发的应用程序,实现JSP最终以Java代码的形式运行,就必须要有Java代码运行的容积:

  1. java VM (Java 虚拟机)
  2. Jre(Java运行环境)
  3. JDK:java代码编译工具等...

JSP实现HTML格式的文本嵌入在servlet代码中被自动识别,servlet使得Java代码可以基于CGI技术通过HTTP协议与客户端进行交互。servlet类作为类文件被加载到Java虚拟机的执行引擎中实现Java代码的运行。这一系列的组合实现被成为Web Container。

Web Container

商业实现:
WebSphere, WebLogic, Oc4j, Glassfish, Geronimo, JOnAS, JBoss, ...
开源实现:
Tomcat, Jetty, Resin, ...
Tomcat: Java 2 EE技术体系的不完整实现;

Tomcat

Tomcat的核心组件:

catalina:servlet container
coyote:http connection
jasper:JSP Engine

Tomcat 的开发语言:JAVA

结构原理

Tomcat Instance:运行中的tomcat进程(Java进程)

图中的各种组件只是Tomcat众多组件中的几个比较重要的组件,在Tomcat中每个组件都由“类”来实现,而有些类的实现还不止一种。

组件分类:

顶级类组件:server
服务类组件:service
容器类组件:可以部署Webapp的组件,例如:engine,host,context
连接器组件:connector
被嵌套类组件:valve,logger,realm

xml格式:

<Server>
	<Service>
		<connector/>
		<connector/>
		...
			<Engine>
				<Host>
					<Context/>
					<Context/>
					...
				</Host>
				<Host>
					...
				</Host>
			</Engine>
	</Service>
</Server>

组件

server

一个server对应一个tomcat实例,一个操作系统可以运行多个tomcat实例,前提是这多个tomcat监听的端口不同,各自的配置文件不冲突,对应的文档不再同一目录下。
tomcat的单实例内存不能大于32G,当tomcat的单实例大于32G时会出现内存管理问题。因此当生产当中一台服务器的运行内存有128G或者更大时,而该服务器又专门用来提供tomcat服务。这个时候就可以考虑在一台主机上运行多个tomcat实例。
instance,即表现出的一个java进程;监听在8005端口,只接收“SHUTDOWN”。各server监听的端口不能相同,因此,在同一物理主机启动多个实例时,需要修改其监听端口为不同的端口;
建议停止该服务,port=-1,或者将命令改为其他字符

service

如上图中所示Tomcat最核心的组件就是Engine,所有的程序最终都要以Java代码的形式在Engine中运行,但是Engine没有通过B/S协议与客户端进行交互的能力。Engine要依赖于连接器Connetctor接入解析用户的请求,并将运行结果构建成响应报文来响应客户端。但是Engine与连接器Connetctor之间也无法进行交互,所以就需要一个辅助的组件让连接器与Engine之间也建立起关联关系,这个辅助组件就叫做service。
通常来讲一个连接器只服务于一个Engine,但是一个Engine可以有多个连接器。举例来讲:用户通过http协议发来的请求由http连接器负责解析和处理,并通过service所关联的Engine建立起与Engine的交互关系将用户的请求发送给Engine。同样的如果用户的请求通过https协议发送给服务器,那么就需要用https的连接器通过service关联的同一个Engine建立与Engine的交互然后发送用户请求。
可想而知service的功能概括来讲就是将若干个连接器与一个engine建立关联关系,所以要求一个service中只能有一个Engine。
属性:

className:org.apache.catalina.core.StandardService
#用于实现service的类名
name:此服务的名称,默认为Catalina;

Engine

Tomcat的核心组件,用于运行JSP或者servlet代码;即servlet引擎,其内部可以一个或多个host组件来定义站点; 通常需要通过defaultHost来定义默认的虚拟主机;
属性:

name=
defaultHost="localhost"		#默认引擎
jvmRoute=					#后端服务的唯一标识

Connector

接入并解析用户请求,将请求映射为Engine中运行的代码;之后将运行结果构建成响应报文;
常见的有三类http/https/ajp;
进入tomcat的请求可分为两类:
(1) standalone : 请求来自于客户端浏览器;
(2) 由其它的web server反代:来自前端的反代服务器;
nginx --> http connector --> tomcat
httpd(proxy_http_module) --> http connector --> tomcat

AJP(Apache JServ Protocol):
AJP是面向数据包的基于TCP/IP的协议,它在Apache和Tomcat的实例之间提供了一个专用的通信信道。目前常用AJP协议的版本是1.3,它主要有以下特征:

  1. 在快速网络有着较好的性能表现,支持数据压缩传输;
  2. 支持SSL,加密及客户端证书;
  3. 支持Tomcat实例集群;
  4. 支持在apache和tomcat之间的连接的重用;

httpd(proxy_ajp_module) --> ajp connector --> tomcat
httpd(mod_jk) --> ajp connector --> tomcat ajp协议作为二进制协议工作效率要比http文本协议高

属性:

port="8080" 
protocol="AJP/1.3"	#http协议不用标ajp协议需要标注
connectionTimeout="20000"等待客户端发送请求的超时时间
address:监听的IP地址;默认为本机所有可用地址;
maxThreads:最大并发连接数,默认为200;
enableLookups:是否启用DNS查询功能;#一般关闭
acceptCount:等待队列的最大长度;
redirectPort:如果某连接器支持的协议是HTTP,当接收客户端发来的HTTPS请求时,则转发至此属性定义的端口;
secure:
sslProtocol:pls1.1,1.2,tls

下面是一个定义了多个属性的SSL连接器:

<Connector port="8443"
    maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
    enableLookups="false" acceptCount="100" debug="0" scheme="https" secure="true"
    clientAuth="false" sslProtocol="TLS" />

Host

可以将Tomcat中的Engine来类比成一个Web server,相对于Engine来说Host就类似于httpd中的虚拟主机;
示例:

<Host
name="localhost"  
appBase="webapps"	#类似documentroot 定义主站点
unpackWARs="true" 	#自动展开功能	
autoDeploy="true">	#自动部署
</Host>

常用属性说明:
(1) appBase:此Host的webapps的默认存放目录,指存放非归档的web应用程序的目录或归档的WAR文件目录路径;可以使用基于$CATALINA_BASE变量所定义的路径的相对路径;
(2) autoDeploy:在Tomcat处于运行状态时,将某webapp放置于appBase所定义的目录中时,是否自动将其部署至tomcat;

示例:

<Host name="a.com" appBase="/appdata/webapps" unpackWARs="true" autoDeploy="true">
</Host>
\# mkdir -pv /appdata/webapps
\# mkdir -pv /appdata/webapps/ROOT/{lib,classes,WEB-INF}

提供一个测试页即可;

主机别名定义:
如果一个主机有两个或两个以上的主机名,额外的名称均可以以别名的形式进行定义,如下:

<Host name="www.a.com" appBase="webapps" unpackWARs="true">
  <Alias>a.com</Alias>
</Host>

Context:

类似于httpd中的alias,一般如果一个网页资源是一个独立的运行程序的话那么就需要将他归为Context中单独配置管理。

常用的属性定义有:

  1. docBase:相应的Web应用程序的存放位置;也可以使用相对路径,起始路径为此Context所属Host中appBase定义的路径;切记,docBase的路径名不能与相应的Host中appBase中定义的路径名有包含关系,比如,如果appBase为deploy,而docBase绝不能为deploy-bbs类的名字;
  2. path:相对于Web服务器根路径而言的URI;如果为空“”,则表示为此webapp的根路径;如果context定义在一个单独的xml文件中,此属性不需要定义;
  3. reloadable:是否允许重新加载此context相关的Web应用程序的类;默认为false;
    示例:
<Context path="/PATH" docBase="/PATH/TO/SOMEDIR" reloadable=""/>

Valve组件:

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
				
Valve存在多种类型:
定义访问日志:org.apache.catalina.valves.AccessLogValve
定义访问控制:org.apache.catalina.valves.RemoteAddrValve 
<Valve className="org.apache.catalina.valves.RemoteAddrValve" deny="172\.16\.100\.67"/>

运行模式

standalone:
通过内置的web server(http connector)来接收客户端的请求;
proxy:有专门的web server服务客户端的http请求;
in-process:部署于同一主机;
network:部署于不同主机;

安装Tomcat

Tomcat的安装有三种方式,光盘中base源中有rpm包。或者下载编译过之后的安装包。也可以下载源码编译安装,由于java的体质所以tomcat编译之前就是Java源代码,编译之后就是类文件。tomcat的编译需要专门的java编译器来进行编译,这点与一般的软件用GCC编译不同。

安装JDK

Java程序的运行需要Java运行环境需要先下载JVM
OpenJDK:
java-VERSION-openjdk:
The OpenJDK runtime environment.
java-VERSION-openjdk-headless:
The OpenJDK runtime environment without audio and video support.
java-VERSION-openjdk-devel:
The OpenJDK development tools.
CentOS 7:
VERSION:1.6.0, 1.7.0, 1.8.0
注意:多版本并存时,可使用 alternatives命令设定默认使用的版本;

Oracle JDK:
安装相应版本的rpm包;
jdk-VERSION-OS-ARCH.rpm
例如:jdk-1.8.0_25-linux-x64.rpm

注意:安装完成后,要配置JAVA_HOME环境变量,指向java的安装路径;
OpenJDK:
JAVA_HOME=/usr
Oracle JDK:
JAVA_HOME=/usr/java/jdk_VERSION

yum安装:

[root@CentOS6 ~]#yum list *java*
#看JDK包名
[root@CentOS6 ~]#yum install java-1.8.0-openjdk.x86_64

查看JDK版本:

[root@CentOS6 ~]#java -version
openjdk version "1.8.0_121"
OpenJDK Runtime Environment (build 1.8.0_121-b13)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)

安装Tomcat

yum安装:

[root@CentOS6 ~]#yum install tomcat

二进制包安装:

Tomcat binary release:
# tar xf apache-tomcat-VERSION.tar.gz  -C /usr/local/
# cd /usr/local
# ln -sv apache-tomcat-VERSION  tomca

配置环境变量:

/etc/profile.d/tomcat.sh 
export CATALINA_BASE=/usr/local/tomcat
export PATH=$CATALINA_BASE/bin:$PATH

除非要使tomcat监听在小于1024的端口上时需要以管理员的身份去运行tomcat,否则出于安全考虑都应该使用普通用户的身份去运行tomcat,使用普通用户运行tomcat是要注意文件权限问题。

目录结构

bin:脚本,及启动时用到的类;
conf:配置文件目录;
lib:库文件,Java类库,jar;
logs:日志文件目录;
temp:临时文件目录;
webapps:webapp的默认目录;
work:工作目录,存放编译后的字节码文件;

配置文件构成

server.xml:主配置文件;

web.xml:每个webapp只有“部署”后才能被访问,它的部署方式通常由web.xml进行定义,其存放位置为WEB-INF/目录中;此文件为所有的webapps提供默认部署相关的配置;

context.xml:每个webapp都可以使用的配置文件,它通常由专用的配置文件context.xml来定义,其存放位置为WEB-INF/目录中;此文件为所有的webapps提供默认配置;

tomcat-users.xml:用户认证的账号和密码文件;角色(role),用户(User);此文件在tomcat启动时被装入内存;

catalina.policy:当使用-security选项启动tomcat时,用于为tomcat设置安全策略;

catalina.properties:Java属性的定义文件,用于设定类加载器路径,以及一些与JVM调优相关参数;

logging.properties:日志系统相关的配置;

JSP WebAPP的组织结构:

/: webapps的根目录
index.jsp:主页;
WEB-INF/:当前webapp的私有资源路径;通常用于存储当前webapp的web.xml和context.xml配置文件;
META-INF/:类似于WEB-INF/;
classes/:类文件,当前webapp所提供的类;
lib/:类文件,当前webapp所提供的类,被打包为jar格式;

webapp归档格式:

.war:webapp应用程序;
.jar:EJB的类打包文件(类库);
.rar:资源适配器类打包文件;
.ear:企业级应用程序;

手动部署一个webapp

部署(deploy)webapp的相关操作:
deploy:将webapp的源文件放置于目标目录(网页程序文件存放目录),配置tomcat服务器能够基于web.xml和context.xml文件中定义的路径来访问此webapp;将其特有的类和依赖的类通过class loader装载至JVM;
部署有两种方式:
自动部署:auto deploy
手动部署:
冷部署:把webapp复制到指定的位置,而后才启动tomcat;
热部署:在不停止tomcat的前提下进行部署;
部署工具:manager、ant脚本、tcd(tomcat client deployer)等;
undeploy:拆除(反部署),停止webapp,并从tomcat实例上卸载webapp;
start:启动处于停止状态的webapp;
stop:停止webapp,不再向用户提供服务;其类依然在jvm上;
redeploy:重新部署;

步骤:

  1. 部署目录结构
[root@CentOS6 webapps]#mkdir -pv myapp/{lib,classes,WEB-INF,META-INF}
mkdir: created directory `myapp'
mkdir: created directory `myapp/lib'
mkdir: created directory `myapp/classes'
mkdir: created directory `myapp/WEB-INF'
mkdir: created directory `myapp/META-INF'
[root@CentOS6 ~]#tree /var/lib/tomcat/webapps/myapp/
/var/lib/tomcat/webapps/myapp/
├── classes
├── lib
├── META-INF
└── WEB-INF
  1. 配置一个测试页面:
[root@CentOS6 myapp]#vim index.jsp
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
        <head>  
        <title>Test Page</title>
        </head> 
        <body>  
                <% out.println("hello world");  %>
        </body>
</html>         
  1. 访问测试

此时用浏览器去访问我们的部署的程序页面速度还是比较快的,因为我们的页面比较简单,编译过程很简单。
当我们使用浏览器对之前部署的程序发送访问请求时,我们部署的JSP格式的文件首先要被转换成成.java结尾的servlet文件,之后再编译成.class结尾的类文件。其编译结果就在/usr/share/tomcat/work/Catalina/loca
lhost目录中:

[root@CentOS6 ~]#tree /usr/share/tomcat/work/
/usr/share/tomcat/work/
└── Catalina	#引擎Engine
    └── localhost	#虚拟主机名
        ├── _
        │   └── org
        │       └── apache
        │           └── jsp
        │               ├── index_jsp.class
        │               └── index_jsp.java
        ├── docs
        ├── examples
        ├── myapp	#应用程序
        │   └── org	
        │       └── apache
        │           └── jsp #类名:rog.apache.jsp
        │              ├── index_jsp.class
        │              └── index_jsp.java #servlet文件
        └── sample
#实现JSP格式的程序文件可以在Java虚拟机中运行所调用的类有多种版本,为了区分各个类在全局唯一性所以规定所有的常被公共调用的类一般以公司或者组织的域名命名,这里的类名rog.apache.jsp也遵循次规定。

Java程序在第一次被访问以后会被编译成类文件,同时将编译结果装载在Java虚拟机上,所以之后的访问就不用在反复执行编译运行过程而是直接从Java虚拟机中加载。
另外Java虚拟机会自动监控程序的源文件是否发生了改变,如果源文件发生改变他会自动的将源文件转化成servlet文件再编译成class类文件重新运行源文件。以供用户访问。

Tomcat自带的管理应用

如果要使用Tomcat自带的管理应用,就必须下载安装tomcat的admin工具包。
访问Tomcat的原始默认页面,点击Manger App 会弹出认证窗口。点击取消会响应一个401错误页面。根据错误页面提示进行相应配置。

给出了四个用户,分别对应不同的权限:
manager-gui - allows access to the HTML GUI and the status pages
同时具有web页面管理权限以及web页面服务器状态访问权限
manager-script - allows access to the text interface and the status pages
只允许访问文本接口和web界面的服务器状态界面
manager-jmx - allows access to the JMX proxy and the status pages
允许访问服务远程接口
manager-status - allows access to the status pages only
只能访问服务状态信息页面

点击Host Manager也会返回401页面,设定与之前相同
两个角色:
admin-gui - allows access to the HTML GUI
admin-script - allows access to the text interface

在/etc/tomcat/tomcat-users.xml文件中设定用户:

[root@CentOS6 ~]#vim /etc/tomcat/tomcat-users.xml
<role rolename="manager-gui,admin-gui"/>
<user username="tomcat" password="0315" roles="manager-gui"/>
#设定用户角色既属于manager-gui又属于admin-gui
#设定用户名tomcat口令0315

LNMT

Linux Nginx MySQL Tomcat

Client (http) --> nginx (reverse proxy)(http) --> tomcat (http connector)

location / {
	proxy_pass http://tc1.a.com:8080;
}

location ~* \.(jsp|do)$ {
	proxy_pass http://tc1.a.com:8080;
}

LAMT

Linux Apache(httpd) MySQL Tomcat
httpd的代理模块:
proxy_module
proxy_http_module:适配http协议客户端;
proxy_ajp_module:适配ajp协议客户端;

proxy_http_module代理配置示例:

<VirtualHost \*:80>
	ServerName      tc1.a.com
	ProxyRequests Off
	ProxyVia        On		#显示前端代理服务器信息
	ProxyPreserveHost On
	<Proxy *>
		Require all granted
	</Proxy>
		ProxyPass / http://tc1.a.com:8080/
		ProxyPassReverse / http://tc1.a.com:8080/ 
	<Location />
		Require all granted
	</Location>
</VirtualHost>

proxy_ajp_module代理配置示例:

<VirtualHost \*:80>
	ServerName      tc1.a.com
	ProxyRequests Off
	ProxyVia        On
	ProxyPreserveHost On
	<Proxy *>
		Require all granted
	</Proxy>
		ProxyPass / ajp://tc1.a.com:8009/ 
		ProxyPassReverse / ajp://tc1.a.com:8009/ 
	<Location />
		Require all granted
	</Location>
</VirtualHost>

Tomcat集群(memcached)

任何应用程序的集群会话保持都是在所难免的,Tomcat要想实现集群话,必然要面对的前提就是会话保持。
Tomcat Cluster(session) session绑定的三种方法:
分布式场景:
(1) session sticky
会话绑定:对IP、URL或者cookie进行hash
映射法
一致性hash算法
会话绑定的方式缺陷在于当Tomcat集群中任何一台Tomcat服务器出现故障那么保存在这台服务器的session就会丢失,也就是说基于这种方式实现的会话绑定集群的所有节点都是一个单点。
(2) session cluster 会话集群适用范围有限
将多个tomcat服务器部署为一个集群,各个tomcat服务器之间通过单播或者多播的方式将会话信息传递给其他服务器。
会话集群的方式缺陷在于在传递会话信息时会占用大量的网络带宽,另外每台服务器都保存所有会话信息造成信息冗余,浪费资源
tomcat delta manager
(3) session server 会话服务器
使用高性能的kv服务器
memcached:将数据缓存在内存中,单点问题容易造成数据丢失
redis:数据存储在磁盘当中

session sticky

Nginx-Tomcat

一、首先在两台虚拟主机上部署tomcat服务:

  1. JDK方面在两台主机上分别使用版本自带的JDK
[root@Centos6 ~]#java -version
openjdk version "1.8.0_121"
OpenJDK Runtime Environment (build 1.8.0_121-b13)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)
  1. tomcat程序则可以选择使用官网下载的较新版本的二进制程序包,分别部署:
[root@Centos6 ~]#tar xf  apache-tomcat-8.0.23.tar.gz -C /usr/local/		#解压包
[root@Centos6 ~]#ln -sv /usr/local/tomcat /usr/local/apache-tomcat-8.0.23/		#创建软链接
#在原有的engine字段中配置虚拟主机:
[root@Centos6 ~]#vim /usr/local/tomcat/conf/server.xml
  <Host name="web1.huxiaoqi.com" appBase="/app/webapps" autoDeploy="ture">
        <Context path="" docBase="ROOT" />
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/app/logs"
               prefix="web1_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
      </Host>
#这里并将默认访问host改为新部署的web1.huxiaoqi.com
  1. 部署tomcat程序页面
#配置目录结构如下:
[root@Centos6 ]#tree /app
├── logs
└── webapps
    └── ROOT
        ├── classes
        ├── index.jsp
        ├── lib
        ├── META-INF
        └── WEB-INF
[root@Centos6 ~]#vim /app/webapps/ROOT/index.jsp 
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
        <head>
        <title>web1 Page</title>
        </head>
        <body>
                <% out.println("hello world,this is web1");  %>
        </body>
</html>
#tomcat程序测试页面如上
  1. 另一台tomcat服务器配置步骤相同,只需将测试页面内容加以区分即可,需要注意的是我们在目前手动配置的情况下可以将目录结构以及配置文件直接复制给其他服务器以减少重复劳动。但是复制目录时应注意使用cp -a 去复制。。

二、 配置前端nginx服务器:
修改nginx配置文件:

upstream tcsrvs {
        ip_hash;	#基于IP会话绑定
        server web1.huxiaoqi.com:8080;
        server web2.huxiaoqi.com:8080;
    }
    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;
    location / {
    }
    location ~*\.(jsp|do)$ {
    #动态请求既以.jsp结尾或.do结尾的发往后端tomcat服务器
        proxy_pass http://tcsrvs;
    }
    }

三、访问测试:
可以先将前端nginx调度器的配置文件中的基于IP会话绑定注释掉,然后测试访问。如果访问效果是两台tomcat服务器轮询调度就说名配置成功。
然后再加上IP会话绑定设定,第一次访问前端的nginx服务器http服务的动态资源nginx基于IP的会话绑定后就会把该客户端以后的所有动态请求都固定的调度到某台tomcat服务器。

Apache-Tomcat

基于apache向后端负载均衡tomcat有三种三种方案可选择,主要以前两种为主,基于http协议以及ajp协议,第三种早期的tomcat负载均衡集群有用到,目前以不多用。
(1)apache:
mod_proxy
mod_proxy_httpd
要实现负载均衡功能还要再用到mod_proxy_balancer模块
tomcat:
http connector

(2)apache:
mod_proxy
mod_proxy_ajp
同样要实现负载均衡功能还要再用到mod_proxy_balancer模块
tomcat:
ajp connector

(3)apache:mod_jk自身既能反带又能做负载均衡
mod_jk
tomcat:
ajp connector

LAMT基于http协议

  1. 配置前端http服务
[root@CentOS6 ~]#vim /etc/httpd/conf.d/virtualhost.conf
<proxy balancer://tcsrvs>
    BalancerMember http://192.168.45.11:8080 loadfactor=10 route=TomcatA
    BalancerMember http://192.168.45.12:8080 loadfactor=10 route=TomcatB
    ProxySet lbmethod=byrequests
</Proxy>
<VirtualHost *:80>
    ServerName web1.huxiaoqi.com
    ProxyVia On	#显示代理主机IP信息
    ProxyRequests Off	#关闭正向代理
    ProxyPreserveHost On
    <Proxy *>
            Require all granted
    </Proxy>
    ProxyPass / balancer://tcsrvs/
    ProxyPassReverse / balancer://tcsrvs/
    <Location />
            Require all granted
    </Location>
</VirtualHost>
#注意:如果使用的apache是2.4以前的版本则无须授权访问,既将所有的Require all granted字段删掉

关于如上apache指令的说明:
ProxyPreserveHost {On|Off}:如果启用此功能,代理会将用户请求报文中的Host:行发送给后端的服务器,而不再使用ProxyPass指定的服务器地址。如果想在反向代理中支持虚拟主机,则需要开启此项,否则就无需打开此功能。

ProxyVia {On|Off|Full|Block}:用于控制在http首部是否使用Via:,主要用于在多级代理中控制代理请求的流向。默认为Off,即不启用此功能;On表示每个请求和响应报文均添加Via:;Full表示每个Via:行都会添加当前apache服务器的版本号信息;Block表示每个代理请求报文中的Via:都会被移除。

ProxyRequests {On|Off}:是否开启apache正向代理的功能;启用此项时为了代理http协议必须启用mod_proxy_http模块。同时,如果为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将请求发送至后端服务器得到错误响应时等待多长时间以后再重试。单位是秒钟。

如果Proxy指定是以balancer://开头,即用于负载均衡集群时,其还可以接受一些特殊的参数,如下所示:
◇lbmethod:apache实现负载均衡的调度方法,默认是byrequests,即基于权重将统计请求个数进行调度,bytraffic则执行基于权重的流量计数调度,bybusyness通过考量每个后端服务器的当前负载进行调度。
◇ maxattempts:放弃请求之前实现故障转移的次数,默认为1,其最大值不应该大于总的节点数。
◇ nofailover:取值为On或Off,设置为On时表示后端服务器故障时,用户的session将损坏;因此,在后端服务器不支持session复制时可将其设置为On。
◇ stickysession:调度器的sticky session的名字,根据web程序语言的不同,其值为JSESSIONID或PHPSESSIONID。

  1. 后端tomcat服务配置基本与上一实验中配置类同
    分别在两台后端的tomcat服务器配置文件中定义节点唯一标识:
[root@Centos6 ~]#vim /usr/local/tomcat/conf/server.xml
<Engine name="Catalina" defaultHost="web1.huxiaoqi.com" jvmRoute="TomcatA">
#在engine组件中添加jvmroute字段,把两个tomcat节点加以区分
  1. 配置tomcat测试页面:
[root@Centos6 ~]#vim /app/webapps/ROOT/index.jsp 
<%@ page language="java" %>
<html>
        <head><title>TomcatA</title></head>
        <body>
                <h1><font color="red">TomcatA.huxiaoqi.com</font></h1>
                <table align="centre" border="1">
                        <tr>
                                <td>Session ID</td>
                        <% session.setAttribute("huxiaoqi.com","huxiaoqi.com"); %>
                                <td><%= session.getId() %></td>
                        </tr>
                        <tr>
                                <td>Created on</td>
                                <td><%= session.getCreationTime() %></td>
                        </tr>
                </table>
        </body>
</html>
#注意两台tomcat节点要有区分
  1. 实现会话绑定配置方法:
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<proxy balancer://tcsrvs>
	BalancerMember http://192.168.45.11:8080 route=TomcatA loadfactor=10
	BalancerMember http://192.168.45.12:8080 route=TomcatB loadfactor=10
	ProxySet lbmethod=byrequests
	ProxySet stickysession=ROUTEID
</Proxy>
#在虚拟主机配置文件中定义报文头部添加ROUTEID信息。
#在proxy字段中定义根据报文头部的routeID实现会话绑定

LAMT基于ajp协议

实现基于ajp协议apache前端调度tomcat方法与http协议的方法大体相同:

[root@CentOS6 ~]#vim /etc/httpd/conf.d/virtualhost.conf
<proxy balancer://tcsrvs>
    BalancerMember ajp://192.168.45.11:8009 loadfactor=10 route=TomcatA
    BalancerMember ajp://192.168.45.12:8009 loadfactor=10 route=TomcatB
    ProxySet lbmethod=byrequests
</Proxy>
#只需将协议修改为ajp,监听端口修改为8009即可

ajp协议实现会话绑定:

[root@CentOS6 ~]#vim /etc/httpd/conf.d/virtualhost.conf 
#Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<proxy balancer://tcsrvs>
    BalancerMember ajp://192.168.45.11:8009 loadfactor=10 route=TomcatA
    BalancerMember ajp://192.168.45.12:8009 loadfactor=10 route=TomcatB
#   ProxySet lbmethod=byrequests
    ProxySet stickysession=ROUTEID
</Proxy>
#只需定义ProxySet stickysession=ROUTEID即可

web页面管理集群

apache balancer模块中有关于集群管理接口,用过设置可以实现基于web界面直接对集群进行管理
启用管理接口:

<Location /balancer-manager>
	SetHandler balancer-manager
	ProxyPass !
	Require all granted
</Location>

apache:mod_jk

此种方式依然淘汰不做讨论

Session Cluster

tomcat内部存在组件session manager用来自动实现本地tomcat集群的会话管理,主要实现方式共有四种:
StandarManager :标准会话管理器
PersistenManager:持久会话管理器
DeltaManager:
BackupManager:

标准会话管理器(StandarManager)

配置方法:

<Manager className="org.apache.catalina.session.StandardManager"maxInactiveInterval="7200"/>
#在HOST组件中配置就对该主机所有应用程序有效如果只针对某个应用程序生效则应设置在context组件中。
#maxActiveSessions:最多允许的活动会话数量,默认为-1,表示不限制; 
#maxInactiveInterval:非活动的会话超时时长,默认为60s;
#pathname:会话文件的保存目录;

StandarManager,标准会话管理器也可以理解为简单的会话管理器,该种方法仅仅实现tomcat具有会话保持的功能,但是无论是对与会话信息的连续保存,以及会话保持的高可用性都无法有效解决。
标准会话管理器的工作原理是周期性的将访问该台主机的用户会话信息保存在本地文件系统中,默认保存于$CATALINA_HOME/work/Catalina///下的SESSIONS.ser文件中。

持久会话管理器(PersistentManager):

将会话数据保存至持久存储中,并且能在服务器意外中止后重新启动时重新加载这些会话信息。持久会话管理器支持将会话保存至文件存储(FileStore)或JDBC存储(JDBCStore)中。
该种方法虽然可以实现将用户会话信息永久存储在一个远程的公用存储之上,但是这些会话信息是不能共享的。也就是说持久会话管理器中,后端的每台Tomcat服务器都将访问该台服务器的会话信息存储在本地或者远程的集中存储区间。但是每台服务器需要去读取这些会话信息时只能去读取自己存储的会话信息,而不能读取其他主机存储的会话信息。这样一来如果后端tomcat集群中某台服务器出现故障,还是无法实现高可用性效果。
该种方法逻辑简单,对于小型的tomcat服务集群具有可用性。

保存至文件中的示例:

<Manager className="org.apache.catalina.session.PersistentManager"
  saveOnRestart="true">
  <Store className="org.apache.catalina.session.FileStore"
    directory="/data/tomcat-sessions"/>
</Manager>

每个用户的会话会被保存至directory指定的目录中的文件中,文件名为.session,并通过后台线程每隔一段时间(checkInterval参数定义,默认为60秒)检查一次超时会话。

保存至JDBCStore中的示例:

<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true">
  <Store className="org.apache.catalina.session.JDBCStore"
    driverName="com.mysql.jdbc.Driver"
    connectionURL="jdbc:mysql://localhost:3306/mydb?user=jb;password=pw"/>
</Manager>

BackupManager(备份会话管理器)

该方法在以上两种会话管理理念的基础之上有所改进,在备份会话管理中后端的Tomcat集群服务器没台服务器都有一台备用机器,同时同步会话信息以提高会话管理高可用性。但是该方法逻辑较为复杂极难实现标准化管理。

DeltaManager(边际会话管理器)

将后端的多个Tomcat服务器节点通过特殊的信道以多播的方式传递信号建立成一个Tomcat服务集群,该方法也是Tomcat session cluster最常用的实现方法。
在这种会话管理的方法中,后端的所有Tomcat服务器收集到访问本机的客户端会话信息以后都会通过一个专门的信道通过多播的方式发送给集群中其他主机,集群中的其他主机也会共同监听同一个多播端口,收集所有主机发来的会话信息并保存。
该方法虽然实现了会话管理的高可用性,但是当集群过于庞大时会话信息的传播将带来巨大的网络压力,同时每台主机都保存所有会话信息也浪费了资源。
该方法配置方法可参考https://tomcat.apache.org/tomcat-8.0-doc/cluster-howto.html#For_the_impatient
注意不同版本的Tomcat配置写法不同

session server

前提:
两个tomcat节点:172.16.100.7(tomcatA.a.com),172.16.100.8(tomcatB.a.com)
两个memcached节点:172.16.100.9, 172.16.100.10
一个负载均衡节点:172.16.100.6

Clients-->172.16.100.6-->(tomcatA, tomcatB)

memcached-session-manager项目地址,http://code.google.com/p/memcached-session-manager/, https://github.com/magro/memcached-session-manager

下载如下jar文件至各tomcat节点的tomcat安装目录下的lib目录中,其中的${version}要换成你所需要的版本号,tc${6,7,8}要换成与tomcat版本相同的版本号。
memcached-session-manager-${version}.jar
memcached-session-manager-tc${6,7,8}-${version}.jar
spymemcached-${version}.jar
msm-javolution-serializer-${version}.jar
javolution-${version}.jar

分别在两个tomcat上的某host上定义一个用于测试的context容器,并在其中创建一个会话管理器,如下所示:

           <Context path="/test" docBase="/usr/local/tomcat/webapps/test" reloadable="true">
              <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
                memcachedNodes="n1:172.16.100.9:11211,n2:172.16.100.10:11211"
                failoverNodes="n1"
                requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
                transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"
              />
             </Context>

分别为两个context提供测试页面:

tomcatA:
# mkdir -pv /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
# vim /usr/local/tomcat/webapps/test/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>

tomcatB:
# mkdir -pv /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
# vim /usr/local/tomcat/webapps/test/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>

在172.16.100.6上配置反向代理的负载均衡内容,类似如下所示:

<Proxy balancer://tomcat>
    BalancerMember  http://172.16.100.7:8080 loadfactor=1
    BalancerMember  http://172.16.100.8:8080 loadfactor=1
    ProxySet  lbmethod=byrequests
</Proxy>

ProxyVia Off
ProxyRequests Off
ProxyPass / balancer://tomcat/
ProxyPassReverse / balancer://tomcat/
<Proxy *>
    Order Allow,Deny
    Allow From all
</Proxy>

<Location />
    Order Allow,Deny
    Allow From all
</Location>

测试结果,在浏览器中访问http://172.16.100.6/test,结果如下所示,其session ID在负载均衡环境中保持不变。

TomcatA.a.com

Session ID 4DD0340CE6294FF2BBE802CD4CD039EC-n2
Created on 1399890838103

TomcatB.magedu.com

Session ID 4DD0340CE6294FF2BBE802CD4CD039EC-n2
Created on 1399890838103

posted @ 2017-11-12 21:54  奇哥与李妞  阅读(280)  评论(0编辑  收藏  举报