Tomcat

Tomcat

JAVA基础概念

编程语言

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

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

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

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库的运行以及系统调用上的某写功能的运行。linux

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

三个技术流派

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实现);git

Java 2 ME

Mobile Edition用途有限,不作介绍程序员

Java 2 EE

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

而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

1、首先在两台虚拟主机上部署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 去复制。。

2、 配置前端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;
    }
    }

3、访问测试:
能够先将前端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