- 基础概念
- tomcat
- 安装
- 结构
- 管理
- 部署模式
- 配置文件
- 组件
- 普通类-服务器server
- 普通类-服务service
- 连接类-连接器connetors
- 容器类-引擎engine
- 容器类-主机host
- 容器类-上下文context
- 被嵌套类-阀门value
- 被嵌套类-日志记录类logger
- 被嵌套类-领域realm
- 问题
- 问题1
- nginx+tomcat动表分离
- apache为tomcat护航
=======================================================================
张贺,多年互联网行业工作经验,担任过网络工程师、系统集成工程师、LINUX系统运维工程师
笔者微信:zhanghe15069028807,现居济南历下区
=======================================================================
基础概念
tomcat
Apache 为HTML页面服务,而Tomcat 实际上运行JSP 页面和Servlet。另外,Tomcat和IIS等Web服务器一样,具有处理HTML页面的功能,另外它还是一个Servlet和JSP容器,独立的Servlet容器是Tomcat的默认模式。不过,Tomcat处理静态HTML的能力不如Apache服务器。目前Tomcat最新版本为9.0.31。
- comcat就是在jdk(se)的基础上再提供一个jsp和servlet。
- comcat自己也是用java写的,所以想要像tomcat运行起来,要先安装java se,jdk包提供java se,所以就要安装先安装jdk,才能安装tomcat;
- 当tomat运行起来之后是看不到tomcat进程,只能看到一个java进程,因为tomcat运行在了这个java进程的jvm之上了。
安装
tomcat也是java语言写的,想要tomcat运行起来,就要安装tomcat的运行环境,也就是java se环境,jdk提供这个环境,所以先要安装jdk,然后再安装tomcat。
[root@kk tmp]# ll
drwxr-xr-x 9 root root 220 Mar 7 11:42 apache-tomcat-8.5.51
-rw-r--r-- 1 root root 126857158 Mar 7 11:48 jdk-7u67-linux-x64.rpm
[root@kk tmp]# rpm -ivh jdk-7u67-linux-x64.rpm
//配置jdk的环境变量
[root@kk ~]# vim /etc/profile.d/java.sh
export JAVA_HOME=/usr/java/latest
export PATH=$JAVA_HOME/bin/:$PATH
[root@kk ~]# . /etc/profile.d/java.sh
[root@kk ~]# java -version
java version "1.7.0_67"
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
[root@kk local]# ln -sv apache-tomcat-8.5.51/ tomcat
‘tomcat’ -> ‘apache-tomcat-8.5.51/’
[root@kk ~]# vim /etc/profile.d/tomcat.sh
export CATLINA_HOME=/usr/local/tomcat
export PATH=$CATLINA_HOME/bin/:$PATH
[root@kk ~]# . /etc/profile.d/tomcat.sh
//验证
[root@kk ~]# version.sh
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr/java/latest
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Server version: Apache Tomcat/8.5.51
Server built: Feb 5 2020 22:26:25 UTC
Server number: 8.5.51.0
OS Name: Linux
OS Version: 3.10.0-957.el7.x86_64
Architecture: amd64
JVM Version: 1.7.0_67-b01
JVM Vendor: Oracle Corporation
//启动
[root@kk ~]# catalina.sh start|stop
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr/java/latest
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Tomcat started.
[root@kk ~]# ss -tnlp | grep 8080
LISTEN 0 100 :::8080 :::* users:(("java",pid=8653,fd=49))
//尝试自己写一个java测试页
[root@kk webapps]# mkdir mysapp/{lib,classes,WEB-INF,META-INF} -pv
[root@kk webapps]# vim mysapp/index.jsp
<%@ page language="java" %>
<%@ page import="java.util.*" %>
JSP TEST PAGE
<% out.println("hello,world"); %>
http://192.168.80.100:8080/myapp/
[root@kk tomcat]# tree work/
├── myapp
│ └── org
│ └── apache
│ └── jsp
│ ├── index_jsp.class #生成了这两个文件
│ └── index_jsp.java #jasper转换的
结构
- server是一个tomcat实例,代表comcat本身。
- service表示服务,可以侦听多个套接字,一个server可以有多个service,但我们通常只让其运行一个service。
- 一个service只能有一个servlet引擎(engine),引擎是最核心的组件,引擎只能运行代码,不能自动装载代码,connector负载接受用户请求并装载代码至引擎当中,将运行后的结果构建响应报文,支持http、cg,接收器和引擎之间需要service相关联,映射,转换,不能直接连接。
- 一个引擎可以有多个连接器,一个连接器只能服务于一个引擎。
- 一个service里面只可以有一个引擎,却可以有多个连接器。一个server就相当于一个web server。一个引擎又可以提供多个虚拟主机(host)。
- context就是上下文,就相当于nginx的alias里面,映射路径用的。
管理
这两个工具就是默认界面上的,很少使用:
-
manger app:webapp的管理工具
-
host manger:virtual hosts管理工具
启动、关闭
catalina.sh stop
catalina.sh start
部署模式
- 单机tomcat:tomcat虽然也能处理http请求,但毕竟不是专门干这个的,所以这种情况并不常见。
- nginx+tomcat:将nginx和tomcat放置到一台服务器上,让nginx处理高并发,tomcat专门处理动态请求,nginx和tomcat因为在同一台主机上,使用进行间通信的方式通信。
- haproxy,nginx+tomcat:实际的生产环境当中是这么干的,haproxy做负载并基于url、cookie做会话保持,nginx和tomcat处于web集群当中。
- nginx和tomcat配合,nginx反代,connector转换,内部是是短连接,让反代去维持长连接。
配置文件
tomcat组件:
catalina:用来实现servlet container
jsp:作用是将jsp代码转换成servlet格式的代码
coyote web,用来接收http协议
jasper:引擎的名称
tomcat的目录结构:
- bin:脚本及启动时用到的类
- lib:类库
- conf:配置文件
- logs:日志文件
- webapps:应用程序默认部署目录
- work:工作目录
- temp:临时文件目录
java webapp组织结构:
有特定的组织形式,层次型的目录结构,主要包含了servlet代码文件,jsp页面文件,类文件,部署描述文件等。
/usr/local/tomcat/webapps/ROOT
- ROOT:webapp的根目录,tomcat的默认都会在此目录下找index.jsp,我们在部署的时候可以直接将软链接命名为ROOT
- WEB-INF:当前webapp的私有资源目录,通常存放当前webapp自用的web.xml
- MATA-INF:当前webapp的私有资源目录,通常存放当前webapp自用的context.xml
- classes: 此webapp的私有类
- lib:此webapp的私有类,被打包为jar格式类
- index.jsp: webapp的主页
webapp归档格式:
- .war:webapp
- .jar:EJB的类
- .rar:资源适配器
- .ear:企业级应用程序;
配置文件:
tomcat的默认文件存放在$CATALINA_HOME/conf目录中,主要有以下几个:
- server.xml:主配置文件,包含service、connector、engine、reaml、valve、hosts主组件的相关配置信息,位置在
/usr/local/tomcat/conf/server.xml
- web.xml:遵循servlet规则标准的配置文件,用于配置servlet,此文件用于为所有的webapp提供默认部署的相关配置,位置在
/usr/local/tomcat/conf/web.xml
- context.xml:所有host的默认配置信息,每个webapp都可以有专用的配置文件,这些配置文件通常位于webapp应用程序目录下的WEB-INF目录中,用于定义会话管理器,JDBC等;conf/context.xml是为各webapp提供的默认配置,位置也在
/usr/local/tomcat/conf/context.xml
- tomcat-user.xml:用户认证的账号和密码配置文件,tomcat自带的manger默认情况下会用到此文件,有tomcat中添加/删除用户,为用户指定角色通过编辑此文件实现。
- catalina.policy:java相关的安全策略,当使用security选项启动tomcat实例时会读取此配置文件来实现其安全运行策略,在系统资源级别上提供访问控制的能力。
- catalina.properties:tomcat内部package的定义及访问相关的控制,也包括能通过类装载器装载的内容的控制,以及一些JVM性能相关的调优参数。tomcat在启动时会读取此文件相关配置,
- logging.properties:日志相关的配置信息。
//主配置文件组织格式
…
…
…
注意:每个组件都是由“类”来实现,有些组件的实现不止一种。
- 顶级类组件:server
- 服务类组件:service
- 容器类组件:即可以部署webapp的组件,engine,host,context
- 连接器组件:connector
- 被嵌套类组件:value,logger,realm
部署(deployment)webapp相关的操作:
所谓tomcat的部署是将webapp的源文件放置于目录目录,配置tomcat服务能够基于context.xml文件中定义的路径来访问此webapp,将共特有类通过class loader 装载至tomcat。
有两种部署方式:
-
冷部署:把webapp复制到指定位置,而后才启动tomcat;
-
热部署:不停止tomcat的前提下进行部署
部署工具:manger、ant脚本、tcd(tomcat client deployer)等;
- undeploy:反部署,停止webapp,并从tomat实例拆除其部分文件和部署名;
- stop:停止,不再向用户提供服务;
- start:启动处于“停止”状态的webapp;
- redeploy:重新部署
tomcat以面对对象的方式运行,它可以在运行时动态加载加载配置文件中定义的对象结构,这有点类似于apache的httpd模块的调用方式。server.xml中定义的每个主元素都会被创建为对象,并以某特定的层次结构将这些对象组织在一起,下面是个样例的配置:
//server.xml配置文件
#通过telnet登录8005端口可以关闭tomcat
[root@kk ~]# telnet 127.0.0.1 8005 #比较危险
SHUTDOWN
#全局命名格式
#用户相关
//service,将引处的8080即侦听的端口,就在此处更改侦听的端口
//ssl连接器
//AJP(apache jserv protocol )端口8009,AJP的连接器,二进制协议,效率比较高,只有apche能对接上。
//默认虚拟主机
//这里面用来定义主机的,
#war文档自动打开
//在主机里面,定义访问日志
#格式
组件
普通类-服务器server
tomcat的一个实例,通常一个JVM只能包含一个tomcat实例,因此,一台物理服务器可以在启动多个JVM的情况下在每一个JVM中启动一个tomcat实例,每个实例分属一个独立的管理端口,这是一个顶级组件。
普通类-服务service
一个服务组件通常包含一个引擎和此引擎相关联的一个或多个连接器。给服务命名可以方便管理员在日志文件中识别不同服务产生的日志,一个server可以包含多个service组件,但通常情况下只为一个service指派一个server
连接类-连接器connetors
如果基于apache做为tomcat前端的架构来讲,apache通过mod_jk,mod_jk2或mod_proxy模块与后端的tomcat进程数据交换,而对tomcat来说,每个web容器实例都且人上java语言开发的连接器模块组件,在tomcat6当中,这个连接器是org.apache.catalina。
进入tomcat的请求可以根据tomcat的工作模式分为两类:
-
tomcat做为应用程序服务器,请求来自前端的web服务器,这可能是apahce,iis、nginx等,最是常见。
-
tomcat作为独立服务器,请求来自于用户浏览器。
tomcat应该考虑工作情况并为相应情形下的请求分别定义好需要的连接器才能正确接收来自客户端的请求,一个引擎可以有一个或多个连接器,以适应多种请求方式。
定义连接器可以使用多种属性,有些属性也只适用于某特定的连接器类型,一般来说,常见于server.xml中的连接器类型通常有4种:
-
HTTP连接器
-
SSL连接器
-
AJP 1.3连接器,专为apache准备。
//server.xml默认的连接器,用的是http协议
定义连接器时可用配置属性非常多,但通常寄定义http连接器时必须定义的属性只有port,定义AJP连接器时必须定义定义的属性只有protocol,默认连接器的类型为http,以下为常用属性的说明。
- address:指定连接器监听的地址,默认为所有地址0.0.0.0,这也不用有地址。
- maxthreads:支持的最大并发连接数,最大为200
- port,监听端口,默认为0
- protocol:连接器使用的协议,默认为http1.1,定义ajp协议的通常为ajp/1.3
- redirectport:如果某连接器支持的协议是http,当接收客户端发来的https请求时,则转发至此属性定义的端口。
- connectionTimeout:等待客户端发送请求的超时时间,单位为毫秒,默认为60000,即1分钟。
- enablelookups:是否通过request.getRemoteHost()进行DNS查询以获取客户端的主机名,置信为true,最好定义为关闭,加快效率。
- acceptCount: 设置等待队列的最大长度,通常在tomcat所有处理线程均处于繁忙的状态时,新发来的请求将被放置于等待队列中上。
容器类-引擎engine
引擎通常是指处理请求的servlet引擎组件,即catalina servlet引擎,它检查每一个请求的HTTP首部信息以判别此请求应该发往哪个host或context,并将请求处理后的结果返回相应的客户端。严格意义上来说,容器不必非得通过引擎来实现,它也可以只是一个容器,如果tomcat被配置成为独立服务器,默认引擎就是已经定义好引擎。而如果tomcat被配置为apache web服务器提供servlet功能的后端,默认引擎会被忽略,因为web服务器自身就能确定将用户请求发向何处,一个引擎可以包含多个host组件。
常用属性定义:
defaultHost: tomcat支持基于FQDN的虚拟主机,这些虚拟主机可以通过在engine容器中定义多个不同的host组件来实现,但如果此引擎的连接器收到一个发往非明确定义的虚拟主机的请求时则需要将此请求发往一个默认的虚拟主机进行处理,因此,在engine中定义的多个虚拟主机的主机名称至少要有一个跟defaultHost定义的主机名称相同。
name:engine组件的名称,用于日志和错误信息记录时区别不同的引擎
jvmrouter:在后面会讲,集群当中会用到
engine容器中可以包含realm、host,listener和valve子容器
容器类-主机host
位于engine容器中用于接收请求并进行相应处理的主机或虚拟主机。
主机级类型相当于apache当中的虚拟主机,但在tomcat中只支持基于FQDNr 虚拟主机,一个引擎至少 要包含一个主机组件。
常用属性:
appbase:此host的webapps目录,即存放非归档的web应用程序的目录或归档后的war文件的目录路径,可以使用基于$CATALINA_HOME的相对路径。
autodeploy:在tomcat处于运行状态时放置于appbase目录中的应用程序文件是否进行deploy,默认为ture。
unpackWars: 在启用此webapps时是否对var格式的归档文件先进行展开,默认为true。
主机别名定义:
如果一个主机有两个或两个以上的主机名,额外的名称均可以以别名的形式进行定义,如下:
magedu.com #发往这个别名的请求会被定义到www.magedu.com中来
容器类-上下文context
context组件是最内层次的组件,它表示web应用程序本身。配置一个context最重要的是指定web应用程序的根目录,以便servlet容器能够将用户请求发往正确的位置。context组件也可以包含自定义的错误页,以实现在用户访问发生错误时提供友好的提示信息。
Context在某些意义上类似于apache中的路径别名,一个Context定义用于标识tomcat实例中的一个Web应用程序;如下面的定义:
#PATH是指URL,docbase文件放置的位置
#这里面的docbase是相对路径,相对于当前虚拟主机的appbase
在Tomcat6中,每一个context定义也可以使用一个单独的XML文件进行,其文件的目录为$CATALINA_HOME/conf/ / 。可以用于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文件中,此属性不需要定义;
- reloadable:是否允许重新加载此context相关的Web应用程序的类;默认为false;
示例1:
path什么也不写,表示根路径相当于web1.zhanghe.com,docbase就是文档放在哪里面了,这里面的位置代表/data/webapps/index.jsp
[root@kk conf]# mkdir -pv /data/{webapps,logs}
mkdir: created directory ‘/data’
mkdir: created directory ‘/data/webapps’
mkdir: created directory ‘/data/logs’
[root@kk ROOT]# pwd
/data/webapps/ROOT
[root@kk ROOT]# ls
index.jsp
//访问
http://web1.zhanghe.com:8080/,如果通过IP地址访问,会再次跑到图形界面,因为它是default
示例2:
被嵌套类-阀门value
这类组件通常包含于容器类组件中以提供具有管理功能的服务,它们不能包含其它组件,但有些却可以由不同层次的容器各自配置。
用来拦截请求并在将其转至目标之前进行某种处理操作,类似于servlet规范中定义的过滤器.value可以定义在任何容器类的组件中。value常被用来记录客户端请求,客户端IP地址和服务器等信息,这各处理技术通常被称作请求转储。请求转储value记录请求客户端请求数据包的http首部信息和cookie信息至文件中,响应转储value则记录响应数据包首部信息和cookie信息至文件中。
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可以分别用来实现基于主机名称和基于IP地址的访问控制,控制本身可以通过allow或deny来进行定义,这有点类似于Apache的访问控制功能;如下面的Valve则实现了仅允许本机访问/probe:
其中相关属性定义有:
- className:相关的java实现的类名,相应于分别应该为org.apache.catalina.valves.RemoteHostValve或org.apache.catalina.valves.RemoteAddrValve;
- allow:以逗号分开的允许访问的IP地址列表,支持正则表达式,因此,点号“.”用于IP地址时需要转义;仅定义allow项时,非明确allow的地址均被deny;
- deny: 以逗号分开的禁止访问的IP地址列表,支持正则表达式;使用方式同allow;
被嵌套类-日志记录类logger
用于记录组件内部的状态信息,可被用于除context之外的任何容器中。日志记录的功能可被继承,因此,一个引擎级别的logger将会记录引擎内部所有组件相关的信息,除非某内部组件定义了自己的logger。
被嵌套类-领域realm
用于用户的认证和授权,在配置一个应用程序时,管理可以为每个资源或资源组定义角色及权限,而这些访问控制功能生效需要通过reaml来实现。realm的认证可以基于文本文件,数据库表、LDAP服务等来实现。realm的效用会遍及整个引擎或顶级容器,因此,一个容器内的所有应用程序将共享用户资源,同时,realm可以被其所在组件的子组件继承,也可以被子组件定义的realm所覆盖。
问题
问题1
假如我们通过nginx向后端toucat进行反代,nginx的反代配置如下:
location / {
proxy_pass http://192.168.80.131:8080;
}
如上,在nginx上通过IP地址进行反代,这样做其实是有问题的。假如后端tomcat有多个虚拟主机,多个虚拟主机用的IP都是一样的,那么nginx的请求到底应该给哪一个虚拟主机呢?很有可能给的主机并不是我们想要的那一台,这种情况怎么办呢?很简单,我们只要告诉nginx不要通过IP地址代理,而是通过FQDN做代理,proxy_pass
是可以基于域名做代理的呀!注意本地要要加上域名解析,这样就行了,如下所示:
location / {
proxy_pass www.zhanghehe.com.cn:8080;
}
nginx通过域名访问后端tomcat时,在请求报文当中会加上www.zhanghehe.com.cn
,这样后端的tomcat就很容易知道nginx到底要访问哪台虚拟主机。
nginx+tomcat动表分离
我们在nginx上做动静分离其实很简单,如下所示:
location / {
root /data/nginx/html;
index index.html index.htm;
}
location ~* .(jsp|do)$ {
proxy_pass http://192.168.80.131:8080;
}
所有的动态请求都转发到后端的tomcat,非动态nginx就自己响应了。
注意java的动态请求当中,不仅仅有jsp,还有do结尾的,所以要把do结尾的也要加上。
apache为tomcat护航
前端负载均衡可以用nginx,但后端的tomcat我们不可以让它单独工作,通常我们要给它加上apache,目的是为了tomcat,那为什么不用nginx,而用apache,因为tomcat和apche的亲和度非常好,tomcat就是apache项目里面的呀! nginx和tomcat交互只能通过http协议,而apche和tomcat可以通过ajp协议,ajp协议是tomcat专门用来对接apache而研制的二进制通信协议,速度非常快,所以架构就变成这个样子了:
哪怕apche不处理任何动静请求,仅仅是保护tomcat,也是有存在的必要的。我们可以让tomcat和apache在同一台主机当中,apache什么也不处理,仅仅做为一个代理。
httpd -M
less /etc/httpd/conf.modules.d/00-proxy.conf
//定义一个虚拟主机,用http
ServerName web1.zhanghe.com
ProxyVia On #打开代理功能
ProxyRequests Off #关闭正向代理,因为我们要用的反向代理功能
ProxyPreserveHost On #携带主机名,专门有这个选项
Require all granted #代理所有的url
proxypass /status ! #不要把状态也代理后而了,排除
ProxyPass / http://192.168.80.131:8080/ #给谁
proxyPassReverse / http://192.168.80.131:8080/
Require all granted #给所有人代理
//定义一个虚拟主机,用ajp协议
ServerName web1.zhanghe.com
ProxyVia On #打开代理功能
ProxyRequests Off #关闭正向代理,因为我们要用的反向代理功能
ProxyPreserveHost On #携带主机名,专门有这个选项
Require all granted #代理所有的url
proxypass /status ! #不要把状态也代理后而了,排除
ProxyPass / ajp://192.168.80.131:8080/ #给谁
proxyPassReverse / ajp://192.168.80.131:8080/
Require all granted #给所有人代理