Tomcat详解

1. Tomcat简介

1.1 Tomcat的核心组件

  • catalina:servlet container(servlet的容器)
  • Coyote:http connection(http连接器)
  • Jasper:JSP Engine(jsp引擎,jsp翻译器)

注意:jsp --> jasper --> servlet --> complie --> bytecodes --> jvm

  • Tomcat的开发语言:JAVA
  • Tomcat要运行在JVM上,而对于JDK而言,它的JVM程序名就叫做 java
    • 所以启动tomcat之后发现不了tomcat运行在哪,tomcat体现为一个java进程
    • 这个java进程不过在运行时装载了tomcat的一些代码而已

1.2 Tomcat Instance

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

1.2.1 Tomcat实例

  • Server:一个tomcat实例(tomcat instance),运行中的tomcat进程(java进程)
  • Service:用于将connector关联至engine组件,一个service只能包含一个engine组件和 一个或多个connector组件
  • Engine:Tomcat的核心组件,用于运行jsp或servlet代码(整个jsp代码的运行都要靠Engine来实现)
  • Connector:
    • 接受并解析用户请求,将请求映射为Engine中运行的代码,之后,将运行结果构建成响应报文响应给客户端
    • http,ajp
  • Host:类似httpd中的虚拟主机
  • Context:类似于httpd中的alias
    • 如http://www.hgzerowzh.com/pma/
    • pma将会被独立的定义成一个Context,并拥有自己的环境

1.2.2 各个组件

每个组件都由“类”来实现,有些组件的实现还不止一种

  • 顶级类组件:server
  • 服务类组件:service
  • 容器类组件:即可以部署webapp的组件,engine,host,context
  • 连接器组件:connector
  • 被嵌套类组件:valve,logger,realm
<server>    
<
service>             <connector />             <connector />             ...             <engine>                     <host>                             <context />                             ...                     </host>                     ...             </engine>     </service> </server>

1.3 Tomcat的运行模式

1.3.1 standalone

  • 通过内置的web server(http connector)来接收客户端请求

1.3.2 proxy

  • 由专门的web server服务客户端的http请求
  • 其实就是在tomcat前面加一个nginx,然后由nginx将动态请求反代至tomcat
    • ip-process:部署于同一台主机
    • network:部署于不同主机

2. Tomcat的安装配置&webapp的部署

2.1 Tomcat的安装和启动

安装JDK:

  • 安装JDK:
    • Oracle JDK
    • OpenJDK
    • 可以直接yum安装或者在官网下载rpm包安装
  • 关于JDK的位置:
    • /usr/bin/java -> /etc/alternatives/java
      /etc/alternatives/java -> /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.232.b09-0.el7_7.x86_64/jre/bin/java
  • 配置环境变量:
    • vim /etc/profile.d/java.sh
      # openjdk1.8的路径是/usr/lib/jvm/java/bin
              export JAVA_HOME=/usr/java/latest
              export PATH=$JAVA_HOME/bin:$PATH  

安装Tomcat:

  • 下载rpm包安装或者yum安装(Tomcat7)
  • 安装后将tomcat的位置添加到环境变量中:
    • vim /etc/profile.d/tomcat.sh        
              export CATALINA_HOME=/usr/share/tomcat
              export PATH=$CATALINA_HOME/bin:$PATH
  • 如果是二进制包安装,最好将其安装到/usr/local路径下,且做好软链接

启动Tomcat:

  • bin/catalina.sh
    • start
    • start -security 安全模式下驱动tomcat,读取conf/catalina.policy的策略,并启动tomcat
    • stop 停止服务(默认是等待5秒后停止)
    • stop -n 可以指定等待n秒之后再停止
    • stop -force 强制停止服务
    • configtest 测试配置文件
    • version 显示版本
    • debug 在debugger中运行
  • 现在在CentOS7上可以直接用systemctl来运行

2.2 Tomcat目录结构和配置文件

Tomcat目录结构:

  • bin:脚本及启动时用到的类
  • lib:类库
  • conf:配置文件
  • logs:日志文件
  • webapps:应用程序默认部署目录
  • work:工作目录(Java代码编译后的结果会保存在这个目录中)
  • temp:临时文件目录

Tomcat的配置文件:

  • server.xml:主配置文件
  • context.xml:
    • 每个webapp都可以有专用的配置文件,
    • 这些配置文件通常位于webapp应用程序目录下的WEB-INF目录中,用于定义会话管理器,JDBC等
    • conf/context.xml是为各webapp提供默认配置
  • web.xml:
    • 每个webapp部署之后才能被访问,此文件则用于为所有的webapp提供默认部署相关的配置
  • tomcat-users.xml:
    • 用于认证的账号和密码配置文件
  • catalina.policy:当使用-security选项启动tomcat实例时会读取此配置文件来实现其安全运行策略
  • catalina.properties:Java属性的定义文件,用于设定类加载器路径等,以及一些JVM性能相关的调优参数
  • logging.properties:日志相关的配置信息

2.3 webapp部署

