【分布式技术专题】「分布式技术架构」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)

探索Tomcat技术架构设计模式的奥秘

  • Tomcat系统架构分析
    • Tomcat 整体结构
      • Tomcat总体结构图
        • 以 Service 作为“婚姻”
          • 1) Service 接口
            • 方法列表
          • 2) StandardService 的类结构图
            • 方法列表
          • 3) StandardService. SetContainer
          • 4) StandardService. addConnector
        • 以 Server 为“居”
          • 1) Server 的类结构图
          • 2) StandardServer.addService
        • 组件的生命线“Lifecycle”
          • 1) Lifecycle 类结构图
          • 2) StandardServer.Start
          • 3) StandardServer.Stop

Tomcat系统架构分析

 Tomcat 的结构很复杂,但是Tomcat也非常的模块化,找到了Tomcat 最核心的模块,就抓住Tomcat的“七寸”。

Tomcat 整体结构

Tomcat的总体结构从外到内进行分布,最大范围的服务容器是Server组件,Service服务组件(可以有多个同时存在),Connector(连接器)、Container(容器服务),其他组件:Jasper(Jasper解析)、Naming(命名服务)、Session(会话管理)、Logging(日志管理)、JMX(Java 管理器扩展服务)、Websocket(交互服务)。

Tomcat总体结构图

从上图中可以看出 Tomcat 的心脏是两个组件:Connector 和
Container,关于这两个组件将在后面详细介绍。

Connector 组件是可以被替换,这样可以提供给服务器设计者更多的选择,因为这个组件是如此重要,不仅跟服务器的设计的本身,而且和不同的应用场景也十分相关,所以一个 Container 可以选择对应多个 Connector。
多个 Connector 和一个 Container 就形成了一个 Service。

Service的概念大家都很熟悉了,有了 Service 就可以对外提供服务了,但是
Service 还要一个生存的环境,必须要有人能够给她生命、掌握其生死大权,那就非 Server 莫属了。所以整个 Tomcat 的生命周期由Server 控制。

以 Service 作为“婚姻”

我们将 Tomcat 中 Connector、Container 作为一个整体比作一对情
侣的话,Connector 主要负责对外交流,可以比作为Boy,Container
主要处理 Connector 接受的请求,主要是处理内部事务,可以比作
为 Girl。那么这个 Service 就是连接这对男女的结婚证了。是
Service 将它们连接在一起,共同组成一个家庭。当然要组成一个家
庭还要很多其它的元素。

说白了,Service 只是在 Connector 和 Container 外面多包一层,
把它们组装在一起,向外面提供服务,一个 Service 可以设置多个
Connector,但是只能有一个 Container 容器。这个 Service 接口的
方法列表如下:


1) Service 接口
方法列表

从 Service 接口中定义的方法中可以看出,它主要是为了关联Connector 和 Container,同时会初始化它下面的其它组件,注意接口中它并没有规定一定要控制它下面的组件的生命周期。所有组件的生命周期在一个 Lifecycle 的接口中控制,这里用到了一个重要的设计模式,关于这个接口将在后面介绍。

Tomcat 中 Service 接口的标准实现类是 StandardService 它不仅
实现了 Service 借口同时还实现了 Lifecycle 接口,这样它就可以控制它下面的组件的生命周期了。StandardService 类结构图如下

2) StandardService 的类结构图
方法列表

从上图中可以看出除了 Service 接口的方法的实现以及控制组件生命周期的 Lifecycle 接口的实现,还有几个方法是用于在事件监听的方法的实现,不仅是这个 Service 组件,Tomcat中其它组件也同样有这几个方法,这也是一个典型的设计模式,将在后面介绍。

下面看一下 StandardService 中主要的几个方法实现的代码,下面是
setContainer 和 addConnector 方法的源码:

