Tomcat
目录结构
部署方式
直接将项目放到webapps目录下
配置conf/server.xml文件
在<Host>标签体中配置 <Context docBase="D:\hello" path="/hehe" /> docBase:项目存放的路径 path:虚拟目录
在conf\Catalina\localhost创建任意名称的xml文件。在文件中编写
<Context docBase="D:\hello" />
- 虚拟目录:xml文件的名称
JAVA WEB项目目录结构
- 项目的根目录
- WEB-INF目录:
- web.xml:web项目的核心配置文件
- classes目录:放置字节码文件的目录
- lib目录:放置依赖的jar包
- WEB-INF目录:
架构
整体设计:
- Connector负责连接的建立以及数据返回
- Container(Engine)负责请求的具体处理
- Service 负责维护Conenctor与Container之间的映射关系
Container设计:
- Engine:Container的具体实现
- Host:以域名为主的一个虚拟主机
- Wrapper:代表Servlet实例
- Context:代表一个独立的web应用
- PipeLine:各个组件之间传递消息的管道
Connector设计:
- Endpoint负责监听连接,将连接交给Processor处理
- Processor再将请求映射到Container
Executor:
共享线程池由Service维护
外部依赖 Bootstrap和Catalina:
Bootstrap启动Cataina Catalina启动Server 实现了Bootstrap 与 Server进行解耦
启动流程
请求处理
总体流程:
CoyoteAdapter.service
- 请求映射:
CoyoteAdapter.postParseRequest
- 调用容器:
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)
- 请求映射:
Catalina请求处理:
类加载器
通过每个app使用自己的类加载器来达到:
- 隔离:不同的app依赖类库不会相互影响
- 灵活:重新部署时的问题
对于Web 应用类加载器,它的加载顺序:
- 从缓存加载
- 如果缓存没有 从JVM的Bootstrap类加载器加载 (防止JAVA SE核心类被覆盖)
- 如果还是没有 从当前类加载器加载 (如果开启委托 则会遵循JVM双亲委托模型)
- 还没有 再从父类加载器加载
Catalina
- Servlet容器
- Digester : XML解析工具
Server 创建
- 解析Server:
Catalina.createStartDigester
- 解析Engine:
EngineRuleSet.addRuleInstances
- 解析Host:
HostRuleSet.addRuleInstances
- 解析Context:
ContextRuleSet.addRuleInstances
Web应用启动流程
StandardHost:
- 从server.xml配置加载
- 或者扫描webapps目录加载
HostConfig:
- START_EVENT事件:会根据context描述文件或者对webapps目录下war包目录等部署应用 调用
deployApps()
- PERIODIC_EVENT事件:检查文件是否发生变更 是则重新部署(之前不存在的应用)或者重新加载(之前存在的应用)
StandardContext:
应用初始化及启动
ContextConfig:
- AFTER_INIT_EVENT事件:加载Context配置文件
- BEFORE_START_EVENT事件:处理docBase(应用所在文件夹)问题
- CONFIGURE_START_EVENT:初始化操作, 解析XML配置文件(或者扫描目录 使用注解的方式) 创建 Servlet Filter等组件
StandardWrapper:
- 根据配置load servlet 以及对 servlet 初始化
Context的命名与请求路径映射
Catalina 自带的 Servlet
- DefaultServlet:处理静态资源 处理目录请求
- JspServlet:编译jsp文件 处理jsp请求
Coyote
- 请求连接器的实现
支持的传输协议:
- HTTP1.1
- HTTP2.0
- AJP1.3
支持的IO方案:
- NIO
- NIO2
- APR
HTTP 配置:
<!-- server.xml -->
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- 使用NIO方式处理HTTP1.1 -->
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true">
<!--
maxThreads:指定Connector创建请求处理线程的最大数
maxSpareThreads:允许空闲线程的最大数
minSpareThreads
tcpNoDelay:禁止TCP通过批量发送数据来提高网络利用率
maxKeepAliveRequest: 最大keepalive的连接数
socketBuffer
enableLookups:是否开启request.getRemoteHost() DNS查询
-->
概念
- Endpoint 通信端点 负责Socekt接收处理
- Porcessor 负责创建请求和响应 将请求转发到Catalina
- ProtocolHandler 封装Endpoint Processor
- UpgradeProtocol 处理HTTP协议的升级协议
请求流程
AJP
<!-- server.xml -->
<Connector protocol="AJP/1.3"
address="::1"
port="8009"
redirectPort="8443" />
AJP(Apache JServ Protocol)是定向包协议。因为性能原因,使用二进制格式来传输可读性文本。WEB服务器通过 TCP连接 和 SERVLET容器连接
包结构:
有效载荷的前一个字节代表类型
请求处理:
Jasper
使用单独的类加载器
编译方式
运行时编译:
编译结果:
- 首选存放在 context-param 的scratchdir
- 否则是 $CATALINA_BASE/work/Engine名称/Host名称/Context名称
- 再否则在系统临时文件目录下
预编译:
jspc
编译原理
// 继承该类
class index_jsp extends HttpJspBase
private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
private static Map<String, Long> _jspx_dependants; // 依赖的外部资源
private static final Set<String> _jspx_imports_packages = new HashSet(); // 导入的包
private static final Set<String> _jspx_imports_classes; // 导入的类
_jspService
处理请求:
- 定义了out pageContext session application config page 等局部变量
- 对于静态内容调用out.write
- 处理jsp标签
配置管理
JVM配置
:: JVM启动参数
set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%"
系统属性:略
服务器配置
catalina.properties: 容器启动阶段的配置
server.xml: 服务器核心配置
Server
Service
Executor 线程池配置 默认其他组件会创建自己的线程池
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/>
Connector 默认配置了两个 HTTP 和 AJP
<Connector port="8080" -- 监听端口 executor="sharedThreadPool" -- 线程池 enableLookups="false" -- 调用request.getRemoteHost 是否调用DNS解析获取主机名 redirectPort="8443" -- SSL 转发端口 acceptCount="100" -- 控制 socket 排队连接的最大数 connectionTimeout="2000o" -- Connector 接收连接处理的超时时间 URIEncoding="UTF-8" -- 解码URI的编码 compression="on" -- 开启压缩 compressionMinSize="2048" -- 最小压缩尺寸 noCompressionUserAgents="gozilla,traviata" -- 符合表达式的UA头不压缩 compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" />
Engine 可以指定虚拟主机
Host
- name 域名
- appBase 存放应用的目录
- unpackWARs 是否解压war包
- autoDeploy 定期检测 自动部署
- Alias 可以配置新的域名
Context
- docBase 具体应用的目录
- path Context路径
CookieProcessor 指定cookie处理器
Loader 用于管理 web 应用的类加载器
- delegate 属性可以打破双亲委派模型
- reloadable 属性会监控资源变化后重新加载应用
- loaderClass 指定类加载器的具体实现
Manger 会话管理器
- Standard和Presistent
Resources 资源共享
<Context docBase="myApp" path=" /myApp"> <Resources> <PreResources className='org.apache.catalina.webresources.FileResourceSet' base=" /Users/liuguangrui/Documents/sample/app.jsp" webAppMount=" /app/app.jsp"/> </Resources> </Context>
JarScanner
content.xml
Web 应用配置
- context-param: ServerContext.getInitParameter() 可以获取到的参数
- session-config 会话配置
- 三种追踪模式 COOKIE URL SSL
- servlet 声明servlet及其映射
- listener
- filter
- mime-mapping 映射文件类型与对应的content-type
- welcome-file-list
- error-page
- locale-encoding-mapping-list 本地化与响应编码的关系
- 安全配置
- jndi配置
内置的 Filter
- CorsFilter:解决跨域问题
- CsrfPreventionFilter:防止CSRF攻击
- ExpiresFilter:控制缓存过期与否
- FailedRequestFilter:解析参数失败就返回错误
- RemoteAddrFilter:只放行符合特定表达式的IP地址
- RemoteHostFilter:只放行符合特定表达式的主机
- RemoteIpFilter:前方有负载均衡器的情况下 将getRemoteAddr()替换为 X-Forwarded-For 中的IP
- RequestDumperFilter:以日志形式输出请求和响应对象 主要用于调试
- SetCharacterEncodingFilter:设置请求编码
Tomcat 管理
/host-manager/html
集群
Tomcat 本身就不适合配置集群 一种通用的解决方案是 接入层为 Nginx
Nginx 对后端的Tomcat进行负载均衡
Tomcat上的Web应用最好是设计成无状态的 如果仍然需要保持会话 最好使用一台独立的服务器来存储会话 比如 Redis
而不要使用Tomcat的会话同步功能
安全
安装部署:下载安全 移除自带的几个Web应用
server.xml:
- 删除不必要的连接器
- 删除UserDatabase
- 修改关键配置:8005管理端口
- 避免恶意web应用的自动启动:autoDeploy
- 允许有限的客户端访问
- 避免将异常堆栈打印到客户端
- listing会导致目录泄漏以及DoS攻击
应用安全
传输安全(SSL)
JAVA安全策略
优化
JVM 优化
Tomcat 配置优化
server.xml:
- 链接器maxConnections 属性:超过该属性的连接会被阻塞
- tcpNoDelay:禁止TCP缓存并发送
- maxKeepAliveRequest
- socketBuffer
- enableLookups
网络传输优化:
- 静态文件压缩
- 高性能链接器(NIO NIO2)
- 禁用自动部署
JSP页面配置(web.xml):
- development 设置为false 不自动检测JSP页面变动
- ...
继承 web 服务器:
- 动静分离
- 负载均衡
应用优化
- 减少通信次数
- 减少通信数据流
- 推迟会话创建
- 不在会话存储大对象
- 合理定义对象作用域
- 使用连接池提高性能
- 使用缓存提高性能
- 最小化日志
附加功能
- 嵌入式启动
- websocket