2.3.1 Java WebAPP简介

Java WebAPP组织结构:(以下是固定结构)

Java WebAPP有特定的组织形式、层次性的目录结构,主要包含了servlet代码文件,JSP页面文件、类文件、部署描述符文件等。

  • /usr/local/tomcat/webapps/app1/(若是yum安装的tomcat,这时的路径为:/usr/share/tomcat/webapps/app1
    • / :webapp的根目录
    • WEB-INF/:当前webapp的私有资源目录,通常存放当前webapp自用的web.xml
    • META-INF/:当前webapp的私有资源目录,通常存放当前webapp自用的context.xml
    • classes/:此webapp的私有类
    • lib/:此webapp的私有类,被打包为jar格式类
    • index.jsp:webapp的主页
    • 还可以有一些其他的目录用以存放如图片等资源
  • webapp归档格式:
    • .war:webapp
    • .jar:EJB的类
    • .rar:资源适配器
    • .ear:企业级应用程序

部署(deployment)webapp的相关操作:

java类应用程序的部署就是把应用程序所依赖的外部类装载到虚拟机上,而后再把这个应用程序自己的类装载到虚拟机上。

  • deploy:一个应用程序只有被部署以后才被被访问
    • 部署:
      • 将webapp的源文件放置于目标目录,并配置tomcat服务器能够基于context.xml文件中定义的路径来访问此webapp
      • 再将其特有类通过class loader装载至tomcat
    • 部署的两种方式:
      • 自动部署:auto deploy
      • 手动部署:
        • 冷部署:把webap复制到指定位置,而后才启动tomcat
        • 热部署:
          • 在不停止tomcat的前提下进行的部署
          • 需要通过部署工具进行,如manager、ant脚本、tcd(tomcat client deployer)等
  • undeploy:反部署,停止webapp,并从tomcat实例拆除其部分文件和部署名
  • stop:停止,不再向用户提供服务
  • start:启动处于“停止”状态的webapp
  • redeploy:重新部署

2.3.2 简单部署webapp

创建webapp特有的目录结构

  • 在/usr/share/tomcaat/webapps下:
    • mdir -pv myapp/{lib,classes,WEB-INF,META-INF}
  • 如果每个app没有自己的配置文件,就会从conf下继承web.xml和context.xml

提供webapp的主页文件:

vim  myapp/index.jsp
        <%@ page language="java" %>
        <%@ page import="java.util.*" %>
        <html>
                <head>
                        <title>JSP Test Page</title>
                </head>
                <body>
                        <% out.println("hello world."); %>
                </body>
        </html>

2.3.3 Tomcat自带的应用程序

  • manager app:webapp管理工具
  • host manager:Virtual Hosts管理工具

配置manager:conf/tomcat-users.xml

vim  conf/tomcat-users.xml
        <role  rolename="manager-gui"/>
        <user username="tomcat"  password="hgzero" roles="manager-gui"/>
        # 注意:修改这个文件不会立即生效,因为tomcat在启动时会将这个文件读取到内存中,因此只有重启

配置host manager

vim  conf/tomcat-users.xml
        <role  rolename="manager-gui"/>
        <role  rolename="admin-gui"/>
        <user username="tomcat"  password="hgzero" roles="manager-gui,admin-gui"/>

3. Tomcat核心组件详解

  • 组件都是定义在配置文件中的

3.1 tomcat核心组件

3.1.1 Server

  • 作用:
    • 其会让Tomcat启动一个server实例(即一个JVM),它监听在8005端口以接收shutdown命令
    • 各Server的定义不能使用同一个端口,若在同一个物理机上启动了多个Server实例,则必须配置它们使用不同的端口
    • 这个端口的定义使得管理员可以直接telnet到这个端口使用SHUTDOWN关闭此实例
    • 建议将shutdown关闭
  • Server的相关属性:
    • className:
      • 用于实现此Server容器的完全限定类的名称,默认为org.apach.catalina.core.StandardServer
    • port:
      • 接收shutdown指令的端口,默认仅允许通过本机访问,默认为8005
    • shutdown:
      • 发往此Server用于实现关闭tomcat实例的命令字符串,默认为SHUTDOWN
  • 示例:
    • <Server port="8005" shutdown="SHUTDOWN">

3.1.2 Service

  • 作用:
    • Service主要用于关联一个引擎和与此引擎相关的连接器
    • 每个连接器通过一个特定的端口和协议接收入站请求,并将其转发至关联的引擎进行处理
    • Service要包含一个引擎、一个或多个连接器
  • Service相关的属性:
    • className:
      • 用于实现service的类名,一般都是org.apache.catalina.core.StandardService
    • name:
      • 此服务的名称,默认为Catalina
  • 示例:
    • <Service name="Catalina">

3.1.3 Connector

场景:进入Tomcat的请求可以根据Tomcat的工作模式访问如下两类

  • Tomcat作为应用程序服务器:请求来自于前端的web服务器,可能是Apache、Nginx、IIS等
  • Tomcat作为独立的服务器:请求来自于web服务器

作用:

  • Tomcat应该考虑工作情形并为相应情形下的请求分别定义好需要的连接器才能正确接收来自于客户端的请求
  • 一个引擎可以有一个或多个连接器,以适应多种请求方式

常见于server.xml中的连接器类型:

  • HTTP连接器
  • SSL连接器
  • AJP 1.3连接器
  • proxy(JK)连接器

定义连接器时配置的属性:

说明:通常定义HTTP连接器时必须定义的属性只有“port”,定义AJP连接器时必须定义的属性只有“protocol”,默认的协议为HTTP

  • address:
    • 指定连接器监听的地址,默认为所有地址,即0.0.0.0
  • maxThreads:
    • 支持的最大并发连接数,默认为200
  • port:
    • 监听的端口,默认为0
  • protocol:
    • 连接器使用的协议,默认为HTTP/1.1,定义AJP协议时通常为AJP/1.3
  • redirectPort:
    • 如果某连接器支持的协议是HTTP,当接收客户端发来的HTTPS请求时,则转发至此属性定义的端口
  • connectionTimeout:
    • 等待客户端发送请求的超时时间,单位为毫秒,默认为60000,即1分钟
  • enableLookups:
    • 是否通过request.getRemoteHost()进行DNS查询以获取客户端的主机名,默认为true,强烈建议关闭此选项
  • acceptCount:
    • 设置等待队列的最大长度,通常在tomcat所有处理线程均处于繁忙状态时,新发来的请求将被放置于等待队列中

使用示例:

# 默认的HTTP连接器:
<Connector port="8080" protocol="HTTP/1.1"
        maxThreads="150" connectionTimeout="20000"
        redirectPort="8443"/>

# 定义多个属性的SSL连接器:
<Connector port="8443"
    maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
    enableLookups="false" acceptCount="100" debug="0" scheme="https" secure="true"
    clientAuth="false" sslProtocol="TLS" />

3.1.4 Engine

作用:

  • Engine是Servlet处理器的一个实例,及servlet引擎,默认为定义在server.xml中的Catalina
  • Engine需要defaultHost属性来为其定义一个接受所有发往非明确定义虚拟主机的请求的host组件

常用的属性:

  • defaultHost:
    • Tomcat支持基于FQDN的虚拟主机,可以通过在Engine容器中定义多个不同的Host组件来实现
    • 但如果此引擎的连接器收到一个发往非明确定义虚拟主机的请求时,则需要将此请求发往一个默认的虚拟主机进行处理,
    • 因此,在Engine中定义的多个虚拟主机的主机名称中至少要有一个跟defaultHost定义的主机名称同名
  • name:
    • Engine组件的名称,用于日志和错误信息记录时区别不同的引擎

使用示例:

  • <Engine name="Catalina" defaultHost="localhost">
  • 注意:Engine容器中可以包含Realm、Host、Listener和Valve子容器

3.1.5 Host

作用:

  • 位于Engine容器中用于接收请求并进行相应处理的主机或虚拟主机

常用属性:

  • appBase:
    • 此Host的webapps目录,即存放非归档的web应用程序的目录或归档后的WAR文件的目录路径
    • 可以使用基于$CATALINA_HOME的相对路径
  • autoDeploy:
    • 在Tomcat处于运行状态时放置于appBase目录中的应用程序文件是否自动进行deploy,默认为true
  • unpackWars:
    • 在启用此webapps时是否对WAR格式的归档文件先进行展开,默认为true

使用示例:

  • 基本定义格式:
    • <Host name="localhost" appBase="webapps"
              unpackWARs="true" autoDeploy="true"
              xmlValidation="false" xmlNamespaceAware="false">
      </Host>
  • 虚拟主机定义示例:
    • <Engine name="Catalina" defaultHost="localhost">
              <Host name="localhost" appBase="webapps">
                      <Context path="" docBase="ROOT"/>
                      <Context path="/bbs" docBase="/web/bss"
                      reloadable="true" crossContext="true"/>
              </Host>
              <Host name="mail.hgzerowzh.com" appBase="/web/mail">
                      <Context path="/" docBase="ROOT"/>
              </Host>
      </Engine>
  • 主机别名定义:
    • # 如果一个主机有两个或两个以上的主机名,额外的名称均可以以别名的形式进行定义:
      <Host name="www.hgzerowzh.com" appBase="webapps" unpackWARs="true">
              <Alias>hgzerowzh.com</Alias>
      </Host>

3.1.6 Context

作用:

  • Context在某些意义上类似于apache中的路径别名,一个Context定义用于标识tomcat实例中的一个Web应用程序
  • 在Tomcat6中,每一个context定义也可以使用一个单独的XML文件进行,
  • 其文件的目录为$CATALINA_HOME/conf/<engine name>/<host name>
  • 可以用于Context中的XML元素有Loader,Manager,Realm,Resources和WatchedResource

常用的属性:

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

示例:

<!-- Tomcat Root Context -->
<Context path="" docBase="/web/webapps"/>

<!-- buzzin webapp -->
<Context path="/bbs"
  docBase="/web/threads/bbs"
  reloadable="true">
</Context>

<!-- chat server -->
  <Context path="/chat" docBase="/web/chat"/>
  
<!-- darian web -->
<Context path="/darian" docBase="darian"/>

3.2 tomcat的其他组件

3.2.1 Realm组件

作用:

  • 一个Realm表示一个安全上下文,它是一个授权访问某个给定Context的用户列表和某用户所允许切换的角色相关定义的列表
  • Realm就像是一个用户和组相关的数据库
  • 定义Realm时唯一必须要提供的属性是classname,它是Realm的多个不同实现,用于表示此Realm认证的用户及角色等认证信息的存放位置

className可选的值:

  • JAASRealm:
    • 基于Java Authintication and Authorization Service实现用户认证
  • JDBCRealm:
    • 通过JDBC访问某关系型数据库表实现用户认证
  • JNDIRealm:
    • 基于JNDI使用目录服务实现认证信息的获取
  • MemoryRealm:
    • 查找tomcat-user.xml文件实现用户信息的获取
  • UserDatabaseRealm:
    • 基于UserDatabase文件(通常是tomcat-user.xml)实现用户认证
    • 它实现是一个完全可更新和持久有效的MemoryRealm,因此能够跟标准的MemoryRealm兼容,它通过JNDI实现

配置示例:

# 使用UserDatabase的配置:
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                resourceName="UserDatabase" />
# 使用JDBC方式获取用户认证信息的配置:
        <Realm className="org.apache.catalina.realm.JDBCRealm" debug="99"
                driverName="org.gjt.mm.mysql.Driver"
                connectionURL="jdbc:mysql://localhost/authority"
                connectionName="test" connectionPassword="test"
                userTable="users" userNameCol="user_name"
                userCredCol="user_pass"
                userRoleTable="user_roles" roleNameCol="role_name" />

3.2.2 Valve组件

作用:

  • Valve类似于过滤器,它可以工作于Engine和Host/Context之间、Host和Context之间以及Context和Web应用程序的某资源之间
  • 一个容器内可以建立多个Valve,而且Valve定义的次序也决定了它们生效的次序

Tomcat6中实现了多种不同的Valve:

  • AccessLogValve:
    • 访问日志Valve
  • ExtendedAccessValve:
    • 扩展功能的访问日志Valve
  • JDBCAccessLogValve:
    • 通过JDBC将访问日志信息发送到数据库中
  • RequestDumperValve:
    • 请求转储Valve
  • RemoteAddrValve:
    • 基于远程地址的访问控制
  • RemoteHostValve:
    • 基于远程主机名称的访问控制
  • SemaphoreValve:
    • 用于控制Tomcat主机上任何容器上的并发访问数量
  • JvmRouteBinderValve:
    • 在配置多个Tomcat为以Apache通过mod_proxy或mod_jk作为前端的集群架构中,
      • 当期望停止某节点时,可以通过此Valve将用记请求定向至备用节点
    • 使用此Valve,必须使用JvmRouteSessionIDBinderListener
  • ReplicationValve:
    • 专用于Tomcat集群架构中,可以在某个请求的session信息发生更改时触发session数据在各节点间进行复制
  • SingleSignOn:
    • 将两个或多个需要对用户进行认证webapp在认证用户时连接在一起,即一次认证即可访问所有连接在一起的webapp
  • ClusterSingleSingOn:
    • 对SingleSignOn的扩展,专用于Tomcat集群当中,需要结合ClusterSingleSignOnListener进行工作

RemoteHostValve和RemoteAddrValve:

RemoteHostValve和RemoteAddrValve可以分别用来实现基于主机名称和基于IP地址的访问控制,控制本身可以通过allow或deny来进行定义,类似于Apache的访问控制功能。

  • 使用Valve实现仅允许本机访问/probe:
    • <Context path="/probe" docBase="probe">
              <Valve className="org.apache.catalina.valves.RemoteAddrValve"
              allow="127.0.0.1"/>
      </Context>
  • 相关属性定义:
    • className:
      • 相关的java实现的类名,相应于分别应该为org.apache.catalina.valves.RemoteHostValve或org.apache.catalina.valves.RemoteAddrValve
    • allow:
      • 以逗号分开的允许访问的IP地址列表,支持正则表达式,因此,点号“.”用于IP地址时需要转义
      • 仅定义allow项时,非明确allow的地址均被deny
    • deny:
      • 以逗号分开的禁止访问的IP地址列表,支持正则表达式,使用方式同allow

3.2.3 GlobalNamingResources

作用:

  • 应用于整个服务器的JNDI映射,此可以避免每个Web应用程序都需要在各自的web.xml创建,这在web应用程序以WAR的形式存在时尤为有用
  • 通常可以包含三个子元素:
    • Environment
    • Resource
    • ResourceEnvRef

3.2.4 WatchedResource

作用:

  • WatchedResource可以用于Context中监视指定的webapp程序文件的改变,
  • 并且能够在监视到文件内容发生改变时重新装载此文件

3.2.5 Listener

作用:

  • Listener用于创建和配置LifecycleListener对象,而LifecycleListener通常被开发人员用来创建和删除容器

3.2.6 Loader

作用:

  • Java的动态装载功能是其语言功能强大表现之一,Servlet容器使用此功能在运行时动态装载servlet和它们所依赖的类
  • Loader可以用于Context中控制java类的加载

3.3 tomcat主配置文件结构

  • /usr/share/tomcat/conf/server.xml
# 这里有showdown属性,因此可以通过telnet连接主机后输入SHUTDOWN指令来关闭服务,只能通过本机来连接
<Server port="8005" shutdown="SHUTDOWN">   
        # 侦听器,是用来侦听资源发生改变的
        <Listener className="org.apache.catalina.startup.VersionLoggerListener" />  
        ......
        # 全局名称资源,用来定义全局命名格式的
        <GlobalNamingResources>   
                <Resource name="UserDatabase" auth="Container"
                              type="org.apache.catalina.UserDatabase"
                              description="User database that can be updated and saved"
                         # 基于内存的用户数据库工厂
                              factory="org.apache.catalina.users.MemoryUserDatabaseFactory" 
                              pathname="conf/tomcat-users.xml" />
        </GlobalNamingResources>
                
        <Service name="Catalina">
                # 这是HTTP连接器
                <Connector port="8080" protocol="HTTP/1.1"  
                               connectionTimeout="20000"
                               redirectPort="8443" />
                # 表示通过tomcat内建的线程池来实现线程管理的
                <Connector executor="tomcatThreadPool"  
                                                     # 注意:java程序于所有的并发响应都是通过线程机制来实现的
                               port="8080" protocol="HTTP/1.1"
                               connectionTimeout="20000"
                               redirectPort="8443" />
                # SSL,也就是https连接器
                <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"  
                               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
                               clientAuth="false" sslProtocol="TLS" />
                # AJP:apache jserv protoco,二进制协议,但是浏览器不支持,通常只能用于特殊场景,只有httpd和tomcat支持
                <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> # AJP连接器
                
                # defaultHost表示默认的虚拟主机
                <Engine name="Catalina" defaultHost="localhost">   
                         # appBase表示主机的引用库,webapp表示应用程序的根路径
                         <Host name="localhost"  appBase="webapps"    
                                # unpackWARs表示是否支持自动解压缩WAR文档, autoDeploy表示是否支持自动部署 
                                unpackWARs="true" autoDeploy="true"> 
                                # 这里定义的是访问日志过滤器的,就是以什么格式记录日志的,directory是相对路径
                                <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"  
                                        # prefix表示每一个日志文件的前缀,siffix表示后缀,而前缀和后缀之间的中间部分是以日期进行滚动
                                        prefix="localhost_access_log." suffix=".txt"   
                                        pattern="%h %l %u %t &quot;%r&quot; %s %b" />  # 记录日志的格式
                                <context />
                                <context />
                        </Host>
                </Engine>
        </Service>
</Server>
  • shopxx开源网上商城

4. LNMP&LAMT&LNAMT架构

4.1 tomcat的配置

  • 自定义Host及Context示例
<Host name="web1.magedu.com" appBase="/data/webapps/" unpackWARs="true" autoDeploy="true">
        <Context path="" docBase="ROOT" reloadable="true">
                <Valve className="org.apache.catalina.valves.RemoteAddrValve"
                        deny="172.16.100.100"/>
        </Context>
        <Context path="/shop" docBase="shopxx" reloadable="true" />
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/data/logs"
                prefix="web1_access_log" suffix=".txt"
                pattern="%h %l %u %t &quot;%r&quot; %s %b" />         
</Host>

4.2 LNMT的配置

请求流程:

  • client -->http --> nginx --> reverse_proxy --> http --> tomcat (http connector)   

Nginx上的配置:

# 因为这里是默认的主页,访问时的URL并没有以.jsp结尾,所以不会被匹配到location上去
index  index.jsp  index.html;

# 定义一个location,所以以jsp和do结尾的url访问请求都被反代到指定的主机上去
location  ~*  .(jsp|do)$  {
        proxy_pass  http://web1.hgzero.com:8080;   # 若后端定义了虚拟主机,这里使用IP地址和和域名的区别是非常大的
}

4.3 LAMT的配置

请求流程:

  • client --> http --> httpd --> reverse_proxy --> { http | ajp } --> tomcat { http connector | ajp connector }

httpd上的反代模块:

  • 主模块:proxy_module
  • 子模块:proxy_module_http,proxy_module_ajp

在httpd上的配置:

  • 配置http的反代:  
    • proxy_module_http:
              <VirtualHost *:80>
                  ServerName web1.hgzerowzh.com
                  ProxyVia On        # 是否加一个首部说明这是通过当前主机反代的
                  ProxyRequests Off  # 关闭正向代理(必须的)
                 # 是否启用支持后端是基于主机名的虚拟主机(其实就是是否支持直接将整个主机名发到后端去)
                  ProxyPreserveHost On 
                  <Proxy *> 
                      Require all granted
                  </Proxy>
                  ProxyPass / http://172.16.100.67:8080/  # 将所有请求全部反代到后端(后面的斜线不能省略)
                  ProxyPassReverse / http://172.16.100.67:8080/
                  <Location />
                      Require all granted
                  </Location>
              </VirtualHost>
  •  配置ajp的反代:
    • proxy_module_ajp:
              <VirtualHost *:80>
                  ServerName web1.magedu.com
                  ProxyVia On
                  ProxyRequests Off
                  ProxyPreserveHost On
                  <Proxy *>
                      Require all granted
                  </Proxy>
                 # 这样加了一个叹号之后,用户对于这里指定的url请求就不会反代给后端主机了
                 # 要写在包含它的ProxyPass的前面
                  ProxyPass  /status !  
                  ProxyPass  /  ajp://172.16.100.67:8009/
                  ProxyPassReverse / ajp://172.16.100.67:8009/
                  <Location />
                      Require all granted
                  </Location>
              </VirtualHost> 

4.4 LNAMT的配置

说明:

  • 一般不会让tomcat直接处理用户请求

架构模型:

  • 在上游部署Nginx作为负载均衡器,然后将请求反代至httpd,然后httpd再将请求传递给Tomcat
  • 无论是否需要实现动静分离,httpd一般都将请求转发给Tomcat(Tomcat和httpd一般都部署在同一台主机上)
  • 一般在同一台主机上的httpd和Tomcat会配置成ajp1.3协议的连接器进行通信,以更高效,也避免了客户端可能的直接访问Tomcat

5. Tomcat负载均衡&会话绑定

5.1 tomcat集群会话绑定的方法

  • Session sticky 会话绑定,会话粘性
    • source ip:基于源ip进行绑定
    • cookie:基于cookie进行绑定
  • Session Cluster 使用Session信息在Tomcat集群中共享
  • Session Server 使用单独的Session服务器
    • kv:memcached,redis

5.2 nginx+tomcat负载均衡&会话绑定

1. 在nginx上配置:

upstream tcsrvs {
    ip_hash;  # 指明使用源地址哈希的调度方法
    server www.node5.com:8080;
    server www.node6.com:8080;
}

server {
        listen 80;
        root /data/nginx/html/www;
        index index.html;
        
        location / {
        }

        location ~* .(jsp|do)$ {   # 将所有以.jsp和.do结尾的url全部代理给指定的upstream
                proxy_pass http://tcsrvs;
        }
}

2. 在tomcat上配置:

  • 将Engine上的默认defaultHost修改成下面已经配置好的Host主机
<Host name="www.node5.com"  appBase="/data/webapp"    
    unpackWARs="true" autoDeploy="true"> 
    <Context path="" docBase="ROOT"/>
    # 这里定义的是访问日志过滤器的,就是以什么格式记录日志的,directory是日志保存的路径,可以定义相对路径
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/data/logs"  
            # prefix表示每一个日志文件的前缀,siffix表示后缀,而前缀和后缀之间的中间部分是以日期进行滚动
            prefix="web1_access_log." suffix=".txt"   
            pattern="%h %l %u %t &quot;%r&quot; %s %b" /> 
</Host>

5.3 httpd+tomcat负载均衡&会话绑定

5.3.1 httpd和tomcat的三种结合方式

1) 第一种:基于http协议

  • apache:
    • mod_proxy
    • mod_proxy_http
    • mod_proxy_balancer
  • tomcat:
    • http connector