3) StandardService. SetContainer
public void setContainer(Container container) {
 	Container oldContainer = this.container;
 	if ((oldContainer != null) && (oldContainer instanceof Engine))
		 ((Engine) oldContainer).setService(null);
 		this.container = container;
		 if ((this.container != null) && (this.container instanceof Engine))
			 ((Engine) this.container).setService(this);
			 if (started && (this.container != null) && (this.container instanceof Lifecycle){
				 try {
						 ((Lifecycle) this.container).start();
				 } catch (LifecycleException e) {
						 ;
				}
			 }
 			synchronized (connectors) {
				 for (int i = 0; i < connectors.length; i++)
				 		connectors[i].setContainer(this.container);
				 }
				 if (started && (oldContainer != null) && (oldContainer instanceof 
														   		Lifecycle)) {
					 try {
					 ((Lifecycle) oldContainer).stop();
					 } catch (LifecycleException e) {
					 ;
					 }
					 }
			 support.firePropertyChange("container", oldContainer, this.container);
		}

这段代码很简单,其实就是先判断当前的这个 Service 有没有已经关联了 Container,如果已经关联了,那么去掉这个关联关系——oldContainer.setService(null)。如果这个 oldContainer 已经被启动了,结束它的生命周期。然后再替换新的关联、再初始化并开始这个新的 Container 的生命周期。最后将这个过程通知感兴趣的事件监
听程序。这里值得注意的地方就是,修改 Container 时要将新的Container 关联到每个 Connector,还好 Container 和 Connector 没有双向关联,不然这个关联关系将会很难维护。

4) StandardService. addConnector
public void addConnector(Connector connector) {
 	synchronized (connectors) {
 		connector.setContainer(this.container);
 		connector.setService(this);
 		Connector results[] = new Connector[connectors.length + 1];
 		System.arraycopy(connectors, 0, results, 0, connectors.length);
 		results[connectors.length] = connector;
 		connectors = results;
 		if (initialized) {
			 try {
			 connector.initialize();
			 } catch (LifecycleException e) {
			 e.printStackTrace(System.err);
			 }
 		}
 		if (started && (connector instanceof Lifecycle)) {
			 try {
			 	((Lifecycle) connector).start();
			 } catch (LifecycleException e) {
			 ;
			 }
 		}
 		support.firePropertyChange("connector", null, connector);
	 } 
}

上面是 addConnector 方法,这个方法也很简单,首先是设置关联关系,然后是初始化工作,开始新的生命周期。这里值得一提的是,注意 Connector 用的是数组而不是 List 集合,这个从性能角度考虑可以理解,有趣的是这里用了数组但是并没有向我们平常那样,一开始就分配一个固定大小的数组,它这里的实现机制是:重新创建一个当前大小的数组对象,然后将原来的数组对象 copy 到新的数组中,这种方式实现了类似的动态数组的功能,这种实现方式,值得我们以后拿来借鉴。


最新的 Tomcat6 中 StandardService 也基本没有变化,但是从Tomcat5 开始 Service、Server 和容器类都继承了MBeanRegistration 接口,Mbeans 的管理更加合理。

以 Server 为“居”

前面说一对情侣因为 Service 而成为一对夫妻,有了能够组成一个家庭的基本条件,但是它们还要有个实体的家,这是它们在社会上生存之本,有了家它们就可以安心的为人民服务了,一起为社会创造财富。

Server 要完成的任务很简单,就是要能够提供一个接口让其它程序能够访问到这个 Service 集合、同时要维护它所包含的所有 Service 的生命周期,包括如何初始化、如何结束服务、如何找到别人要访问的 Service。

还有其它的一些次要的任务,如您住在这个地方去登记啊、可能还有要配合当地机关日常的安全检查什么
的。

Server 的类结构图如下:

1) Server 的类结构图


它的标准实现类 StandardServer 实现了上面这些方法,同时也实现LifecycleMbeanRegistration 两个接口的所有方法,下面主要看一下 StandardServer 重要的一个方法 addService 的实现:

2) StandardServer.addService
public void addService(Service service) {
 	service.setServer(this);
 	synchronized (services) {
 	Service results[] = new Service[services.length + 1];
 	System.arraycopy(services, 0, results, 0, services.length);
 	results[services.length] = service;
 	services = results;
 		if (initialized) {
			 try {
			 service.initialize();
			 } catch (LifecycleException e) {
			 e.printStackTrace(System.err);
			 }
		}
		 if (started && (service instanceof Lifecycle)) {
					 try {
					 ((Lifecycle) service).start();
					 } catch (LifecycleException e) {
					 ;
					 }
 		}
 		support.firePropertyChange("service", null, service);
 }
}

从上面第一句就知道了 Service 和 Server 是相互关联的,Server也是和 Service 管理 Connector 一样管理它,也是将 Service 放在一个数组中,后面部分的代码也是管理这个新加进来的 Service 的生命周期。Tomcat6 中也是没有什么变化的。


组件的生命线“Lifecycle”
 前面一直在说 Service 和 Server 管理它下面组件的生命周期,那它们是如何管理的呢?

Tomcat 中组件的生命周期是通过 Lifecycle 接口来控制的,组件只要继承这个接口并实现其中的方法就可以统一被拥有它的组件控制了,这样一层一层的直到一个最高级的组件就可以控制 Tomcat 中所有组件的生命周期,这个最高的组件就是 Server,而控制 Server 的是 Startup,也就是您启动和关闭 Tomcat

下面是 Lifecycle 接口的类结构图:

