- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章分析Tomcat的工作原理由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
springboot 就像一条巨蟒,慢慢缠绕着我们,使我们麻痹。不得不承认,使用了 springboot 确实提高了工作效率,但同时也让我们遗忘了很多技能。刚入社会的时候,我还是通过 tomcat 手动部署 javaweb 项目,还经常对 tomcat 进行性能调优。除此之外,还需要自己理清楚各 jar 之间的关系,以避免 jar 丢失和各版本冲突导致服务启动异常的问题。到如今,这些繁琐而又重复的工作已经统统交给 springboot 处理,我们可以把更多的精力放在业务逻辑上。但是,清楚 tomcat 的工作原理和处理请求流程和分析 spring 框架源码一样的重要。至少面试官特别喜欢问这些底层原理和设计思路。希望这篇文章能给你一些帮助.
tomcat 整体架构 。
tomcat 是一个免费的、开源的、轻量级的 web 应用服务器。适合在并发量不是很高的中小企业项目中使用.
文件目录结构 。
以下是 tomcat 8 主要目录结构 。
。
目录 | 功能说明 |
---|---|
bin | 存放可执行的文件,如 startup 和 shutdown |
conf | 存放配置文件,如核心配置文件 server.xml 和应用默认的部署描述文件 web.xml |
lib | 存放 tomcat 运行需要的jar包 |
logs | 存放运行的日志文件 |
webapps | 存放默认的 web 应用部署目录 |
work | 存放 web 应用代码生成和编译文件的临时目录 |
。
功能组件结构 。
tomcat 的核心功能有两个,分别是负责接收和反馈外部请求的连接器 connector,和负责处理请求的容器 container。其中连接器和容器相辅相成,一起构成了基本的 web 服务 service。每个 tomcat 服务器可以管理多个 service.
。
组件 | 功能 |
---|---|
connector | 负责对外接收反馈请求。它是 tomcat 与外界的交通枢纽,监听端口接收外界请求,并将请求处理后传递给容器做业务处理,最后将容器处理后的结果反馈给外界。 |
container | 负责对内处理业务逻辑。其内部由engine、host、context 和 wrapper 四个容器组成,用于管理和调用 servlet 相关逻辑。 |
service | 对外提供的 web 服务。主要包含连接器和容器两个核心组件,以及其他功能组件。tomcat 可以管理多个 service,且各 service 之间相互独立。 |
。
tomcat 连接器核心原理 。
tomcat 连接器框架——coyote 。
连接器核心功能 。
1、监听网络端口,接收和响应网络请求.
2、网络字节流处理。将收到的网络字节流转换成 tomcat request 再转成标准的 servletrequest 给容器,同时将容器传来的 servletresponse 转成 tomcat response 再转成网络字节流.
连接器模块设计 。
为满足连接器的两个核心功能,我们需要一个通讯端点来监听端口;需要一个处理器来处理网络字节流;最后还需要一个适配器将处理后的结果转成容器需要的结构.
。
组件 | 功能 |
---|---|
endpoint | 端点,用来处理 socket 接收和发送的逻辑。其内部由 acceptor 监听请求、handler 处理数据、asynctimeout 检查请求超时。具体的实现有 nioendpoint、aprendpoint 等。 |
processor | 处理器,负责构建 tomcat request 和 response 对象。具体的实现有 http11processor、streamprocessor 等。 |
adapter | 适配器,实现 tomcat request、response 与 servletrequest、servletresponse之间的相互转换。这采用的是经典的适配器设计模式。 |
protocolhandler | 协议处理器,将不同的协议和通讯方式组合封装成对应的协议处理器,如 http11nioprotocol 封装的是 http + nio。 |
。
对应的源码包路径 org.apache.coyote 。对应的结构图如下 。
tomcat 容器核心原理 。
tomcat 容器框架——catalina 。
容器结构分析 。
每个 service 会包含一个容器。容器由一个引擎可以管理多个虚拟主机。每个虚拟主机可以管理多个 web 应用。每个 web 应用会有多个 servlet 包装器。engine、host、context 和 wrapper,四个容器之间属于父子关系.
。
容器 | 功能 |
---|---|
engine | 引擎,管理多个虚拟主机。 |
host | 虚拟主机,负责 web 应用的部署。 |
context | web 应用,包含多个 servlet 封装器。 |
wrapper | 封装器,容器的最底层。对 servlet 进行封装,负责实例的创建、执行和销毁功能。 |
。
对应的源码包路径 org.apache.coyote 。对应的结构图如下 。
容器请求处理 。
容器的请求处理过程就是在 engine、host、context 和 wrapper 这四个容器之间层层调用,最后在 servlet 中执行对应的业务逻辑。各容器都会有一个通道 pipeline,每个通道上都会有一个 basic valve(如standardenginevalve), 类似一个闸门用来处理 request 和 response 。其流程图如下.
tomcat 请求处理流程 。
上面的知识点已经零零碎碎地介绍了一个 tomcat 是如何处理一个请求。简单理解就是连接器的处理流程 + 容器的处理流程 = tomcat 处理流程。哈!那么问题来了,tomcat 是如何通过请求路径找到对应的虚拟站点?是如何找到对应的 servlet 呢?
映射器功能介绍 。
这里需要引入一个上面没有介绍的组件 mapper。顾名思义,其作用是提供请求路径的路由映射。根据请求url地址匹配是由哪个容器来处理。其中每个容器都会它自己对应的mapper,如 mappedhost。不知道大家有没有回忆起被 mapper class not found 支配的恐惧。在以前,每写一个完整的功能,都需要在 web.xml 配置映射规则,当文件越来越庞大的时候,各个问题随着也会出现 。
http请求流程 。
打开 tomcat/conf 目录下的 server.xml 文件来分析一个http://localhost:8080/docs/api 请求.
第一步:连接器监听的端口是8080。由于请求的端口和监听的端口一致,连接器接受了该请求.
第二步:因为引擎的默认虚拟主机是 localhost,并且虚拟主机的目录是webapps。所以请求找到了 tomcat/webapps 目录.
第三步:解析的 docs 是 web 程序的应用名,也就是 context。此时请求继续从 webapps 目录下找 docs 目录。有的时候我们也会把应用名省略.
第四步:解析的 api 是具体的业务逻辑地址。此时需要从 docs/web-inf/web.xml 中找映射关系,最后调用具体的函数.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?xml version=
"1.0"
encoding=
"utf-8"
?>
<server port=
"8005"
shutdown=
"shutdown"
>
<service name=
"catalina"
>
<!-- 连接器监听端口是
8080
,默认通讯协议是 http/
1.1
-->
<connector port=
"8080"
protocol=
"http/1.1"
connectiontimeout=
"20000"
redirectport=
"8443"
/>
<!-- 名字为 catalina 的引擎,其默认的虚拟主机是 localhost -->
<engine name=
"catalina"
defaulthost=
"localhost"
>
<!-- 名字为 localhost 的虚拟主机,其目录是 webapps-->
<host name=
"localhost"
appbase=
"webapps"
unpackwars=
"true"
autodeploy=
"true"
>
</host>
</engine>
</service>
</server>
|
springboot 如何启动内嵌的 tomcat 。
springboot 一键启动服务的功能,让有很多刚入社会的朋友都忘记 tomcat 是啥。随着硬件的性能越来越高,普通中小项目都可以直接用内置 tomcat 启动。但是有些大一点的项目可能会用到 tomcat 集群和调优,内置的 tomcat 就不一定能满足需求了.
我们先从源码中分析 springboot 是如何启动 tomcat,以下是 springboot 2.x 的代码.
代码从 main 方法开始,执行 run 方法启动项目.
1
|
springapplication.run
|
从 run 方法点进去,找到刷新应用上下文的方法.
1
2
3
|
this
.preparecontext(context, environment, listeners, applicationarguments, printedbanner);
this
.refreshcontext(context);
this
.afterrefresh(context, applicationarguments);
|
从 refreshcontext 方法点进去,找 refresh 方法。并一层层往上找其父类的方法.
1
|
this
.refresh(context);
|
在 abstractapplicationcontext 类的 refresh 方法中,有一行调用子容器刷新的逻辑.
1
2
3
4
5
6
7
8
9
|
this
.postprocessbeanfactory(beanfactory);
this
.invokebeanfactorypostprocessors(beanfactory);
this
.registerbeanpostprocessors(beanfactory);
this
.initmessagesource();
this
.initapplicationeventmulticaster();
this
.onrefresh();
this
.registerlisteners();
this
.finishbeanfactoryinitialization(beanfactory);
this
.finishrefresh();
|
从 onrefresh 方法点进去,找到 servletwebserverapplicationcontext 的实现方法。在这里终于看到了希望.
1
2
3
4
5
6
7
8
9
|
protected
void
onrefresh() {
super
.onrefresh();
try
{
this
.createwebserver();
}
catch
(throwable var2) {
throw
new
applicationcontextexception(
"unable to start web server"
, var2);
}
}
|
从 createwebserver 方法点进去,找到从工厂类中获取 webserver的代码.
1
2
3
4
5
6
7
8
9
10
11
12
|
if
(webserver ==
null
&& servletcontext ==
null
) {
servletwebserverfactory factory =
this
.getwebserverfactory();
// 获取 web server
this
.webserver = factory.getwebserver(
new
servletcontextinitializer[]{
this
.getselfinitializer()});
}
else
if
(servletcontext !=
null
) {
try
{
// 启动 web server
this
.getselfinitializer().onstartup(servletcontext);
}
catch
(servletexception var4) {
throw
new
applicationcontextexception(
"cannot initialize servlet context"
, var4);
}
}
|
从 getwebserver 方法点进去,找到 tomcatservletwebserverfactory 的实现方法,与之对应的还有 jetty 和 undertow。这里配置了基本的连接器、引擎、虚拟站点等配置.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
webserver getwebserver(servletcontextinitializer... initializers) {
tomcat tomcat =
new
tomcat();
file basedir =
this
.basedirectory !=
null
?
this
.basedirectory :
this
.createtempdir(
"tomcat"
);
tomcat.setbasedir(basedir.getabsolutepath());
connector connector =
new
connector(
this
.protocol);
tomcat.getservice().addconnector(connector);
this
.customizeconnector(connector);
tomcat.setconnector(connector);
tomcat.gethost().setautodeploy(
false
);
this
.configureengine(tomcat.getengine());
iterator var5 =
this
.additionaltomcatconnectors.iterator();
while
(var5.hasnext()) {
connector additionalconnector = (connector)var5.next();
tomcat.getservice().addconnector(additionalconnector);
}
this
.preparecontext(tomcat.gethost(), initializers);
return
this
.gettomcatwebserver(tomcat);
}
|
服务启动后会打印日志 。
o.s.b.w.embedded.tomcat.tomcatwebserver : tomcat initialized with port(s): 8900 (http) o.apache.catalina.core.standardservice : starting service [tomcat] org.apache.catalina.core.standardengine : starting servlet engine: apache tomcat/8.5.34 o.a.catalina.core.aprlifecyclelistener : the apr based apache tomcat native library which allows optimal ... o.a.c.c.c.[tomcat].[localhost].[/] : initializing spring embedded webapplicationcontext o.s.web.context.contextloader : root webapplicationcontext: initialization completed in 16858 ms 。
end 。
文章到这里就结束了,实在是 hold 不住了,周末写了一整天还没有写到源码部分,只能放在下一章了。再写真的就要废了,有什么不对的地方请多多指出 。
以上就是tomcat的工作原理是怎样的的详细内容,更多关于tomcat 工作原理的资料请关注我其它相关文章! 。
原文链接:https://www.cnblogs.com/itdragon/p/13657104.html?utm_source=tuicool&utm_medium=referral 。
最后此篇关于分析Tomcat的工作原理的文章就讲到这里了,如果你想了解更多关于分析Tomcat的工作原理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
本文全面深入地探讨了Docker容器通信技术,从基础概念、网络模型、核心组件到实战应用。详细介绍了不同网络模式及其实现,提供了容器通信的技术细节和实用案例,旨在为专业从业者提供深入的技术洞见和实
📒博客首页:崇尚学技术的科班人 🍣今天给大家带来的文章是《Dubbo快速上手 -- 带你了解Dubbo使用、原理》🍣 🍣希望各位小伙伴们能够耐心的读完这篇文章🍣 🙏博主也在学习阶段,如若发
一、写在前面 我们经常使用npm install ,但是你是否思考过它内部的原理是什么? 1、执行npm install 它背后帮助我们完成了什么操作? 2、我们会发现还有一个成为package-lo
Base64 Base64 是什么?是将字节流转换成可打印字符、将可打印字符转换为字节流的一种算法。Base64 使用 64 个可打印字符来表示转换后的数据。 准确的来说,Base64 不算
目录 协程定义 生成器和yield语义 Future类 IOLoop类 coroutine函数装饰器 总结 tornado中的
切片,这是一个在go语言中引入的新的理念。它有一些特征如下: 对数组抽象 数组长度不固定 可追加元素 切片容量可增大 容量大小成片增加 我们先把上面的理念整理在这
文章来源:https://sourl.cn/HpZHvy 引 言 本文主要论述的是“RPC 实现原理”,那么首先明确一个问题什么是 RPC 呢?RPC 是 Remote Procedure Call
源码地址(包含所有与springmvc相关的,静态文件路径设置,request请求入参接受,返回值处理converter设置等等): spring-framework/WebMvcConfigurat
请通过简单的java类向我展示一个依赖注入(inject)原理的小例子虽然我已经了解了spring,但是如果我需要用简单的java类术语来解释它,那么你能通过一个简单的例子向我展示一下吗?提前致谢。
1、背景 我们平常使用手机和电脑上网,需要访问公网上的网络资源,如逛淘宝和刷视频,那么手机和电脑是怎么知道去哪里去拿到这个网络资源来下载到本地的呢? 就比如我去食堂拿吃的,我需要
大家好,我是飞哥! 现在 iptables 这个工具的应用似乎是越来越广了。不仅仅是在传统的防火墙、NAT 等功能出现,在今天流行的的 Docker、Kubernets、Istio 项目中也经
本篇涉及到的所有接口在公开文档中均无,需要下载 GitHub 上的源码,自己创建私有类的文档。 npm run generateDocumentation -- --private yarn gene
我最近在很多代码中注意到人们将硬编码的配置(如端口号等)值放在类/方法的深处,使其难以找到,也无法配置。 这是否违反了 SOLID 原则?如果不是,我是否可以向我的团队成员引用另一个“原则”来说明为什
我是 C#、WPF 和 MVVM 模式的新手。很抱歉这篇很长的帖子,我试图设定我所有的理解点(或不理解点)。 在研究了很多关于 WPF 提供的命令机制和 MVVM 模式的文本之后,我在弄清楚如何使用这
可比较的 jQuery 函数 $.post("/example/handler", {foo: 1, bar: 2}); 将创建一个带有 post 参数 foo=1&bar=2 的请求。鉴于 $htt
如果Django不使用“延迟查询执行”原则,主要问题是什么? q = Entry.objects.filter(headline__startswith="What") q = q.filter(
我今天发现.NET框架在做计算时遵循BODMAS操作顺序。即计算按以下顺序进行: 括号 订单 部门 乘法 添加 减法 但是我四处搜索并找不到任何文档确认 .NET 绝对 遵循此原则,是否有此类文档?如
已结束。此问题不符合 Stack Overflow guidelines .它目前不接受答案。 我们不允许提出有关书籍、工具、软件库等方面的建议的问题。您可以编辑问题,以便用事实和引用来回答它。 关闭
API 回顾 在创建 Viewer 时可以直接指定 影像供给器(ImageryProvider),官方提供了一个非常简单的例子,即离屏例子(搜 offline): new Cesium.Viewer(
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be
我是一名优秀的程序员,十分优秀!