2) 第二种:基于ajp协议

  • apache:
    • mod_proxy
    • mod_proxy_ajp
    • mod_proxy_balancer                
  • tomcat:
    • ajp connector

3) 第三种:基于jk模块

  • apache:
    • mod_jk
  • tomcat:
    • ajp connector

5.3.2 第一种:基于httpd配置对tomcat负载均衡

1) 仅实现负载均衡

<proxy balancer://lbcluster1>
        # 这里的route指定的是后端的tomcat的jvm的唯一标识
        # 可以在后端的tomcat的配置文件中的Engine标签中分别定义jvmRoute="TomcatA"和jvmRoute="TomcatB"
    BalancerMember http://172.16.100.68:8080 loadfactor=10 route=TomcatA 
    BalancerMember http://172.16.100.69:8080 loadfactor=10 route=TomcatB
</proxy>

<VirtualHost *:80>
    ServerName web1.hgzerowzh.com
    ProxyVia On          # 首部记录多级代理的via信息
    ProxyRequests Off    # 关闭正向代理
    ProxyPreserveHost On # 反向代理时启用虚拟主机
    <Proxy *>
        Require all granted
    </Proxy>
    ProxyPass / balancer://lbcluster1/
    ProxyPassReverse / balancer://lbcluster1/
    <Location />
        Require all granted
    </Location>