1) Lifecycle 类结构图

除了控制生命周期的 Start 和 Stop 方法外还有一个监听机制,在生命周期开始和结束的时候做一些额外的操作。这个机制在其它的框架中也被使用,如在 Spring 中。关于这个设计模式会在后面介绍。

Lifecycle 接口的方法的实现都在其它组件中,就像前面中说的,组件的生命周期由包含它的父组件控制,所以它的 Start 方法自然就是调用它下面的组件的 Start 方法,Stop 方法也是一样。如在 Server 中 Start 方法就会调用 Service 组件的 Start 方法,Server 的Start 方法代码如下:

2) StandardServer.Start
public void start() throws LifecycleException {
 	if (started) {
 	      log.debug(sm.getString("standardServer.start.started"));
              return;
 	}
 	lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
 	lifecycle.fireLifecycleEvent(START_EVENT, null);
 	started = true;
 	synchronized (services) {
 		for (int i = 0; i < services.length; i++) {
 			if (services[i] instanceof Lifecycle)
 				((Lifecycle) services[i]).start();
 			}
 		}
 	lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}

监听的代码会包围 Service 组件的启动过程,就是简单的循环启动所有 Service 组件的Start方法,但是所有 Service 必须要实现Lifecycle 接口,这样做会更加灵活。
Server 的 Stop 方法代码如下:

3) StandardServer.Stop
public void stop() throws LifecycleException {
 	if (!started)
 		return;
 	lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
 	lifecycle.fireLifecycleEvent(STOP_EVENT, null);
 	started = false;
 	for (int i = 0; i < services.length; i++) {
 		if (services[i] instanceof Lifecycle)
 			((Lifecycle) services[i]).stop();
 		}
 		lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
}

它所要做的事情也和 Start 方法差不多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/344015.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nvm 配置淘宝镜像失效,以及安装node后 npm-v 无效

win11 nvm版本 1.1.4 和1.1.7和1.1.12&#xff08;目前最新版本24年 一月二十三日&#xff09; 以上nvm版本都会出现一下问题&#xff0c; 从https://github.com/coreybutler/nvm-windows/releases 下载nvm安装包如下图 傻瓜式安装后&#xff0c;不用去配置环境变量&#…

冰箱和小型制冷系统毛细管选型计算软件介绍

这是一款比较实用的软件&#xff0c;可以快速根据制冷剂类型、制冷量、蒸发温度、冷凝温度、回气温度大致确定毛细管内径和长度。可以根据不同的内径选择对应的长度&#xff0c;通常内径越小长度也会越短。可以选择的冷媒类型 可以设置个人喜好的单位 对于初学者可以使用该软件…

router4j--SpringCloud动态路由利器

前言 本文介绍Java的动态路由中间件&#xff1a;router4j。router4j用于SpringCloud项目&#xff0c;它可以将某个url请求路由到指定的机器上&#xff0c;也可以将所有请求强制转到指定机器。 问题描述 Java后端在开发SpringCloud项目时如果同一个应用起了多个实例&#xff…

UE5.2、CesiumForUnreal实现加载GeoJson绘制单面

文章目录 前言一、实现目标二、实现过程1.实现原理2.数据读取3.三角剖分3.具体代码 4.蓝图测试 前言 UE5、CesiumForUnreal实现加载GeoJson绘制单面&#xff08;Polygon&#xff09;功能&#xff08;StaticMesh方式&#xff09; 一、实现目标 通过读取本地的Geojson数据&…

6.go 库源码文件

目录 概述总结例子代码结构代码执行结果 结束 概述 库源码文件是不能被直接运行的源码文件&#xff0c;它仅用于存放程序实体&#xff0c;这些程序实体可以被其他代码使用&#xff08;只要遵从 Go 语言规范的话&#xff09; 那么程序实体是什么呢&#xff1f;在 Go 语言中&…

深度强化学习Task3:A2C、A3C算法

本篇博客是本人参加Datawhale组队学习第三次任务的笔记 【教程地址】 文章目录 Actor-Critic 算法提出的动机Q Actor-Critic 算法A2C 与 A3C 算法广义优势估计A3C实现建立Actor和Critic网络定义智能体定义环境训练利用JoyRL实现多进程 练习总结 Actor-Critic 算法提出的动机 蒙…

基于springboot+vue的网上租赁系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

浪花 - 查询队伍列表

一、接口设计 1. 请求参数&#xff1a;封装 TeamQuery package com.example.usercenter.model.dto;import com.example.usercenter.common.PageRequest; import lombok.Data;/*** author 乐小鑫* version 1.0* Date 2024-01-22-20:14*/ Data public class TeamQuery extends …

