简单了解Tomcat之后, 我们来看看他的实际配置和相关的应用吧,
我们之前已经说过了, 对于Tomcat运行的核心配置文件就是server.xml
. 各个层级组件的顺序是这样子的:
server -> service -> connector -> engine -> host -> context
我们还说过在部署的时候, tomcat启动一个jvm, 产生运行时区域, 主要就是堆和栈两个部分, 其中对象都保存在堆中.
在CentOS7上部署使用Tomcat
之前我们已经将相关的环境变量配置好了, 也成功的部署了Tomcat的默认页面, 现在我们来先看一下默认的页面的组织结构:
1 | [root@VM-node0 host-manager]# tree . |
这个是默认tomcat页面的host-manager
的目录结构 从中我们可以得到一个Java Web工程的目录组织结构.
一个JavaWeb程序, 都有特定的组织形式, 层次性的目录结构, 从上面也可以看到了. 主要包含了servlet代码文件, jsp页面文件, 类文件, 部署描述符文件等等.
例如我们在上面看到的, WEB-INF
, META-INF
这两个目录是完全大写的, 但是却并不一定是必须要有的, 不过基本上每一个JavaWeb程序都有这个. 其中, 前者是webapp的私有资源目录, 通常存放的是webapp自用的web.xml.而后者基本相似, 存放的是webapp的context.xml
显然的, 我们这两个目录是不能被访问到的. 另外, 还有classes
, 用来存放webapp的私有类的, 以及lib
, 这也是webapp的私有类, 但是是被打包成jar格式的类. 当然了, 还有一个webapp的主页, 也就是index.jsp了.
说完了webapp的组织结构, 接下来我们再来说一下webapp的几种归档格式.
- war webapp
- jar EJB的类
- rar 资源适配器
- ear 企业级应用程序
以上就是关于Java的Webapp组织结构的相关了, 接下来我们就来手动添加一个测试应用程序, 步骤如下:
- 创建一个webapp的特有的目录结构
- 提供webapp的各个文件
1 | [root@VM-node0 webapps]# mkdir myapp |
接下来就是创建一些必要的文件.
来随便写一个:
1 | <%@ page language="java" %> |
ok, 接下来我们就来直接访问一下. 默认配置的是自动部署的 我们先不要关心这方面的.
直接访问的结果:
结果和我们设想的结果是一致的.
我们之前说过, webapp中的源文件会被翻译成Java代码和Class文件在work
目录下, 我们去看一下吧.
1 | [root@VM-node0 myapp]# pwd |
已经生成了对应的Java代码和类文件.
我们部署webapp应用, 其实就是将源文件放置于目标目录, 接着配置tomcat服务器是的能够给予context.xml文件中定义的路径来访问此webapp. 接着通过特有的类来将class loader装载到tomcat.
主要有两种方式, 也即是自动部署和手动部署, 其实手动部署还有两种, 一个是冷部署, 也就是把webapp复制倒指定位置, 接着再启动tomcat. 另外一个就是热部署, 通过一些manager的部署工具, ant, tcd等.
除了部署操作, 还有反部署, 重新部署, 停止, 启动等等几种和部署有关的操作. 其中稍微有点疑问的应该就是反部署操作了吧. 这个其实是说停止webapp, 并且从tomcat实例中拆除其部分文件和部署名.
Tomcat的组件配置
我们再来看一遍tomcat主配置文件的目录结构.
1 | <server> |
除了上面的这些, 还有一些被嵌套的组件, 例如Valve(阀门)
. 这个东西可以存在在任何容器类的组件中, 这个东西可以理解成是一种拦截器, 可以用来进行日志收集或者试请求拦截. 另外, 还有日志记录器Logger
, 可以存放在Context之外的任何容器中, 用来定义日志是如何存储, 如何记录的. 除此之外, 还有一个用来进行用户身份认证的组件叫做Realm
这个在之前也提到过.
Server.xml
我们现在就打开server.xml
来看一下, 首先还是先复制一份吧 然后更方便进行实验.
首先我们可以看到默认定义的Server实例, 后面跟上了一个端口和一个shutdown属性. 这是干嘛的? 相信聪明的你一定看出来了, 对. 这是销毁虚拟机的一个接口, 我们可以使用telnet连接过去之后, 输入shutdown
属性所定义的语句, tomcat就会销毁虚拟机了, 来试试吧.
1 | [root@VM-node0 tomcat]# ss -tnl |
这个是启动之后的情况, 我们可以看到监听本机的8005端口, 接下来使用telnet去连接并且发送对应的字符串:
1 | [root@VM-node0 tomcat]# telnet 127.0.0.1 8005 |
看 这样监听的套接字都被释放了, 事实上, 虚拟机都销毁了, 整个tomcat进程其实都没了.
再往下面看, 我们可以看到tomcat对用户认证相关的属性:
1 | <GlobalNamingResources> |
这里定义了一个全局的命名资源, 创建了一个基于内存的用户数据库, 所对应的配置文件就是conf/tomcat-user.xml
.
再往下走, 我们看到了第一个对于HTTP/1.1协议版本的连接器:
1 | <Connector port="8080" protocol="HTTP/1.1" |
在下面的注释中, 还有一个使用https也就是SSL/TLS的连接器:
1 | <!-- |
在上面做销毁实例的实验中 我们注意到除了这个服务实例监听的8005端口和HTTP连接器监听的8080端口之外, 还有一个8009
端口, 这个端口是干什么的呢? 向下看就可以看到了:
1 | <!-- Define an AJP 1.3 Connector on port 8009 --> |
就像上面注释写的, 这是一个AJP协议的连接器.
AJP的全称是
Apache Jserv Protocol
. 这是一个定向包协议, 使用二进制格式来传输可读文本.
接着向下就可以看到我们的Catalina的Engine
了, 使用的默认host就是下面定义的localhost
:
1 | <Engine name="Catalina" defaultHost="localhost"> |
这里省略了注释的部分, 这里的host定义了使用的webapp的目录名称和一些部署选项,例如: 是否自动部署, 解包WAR等. host内部就是我们说的valve
, 定义了一个日志的存储格式和存储类型.txt
Connector
在绝大多数的应用场景中, 我们都不会把Tomcat直接面向客户提供服务, 虽然官方说Tomcat是使用event模型开发的. 那么搭配反向代理使用的时候就需要配置一下连接器组件了.
假设我们的前端服务器是Apache的话, 我们可以使用AJP协议进行通信, 这样更高效. 甚至, 在这种情况下, 我们可以直接关闭HTTP的连接器防止用户直接越过前端服务器访问到. 基本上我们的连接器类型分以下的3种:
- HTTP连接器
- SSL连接器
- AJP连接器
如果是HTTP类型的连接器, 必须要配置的属性是port
, 协议默认就是http, 这就意味着 我们在定义AJP连接器的时候需要定义protocol
. 除这两之外, 常用的属性还有:
- address: 连接器定义的监听地址, 默认是0.0.0.0
- maxThreads: 最大并发连接, 默认是200
- redirectPort: HTTP和HTTPS的转发端口, 如果连接器支持的协议是HTTP但是收到了HTTPS请求的话, 就会转发到这个接口.
- connectionTimeout: 顾名思义了, 等到客户端发送请求的超时时长
- enableLookups: 是否进行DNS解析, 默认是true. 这十分耗时间, 一般都会设置成为false
- acceptCount: 设置等待队列的最大长度. 在tomcat所有的处理线程都处于繁忙状态的时候, 新发来的请求就会放置于这个队列中.
一个SSL连接器的示例:
1 | <Connector port="8443" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" |
提个醒, 这里我们还没有配置使用的证书和密钥, tomcat仅仅支持JKS
, PKCS11
or PKCS12
这三种格式的密钥存储格式, 也就是说我们需要先把之前的证书和密钥重新导入进去, 使用形如这样的cmd:
1 | openssl pkcs12 -export -in mycert.crt -inkey mykey.key |
过了很久之后的补充, 通过这种方式我没有正确的配置好SSL访问, 但是连接器是启动了, 8443端口是在监听状态了. 通过查阅了官方文档, 使用另外一种
Apr
的方式配置好了, 当然前提是下载了一个so组件,tomcat-native
. 在下面贴上一个能够使用的:
1 | <Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol" |
这里的前提是本机需要安装tomcat-native
包
Engine
engine是servlet的运行实例, 他只有三个常用的属性, defaultHost
, 默认的虚拟主机实例, name
: engine组件的名字, 用于日志和错误信息记录时区别不同的引擎. 最后一个是jvmRoute
, 是用来进行集群搭建时用作路由的.
Host
虚拟主机, 常用的属性有appBase
, 就是这个host的webapp目录. autoDeploy
是否进行自动部署, unpackWARs
是否对WAR格式的文档进行解包.
Context
类似Apache中的路径别名, 一个Context定义用来表示tomcat实例中的web应用程序. 常用的属性有: docBase
, 表示响应的webapp应用程序存放位置. ``reloadable` 是否允许重新加载context相关的web应用程序类, 默认是false.
Valve
之前我们就说过了, 这个东西像是一个过滤器,并且可以存在在很多组件之间, 按照Valve定义的次序来决定生效的次序.
我们有很多种不同的Valve:
- AccessLogValve: 访问日志
- ExtendedAccessValve: 扩展功能的访问日志
- JDBCAccessLogValve: 通过JDBC将访问日志发送到数据库中
- RemoteAddrValve: 远程地址的访问控制
- RemoteHostValve: 远程主机名称的访问控制
- ….
等等.
一个使用访问控制的Valve示例就像这样子:
1 | <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.0\.0\.1" /> |
这样子配置就会只允许本机访问了:
1 | [root@VM-node0 conf]# curl -I 192.168.16.100:8080 |
LNMT & LAMT
首先我们在前端加上一个Nginx实现反向代理.
1 | location / { |
这样就很简单了. 稍微细致一点的话就像这样:
1 | index index.jsp index.html; |
除了使用Nginx, 我们还有一个选择就是使用httpd. 在使用Apache的选择上 我们就可以使用之前说的AJP协议而不是HTTP协议了. AJP是一个二进制协议, 相较于文本传输的HTTP性能要更好一点.
要想使用AJP, 要求我们的HTTPD加载模块, 我们来看一下当前加载的
1 | [root@VM-node0 ~]# httpd -M |
这里就装载了我们的ajp协议的代理协议模块. 当然 为了能够开启这个模块, 我们首先需要开开启总反代模块proxy_module
接着我们在配置文件中确认一下:
1 | [root@VM-node0 ~]# cat /etc/httpd/conf.modules.d/00-proxy.conf |
确实是启用了模块, 接着我们就可以进行配置一下了: (在虚拟主机中配置)
1 | <VirtualHost *:80> |
接着我们就可以尝试访问了.(顺便贴一下关于Tomcat主机的配置相关)
1 | <Host name="test.wyx.com" appBase="/data/webapps" |
这个是在原来的默认Host下面增加的一个, host的名字和上面的ServerName保持一致, 这样会有什么作用呢?
当我们访问192.168.16.100
的时候, 代理出现的页面就是默认的那个Tomcat的首页, 但是当我们访问test.wyx.com
的时候, 呈现在我们眼前的就是当前上面配置的那个自定义的jsp页面. 这是因为我们在代理的时候保留了host主机.
至于AJP协议的使用, 直接在上面的ProxyPass
那个地方把协议改成ajp就OK了.