</VirtualHost>

2) 实现会话绑定

Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<proxy balancer://lbcluster1>
    BalancerMember http://172.16.100.68:8080 loadfactor=10 route=TomcatA
    BalancerMember http://172.16.100.69:8080 loadfactor=10 route=TomcatB
    ProxySet stickysession=ROUTEID
</proxy>

<VirtualHost *:80>
    ServerName web1.magedu.com
    ProxyVia On
    ProxyRequests Off
    ProxyPreserveHost On
    <Proxy *>
        Require all granted
    </Proxy>
    ProxyPass / balancer://lbcluster1/
    ProxyPassReverse / balancer://lbcluster1/
    <Location />
        Require all granted
    </Location>
</VirtualHost>   

5.3.3 第二种:基于ajp协议实现负载均衡+会话绑定

Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<proxy balancer://lbcluster1>
    BalancerMember ajp://172.16.100.68:8009 loadfactor=10 route=TomcatA
    BalancerMember ajp://172.16.100.69:8009 loadfactor=10 route=TomcatB
    ProxySet stickysession=ROUTEID
</proxy>

<VirtualHost *:80>
    ServerName web1.magedu.com
    ProxyVia On
    ProxyRequests Off
    ProxyPreserveHost On
    <Proxy *>
        Require all granted
    </Proxy>
    ProxyPass / balancer://lbcluster1/
    ProxyPassReverse / balancer://lbcluster1/
    <Location />
        Require all granted
    </Location>