助力医疗数字化转型,贝锐x医百科技案例解析

在医疗数字化这个历史进程的大浪潮中&#xff0c;医药企业扮演着重要的角色&#xff0c;其重要程度恐怕仅次于医疗机构本身。同时&#xff0c;数字化转型对于医药企业的赋能作用也是十分明显的&#xff0c;尤其在营销端&#xff0c;一系列的数字化管理、数字化推广方案已经成为…

Windows下安装达梦8开发版数据库

达梦数据库属于国产主流数据库之一&#xff0c;本文记录WIndows下安装最新的达梦8数据库的过程。   达梦官网&#xff08;参考文献1&#xff09;下载开发版&#xff08;X86平台&#xff09;版安装包&#xff0c;如下图所示&#xff1a; 解压安装包后&#xff0c;其中包含ISO文…

Neo4j基本用法

Neo4j基本用法 找到电影的示例点开 跳到2&#xff0c;然后点击提供的示例代码&#xff0c;会自动复制粘贴到上方的控制台&#xff0c;然后点击执行按钮 当我们点击了执行以后&#xff0c;会得到如下的基本图模型 将指示器跳到3&#xff0c;然后点击第一个查询语句 同样地&…

虚拟机网络配置及Moba工具的使用

A、设置IP和网关 1、设置IP [roothadoop00 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0 &#xff08;修改如下标红内容&#xff0c;没有的就添加&#xff09; DEVICEeth0 HWADDR08:00:27:BD:9D:B5 #不用改 TYPEEthernet UUID53e4e4b6-9724-43ab-9da7-68792e611031…

人类行为动作数据集大合集

最近收集了一大波关于人类行为动作的数据集&#xff0c;主要包括&#xff1a;动作识别、行为识别、活动预测、动作行为分类等数据集。废话不多说&#xff0c;接下来就给大家介绍这些数据集&#xff01;&#xff01; 1、用于自动视频编辑的视频Blooper数据集 用于自动视频编辑…

助力工业焊缝质量检测,YOLOv7【tiny/l/x】不同系列参数模型开发构建工业焊接场景下工件表面焊接缺陷检测识别分析系统

焊接是一个不陌生但是对于开发来说相对小众的场景&#xff0c;在工件表面焊接场景下常常有对工件表面缺陷智能自动化检测识别的需求&#xff0c;工业AI结合落地是一个比较有潜力的场景&#xff0c;在我们前面的博文开发实践中也有一些相关的实践&#xff0c;感兴趣的话可以自行…

python-分享篇-时钟

文章目录 代码效果 代码 Function:Python制作简易时钟 Author:Charles 微信公众号:TONOWimport turtle import datetime悬空移动 def move(distance):turtle.penup()turtle.forward(distance)turtle.pendown()创建表针turtle def createHand(name, length):turtle.reset()move(…

【开源】基于JAVA的班级考勤管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统基础支持模块2.2 班级学生教师支持模块2.3 考勤签到管理2.4 学生请假管理 三、系统设计3.1 功能设计3.1.1 系统基础支持模块3.1.2 班级学生教师档案模块3.1.3 考勤签到管理模块3.1.4 学生请假管理模块 3.2 数据库设…

C++PythonC# 三语言OpenCV从零开发(3):图像读取和显示

文章目录 相关链接前言Mat是什么读取图片CC#Python 灰度处理CCSharpPython 打印图像信息CCsharpPython 总结 相关链接 C&Python&Csharp in OpenCV 专栏 【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程&#xff08;附带课程课件资料课件笔记&#xff09; …

C#winform上位机开发学习笔记11-串口助手接收数据用波形显示功能添加

1.功能描述 接收串口数据&#xff0c;并将收到的十六进制数据用坐标系的方式将数据波形展示出来 2.代码部分 步骤1&#xff1a;定义链表&#xff0c;用于数据保存 //数据结构-线性链表private List<byte> DataList new List<byte>(); 步骤2&#xff1a;定义波…

消息中间件之Kafka(一)

1.简介 高性能的消息中间件&#xff0c;在大数据的业务场景下性能比较好&#xff0c;kafka本身不维护消息位点&#xff0c;而是交由Consumer来维护&#xff0c;消息可以重复消费&#xff0c;并且内部使用了零拷贝技术&#xff0c;性能比较好 Broker持久化消息时采用了MMAP的技…

springboot集成tess4j

spring整合tess4j用于OCR识别图片&#xff0c;在windows环境识别正常&#xff0c;在liunx没有反应&#xff0c;本文用于解决部署linux问题。 整合springboot 1、引入pom文件 <dependency><groupId>net.sourceforge.tess4j</groupId><artifactId>tess…