JAVAWeb-Tomcat学习笔记
Tomcat
服务器分类
服务器的概念非常的广泛,它可以指代一台特殊的计算机(相比普通计算机运行更快、负载更高、价格更贵),也可以指代用于部署网站的应用
。
我们这里说的服务器,其实是web服务器,或者应用服务器。它本质就是一个软件,一个应用。作用就是发布我们的应用(工程),让用户可以通过浏览器访问我们的应用。
常见的应用服务器:
服务器名称 | 说明 |
---|---|
weblogic | 实现了 JavaEE 规范,重量级服务器,又称为 JavaEE 容器 |
websphereAS | 实现了 JavaEE 规范,重量级服务器。 |
JBOSSAS | 实现了 JavaEE 规范,重量级服务器,免费 |
Tomcat | 实现了 jsp/servlet 规范,是一个轻量级服务器,开源免费 |
基本使用
- 使用Tomcat自带的startup.bat,startup.sh、shutdown.bat,shutdown.sh进行服务启动、停止
- 使用IDEA集成Run -> Edit Configurations -> Templates -> Tomcat Server -> Local
执行原理
整体架构
组件介绍:
- GlobalNamingResources:实现 JNDI,指定一些资源的配置信息
- Server:Tomcat 是一个 Servlet 容器,
一个 Tomcat 对应一个 Server,一个 Server 可以包含多个 Service
- Service:核心服务是 Catalina,用来对请求进行处理,一个 Service 包含多个 Connector 和一个 Container
- Connector:连接器,负责处理客户端请求,解析不同协议及 I/O 方式
- Executor:线程池
- Container:包含 Engine,Host,Context,Wrapper 等组件
- Engine:服务交给引擎处理请求,Container 容器中顶层的容器对象,一个 Engine 可以包含多个 Host 主机
- Host:Engine 容器的子容器,一个 Host 对应一个网络域名,一个 Host 包含多个 Context
- Context:Host 容器的子容器,表示一个 Web 应用
- Wrapper:Tomcat 中的最小容器单元,表示 Web 应用中的 Servlet
核心类库:
- Coyote:Tomcat 连接器的名称,封装了底层的网络通信,为 Catalina 容器提供了统一的接口,使容器与具体的协议以及 I/O 解耦
- EndPoint:Coyote 通信端点,即通信监听的接口,是 Socket 接收和发送处理器,是对传输层的抽象,用来实现 TCP/IP 协议
- Processor : Coyote 协议处理接口,用来实现 HTTP 协议,Processor 接收来自 EndPoint 的 Socket,读取字节流解析成 Tomcat 的 Request 和 Response 对象,并通过 Adapter 将其提交到容器处理,Processor 是对应用层协议的抽象
- CoyoteAdapter:适配器,连接器调用 CoyoteAdapter 的 sevice 方法,传入的是 TomcatRequest 对象,CoyoteAdapter 负责将TomcatRequest 转成 ServletRequest,再调用容器的 service 方法
启动过程
Tomcat 的启动入口是 Bootstrap#main 函数,首先通过调用 bootstrap.init()
初始化相关组件:
initClassLoaders()
:初始化三个类加载器,commonLoader 的父类加载器是启动类加载器Thread.currentThread().setContextClassLoader(catalinaLoader)
:自定义类加载器加载 Catalina 类,打破双亲委派Object startupInstance = startupClass.getConstructor().newInstance()
:反射创建 Catalina 对象method.invoke(startupInstance, paramValues)
:反射调用方法,设置父类加载器是 sharedLoadercatalinaDaemon = startupInstance
:引用 Catalina 对象
daemon.load(args)
方法反射调用 Catalina 对象的 load 方法,对服务器的组件进行初始化,并绑定了 ServerSocket 的端口:
-
parseServerXml(true)
:解析 XML 配置文件 -
getServer().init()
:服务器执行初始化,采用责任链的执行方式-
LifecycleBase.init()
:生命周期接口的初始化方法,开始链式调用 -
StandardServer.initInternal()
:Server 的初始化,遍历所有的 Service 进行初始化 -
StandardService.initInternal()
:Service 的初始化,对 Engine、Executor、listener、Connector 进行初始化 -
StandardEngine.initInternal()
:Engine 的初始化getRealm()
:创建一个 Realm 对象ContainerBase.initInternal()
:容器的初始化,设置处理容器内组件的启动和停止事件的线程池
-
Connector.initInternal()
:Connector 的初始化1
2
3public Connector() {
this("HTTP/1.1"); //默认无参构造方法,会创建出 Http11NioProtocol 的协议处理器
}-
adapter = new CoyoteAdapter(this)
:实例化 CoyoteAdapter 对象 -
protocolHandler.setAdapter(adapter)
:设置到 ProtocolHandler 协议处理器中 -
ProtocolHandler.init()
:协议处理器的初始化,底层调用AbstractProtocol#init
方法endpoint.init()
:端口的初始化,底层调用AbstractEndpoint#init
方法NioEndpoint.bind()
:绑定方法initServerSocket()
:初始化 ServerSocket,以 NIO 的方式监听端口serverSock = ServerSocketChannel.open()
:NIO 的方式打开通道serverSock.bind(addr, getAcceptCount())
:通道绑定连接端口serverSock.configureBlocking(true)
:切换为阻塞模式(没懂,为什么阻塞)
initialiseSsl()
:初始化 SSL 连接selectorPool.open(getName())
:打开选择器,类似 NIO 的多路复用器
-
-
初始化完所有的组件,调用 daemon.start()
进行组件的启动,底层反射调用 Catalina 对象的 start 方法:
-
getServer().start()
:启动组件,也是责任链的模式-
LifecycleBase.start()
:生命周期接口的初始化方法,开始链式调用 -
StandardServer.startInternal()
:Server 服务的启动globalNamingResources.start()
:启动 JNDI 服务for (Service service : services)
:遍历所有的 Service 进行启动
-
StandardService.startInternal()
:Service 的启动,对所有 Executor、listener、Connector 进行启 -
StandardEngine.startInternal()
:启动引擎,部署项目ContainerBase.startInternal()
:容器的启动- 启动集群、Realm 组件,并且创建子容器,提交给线程池
((Lifecycle) pipeline).start()
:遍历所有的管道进行启动Valve current = first
:获取第一个阀门((Lifecycle) current).start()
:启动阀门,底层ValveBase#startInternal
中设置启动的状态current = current.getNext()
:获取下一个阀门
-
Connector.startInternal()
:Connector 的初始化-
protocolHandler.start()
:协议处理器的启动endpoint.start()
:端点启动NioEndpoint.startInternal()
:启动 NIO 的端点createExecutor()
:创建 Worker 线程组,10 个线程,用来进行任务处理initializeConnectionLatch()
:用来进行连接限流,最大 8*1024 条连接poller = new Poller()
:创建 Poller 对象,开启了一个多路复用器 SelectorThread pollerThread = new Thread(poller, getName() + "-ClientPoller")
:创建并启动 Poller 线程,Poller 实现了 Runnable 接口,是一个任务对象,线程 start 后进入 Poller#run 方法pollerThread.setDaemon(true)
:设置为守护线程startAcceptorThread()
:启动接收者线程acceptor = new Acceptor<>(this)
:创建 Acceptor 对象Thread t = new Thread(acceptor, threadName)
:创建并启动 Acceptor 接受者线程
-
-
处理过程
- Acceptor 监听客户端套接字,每 50ms 调用一次
serverSocket.accept
,获取 Socket 后把封装成 NioSocketWrapper(是 SocketWrapperBase 的子类),并设置为非阻塞模式,把 NioSocketWrapper 封装成 PollerEvent 放入同步队列中 - Poller 循环判断同步队列中是否有就绪的事件,如果有则通过
selector.selectedKeys()
获取就绪事件,获取 SocketChannel 中携带的 attachment(NioSocketWrapper),在 processKey 方法中根据事件类型进行 processSocket,将 Wrapper 对象封装成 SocketProcessor 对象,该对象是一个任务对象,提交到 Worker 线程池进行执行 SocketProcessorBase.run()
加锁调用SocketProcessor#doRun
,保证线程安全,从协议处理器 ProtocolHandler 中获取 AbstractProtocol,然后创建 Http11Processor 对象处理请求Http11Processor#service
中调用CoyoteAdapter#service
,把生成的 Tomcat 下的 Request 和 Response 对象通过方法 postParseRequest 匹配到对应的 Servlet 的请求响应,将请求传递到对应的 Engine 容器中调用 Pipeline,管道中包含若干个 Valve,执行完所有的 Valve 最后执行 StandardEngineValve,继续调用 Host 容器的 Pipeline,执行 Host 的 Valve,再传递给 Context 的 Pipeline,最后传递到 Wrapper 容器StandardWrapperValve#invoke
中创建了 Servlet 对象并执行初始化,并为当前请求准备一个 FilterChain 过滤器链执行 doFilter 方法,ApplicationFilterChain#doFilter
是一个责任链的驱动方法,通过调用 internalDoFilter 来获取过滤器链的下一个过滤器执行 doFilter,执行完所有的过滤器后执行servlet.service
的方法- 最后调用 HttpServlet#service(),根据请求的方法来调用 doGet、doPost 等,执行到自定义的业务方法