</VirtualHost>            

5.3.4 第三种:基于mod_jk通过ajp实现反向代理+负载均衡

1) 编译安装tomcat-connectors

  • 下载好tomcat-connectors的源码包:
    • tar -zxvf tomcat-connectors-1.2.46-src.tar.gz
  • 查看apxs是否存在,若apxs不存在,则安装httpd-devel和开发工具包:
    • which apxs
    • yum install httpd-devel -y
    • yum groupinstall "Development Tools" -y
    • which apxs
  • 进入tomcat-connectors的native目录:
    • cd tomcat-connectors-1.2.46-src/native/
  • 编译安装:
    • ./configure --with-apxs=/usr/bin/apxs
    • make && make install

2) 配置反向代理

  • 创建模块配置文件:/etc/httpd/conf.d/mod_jk.conf
    • # 装载mod_jk模块
      LoadModule  jk_module  modules/mod_jk.so   
      # 指明workers文件的位置,每一个Tomcat实例叫一个worker
      JkWorkersFile  /etc/httpd/conf.d/workers.properties  
      # 日志文件的保存位置
      JkLogFile  logs/mod_jk.log   
      # 记录日志的级别
      JkLogLevel  debug            
      # 把根下的所有内容的url全部都代理至TomcatA,这里的TomcatA是一个worker的名字
      JkMount  /*  TomcatA   
      # 这里指明/status/没有发给TomcatA这个worker
      JkMount  /status/  stat1 
  • 创建workers配置文件:/etc/httpd/conf.d/workers.properties(注意这个文件不能以conf结尾)
    • worker.list=TomcatA,stat1      # 指明可用的worker列表
      worker.TomcatA.port=8009       # 指明TomcatA这个worker的端口
      worker.TomcatA.host=10.0.0.205 # 指明IP地址
      worker.TomcatA.type=ajp13      # 指明所使用的协议
              # type一共有三种:
                      # ajp13:定义各Tomcat节点类型的
                      # status:定义内置的status页面的
                      # lb:做负载均衡器的
      worker.TomcatA.lbfactor=1 # 指明权重
      worker.stat1.type=status  
      # 注意:stat1的访问要做访问控制

3) 配置负载均衡

  • 模块配置文件:/etc/httpd/conf.d/mod_jk.conf
    • LoadModule jk_module modules/mod_jk.so
      JkWorkersFile /etc/httpd/conf.d/workers.properties
      JkLogFile logs/mod_jk.log
      JkLogLevel debug
      JkMount /* lbcluster1
      JkMount /status/  stat1
  • workers配置文件:/etc/httpd/conf.d/workers.properties
    • worker.list=lbcluster1,stat1
      worker.TomcatA.port=8009
      worker.TomcatA.host=10.0.0.205
      worker.TomcatA.type=ajp13
      worker.TomcatA.lbfactor=1
      
      worker.TomcatB.port=8009
      worker.TomcatB.host=10.0.0.206
      worker.TomcatB.type=ajp13
      worker.TomcatB.lbfactor=2
      
      worker.lbcluster1.type = lb
      worker.lbcluster1.sticky_session=0 # 不使用session绑定,如果要用,改成1即可
      worker.lbcluster1.balance_workers=TomcatA,TomcatB
      
      worker.stat1.type=status

6. Tomcat集群

tomcat会话管理器

  • 定义在Host或者context中

tomcat会话管理器的种类(session manager):

  • StandardManager:标准会话管理器
  • PersistentManager:持久会话管理器
    • FileStore:文件存储
    • JDBC:数据库存储
  • DeltaManager
  • BackupManager(很少用)

6.1.1 标准会话管理器

  • 作用:
    • 标准会话管理器(StandardManager)
    • 默认保存于$CATALINA_HOME/work/Catalina/<hostname>/<webapp-name>/下的SESSIONS.ser文件中
    • 会周期性的保存到此文件中,有一定的持久功能,但并不能持久保存所有会话,因为它的周期性的保存
  • 示例:
    • <Manager className="org.apache.catalina.session.StandardManager"
               maxInactiveInterval="7200"/> # 会话的最大非活动连接时长,超过此时长将会被移除
  • 参数说明:
    • maxActiveSessions:最多允许的活动会话数量,默认为-1,表示不限制
    • maxInactiveInterval:非活动的会话超时时长,默认为60s
    • pathname:会话文件的保存目录

6.1.2 持久会话管理器

  • 作用:
    • 将会话数据保存至持久存储中,并且能在服务器意外中止后重新启动时重新加载这些会话信息
    • 持久会话管理器支持将会话保存至文件存储(FileStore)或JDBC存储(JDBCStore)中
  • 使用示例:
    • 保存至文件中的示例:
      • <Manager className="org.apache.catalina.session.PersistentManager"
          saveOnRestart="true">
          <Store className="org.apache.catalina.session.FileStore"
            directory="/data/tomcat-sessions"/>
        </Manager>
        # 每个用户的会话会被保存至directory指定的目录中的文件中,文件名为<session id>.session,
        # 并通过后台线程每隔一段时间(checkInterval参数定义,默认为60秒)检查一次超时会话
    • 保存至JDBCStore中的示例:
      • <Manager className="org.apache.catalina.session.PersistentManager"
          saveOnRestart="true">
          <Store className="org.apache.catalina.session.JDBCStore"
            # 这是连接mysql的JDBC的驱动
            driverName="com.mysql.jdbc.Driver"  
            # 保存在mydb数据库上,以用户名为jb,密码为pw
            connectionURL="jdbc:mysql://localhost:3306/mydb?user=jb;password=pw"/>  
        </Manager>

tomcat配置Session Cluster

  • DeltaManager的使用

1. 各节点配置使用deltamaanager:(以下为在Tomcat8上的配置)

  • 以下配置可以在Tomcat官网的文档的Cluster中找到
  • 多播session replication
<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.1.7"   # 定义多播地址
                port="45564"          # 定义多播地址监听的端口
                frequency="500"       # 每多长时间发一次心跳,单位是毫秒,这里表示每0.5秒发一次心跳
                dropTime="3000"/>     # 如果在多长时间内不再收到其他节点的心跳,就将其从集群中剔除
    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" # 自己如何接收别人传过来的session
              address="auto"          # 监听在哪个地址上,auto表示自动找一个地址来监听,这里要给定一个地址
              port="4000"             # 监听的端口
              autoBind="100"          # 是否自动绑定,绑定多长时间
              selectorTimeout="5000"  # 挑选器的超时时间为5000毫秒
              maxThreads="6"/>        # 最多可以启动6个线程

    # 定义如何把自己的session传递给别人
    <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.ClusterSessionListener"/>
</Cluster>

2. 为需要使用session cluster的webapps开启session distribution的功能:(关键步骤)

  • 将conf/web.xml复制到指定的app的WEB-INF中
  • 在WEB-INF/web.xml的<web-app>标签中添加:(可以放在web-app标签内的结尾,但一定要放在web-app标签内)
    • <distributable/>

3. 查看日志信息,看节点信息是否同步:

  • tail -n 30 /var/log/tomcat/catalina.2019-12-10.log
  • 出现以下信息说明节点信息同步完成:
INFO: Replication member 
added:org.apache.catalina.tribes.membership.MemberImpl
[tcp://{10, 0, 0, 205}:4000,{10, 0, 0, 205},4000, alive=1073, securePort=-1,
 UDP Port=-1, id={22 67 69 -101 -41 -97 75 -53 -82 93 -30 76 -34 -56 67 -2 }, 
payload={}, command={}, domain={}, ]

4. 测试:

  • 在每个Tomcat上部署这个测试页面:(内容视节点不同而需做一些改动)
<%@ page language="java" %>
<html>
        <head><title>TomcatA</title></head>
        <body>
                <h1><font color="red">TomcatA</font></h1>
                <table align="centre" border="1">
                        <tr>
                                <td>Session ID</td>
                <% session.setAttribute("www.node.com","www.node.com"); %>
                        <td><%= session.getId() %></td>
                        </tr>
                        <tr>
                                <td>Created on</td>
                                <td><%= session.getCreationTime() %></td>
                        </tr>
                </table>
                <h1>10.0.0.206</h1>
        </body>
</html>

5. 查看:

  • 通过负载均衡器访问后端tomcat主机
  • 如果显示的结果中,虽然响应的主机不同,但是session信息显示相同,说明,Tomcat的Session Cluster配置成功

tomcat之Session Server

原文地址:https://www.cnblogs.com/hgzero/p/13149665.html