# Minicat **Repository Path**: orangezh/minicat ## Basic Information - **Project Name**: Minicat - **Description**: 手写实现简易tomcat,并实现多实例部署 - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2020-10-08 - **Last Updated**: 2021-10-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Minicat #### 介绍 手写实现简易tomcat,并实现多实例部署 #### Tomcat主要目录结构

tomcat主要目录结构
Bin:主要是用来存放tomcat的命令,启动(startup)/停止(shutdown)脚本等,修改catalina可以设置tomcat的内存 Conf:主要是用来存放tomcat的一些配置文件; - server.xml可以设置端口号、设置域名或IP、默认加载的项目、请求编码 - web.xml可以设置tomcat支持的文件类型 - context.xml可以用来配置数据源之类的 - tomcat-users.xml用来配置管理tomcat的用户与权限 - 在Catalina目录下可以设置默认加载的项目 Webapps:用来存放应用程序,当tomcat启动时会去加载webapps目录下的应用程序,可以以文件夹、war包、jar包的形式发布应用;也可以把应用程序放在其他位置,在配置文件中修改路径 Work:用来存放tomcat在运行时的编译后文件,例如JSP编译后的文件,清空work目录,然后重启tomcat,可以达到清除缓存的作用 Lib:主要用来存放tomcat运行需要加载的jar包 Logs:用来存放tomcat在运行过程中产生的日志文件 - 在windows环境中,控制台的输出日志在catalina.xxxx-xx-xx.log文件中 - 在linux环境中,控制台的输出日志在catalina.out文件中 #### Tomcat总体架构

总体架构
使用Container代表容器,Engine、Host、Context、Wrapper都是Container的子容器,Container可以维护子容器。backgroundProcess()方法针对后台处理,并且其基础抽象类(ContainerBase)确保在启动组件的同时,异步启动后台处理 #### Lifecycle生命周期

生命周期图
所有的容器中都存在start()、stop()等方法,因此抽象出Lifecycle接口,该接口中定义了相关的生命周期的方法。主要方法如下: - Init():初始化组件 - Start():启动组件 - Stop():停止组件 - Destroy():销毁组件

Lifecycle状态流程
#### Tomcat类加载机制

tomcat类加载机制
- 从缓存中加载 - 如果没有,从JVM的Bootstrap类加载器加载 - 如果没有,则从当前类加载器加载(WEB-INF/classes 、WEB-INF/lib的顺序) - 如果没有,则从父类加载器加载,由于父类加载器采用默认的委派模式,所以加载顺序为System、Common、Shared - 如果启用Java委派模式(delegate=true):缓存-Bootstrap-从父类(System、common、shared)-当前类加载器加载 #### Tomcat Coyote与Catalina - Catalina组件: - 包含所有的容器组件 - 通过松耦合方式集成Cotoye - 包含启动入口以及Shell - 使用Apache Digester解析XML配置文件并创建服务器

Catalina依赖关系图
Coyote组件: - Tomcat链接器 - 外部访问接口服务 - 建立链接,发送请求,并接收响应 - 封装底层网络通信协议,为Catalina提供接口,使之与请求协议及I/O方式解耦 - 将Socket输入转换为Request交由Catalina处理 - 提供Response对象将Catalina的处理结果写入输出流 - 此处的Request与Response与Servlet还未关联

Coyote与Catalina交互关系
#### Tomcat 服务器核⼼配置详解 > Tomcat作为服务器的配置,主要是 server.xml ⽂件的配置;server.xml中包含了 Servlet容器的相关配置,即 Catalina 的配置;Xml ⽂件的讲解主要是标签的使⽤ **主要标签结构如下:** ``` ``` **Server 标签:** ``` ... ``` **Service 标签:** ``` ... ``` **Executor 标签:** ``` ``` **Connector 标签:** Connector 标签⽤于创建链接器实例 默认情况下,server.xml 配置了两个链接器,⼀个⽀持HTTP协议,⼀个⽀持AJP协议 ⼤多数情况下,我们并不需要新增链接器配置,只是根据需要对已有链接器进⾏优化 ``` ``` 可以使⽤共享线程池 ``` ``` **Engine 标签:** Engine 表示 Servlet 引擎 ``` ... ``` **Host 标签:** Host 标签⽤于配置⼀个虚拟主机 ``` ... ``` **Context 标签:** Context 标签⽤于配置⼀个Web应⽤ ``` ``` #### 自定义Tomcat [自定义Tomcat Demo](https://gitee.com/orangezh/minicat) #### Tomcat 源码构建及分析 #### Tomcat 源码构建 **1、源码下载** 官网下载源码包 **2、源码导入前的准备工作** - 解压 tar.gz 压缩包,得到⽬录 apache-tomcat-8.5.50-src - 进⼊ apache-tomcat-8.5.50-src ⽬录,创建⼀个pom.xml⽂件,⽂件内容如下 ``` 4.0.0 org.apache.tomcat apache-tomcat-8.5.50-src Tomcat8.5 8.5 Tomcat8.5 java java org.apache.maven.plugins maven-compiler-plugin 3.1 UTF-8 11 11 org.easymock easymock 3.4 ant ant 1.7.0 wsdl4j wsdl4j 1.6.2 javax.xml jaxrpc 1.1 org.eclipse.jdt.core.compiler ecj 4.5.1 javax.xml.soap javax.xml.soap-api 1.4.0 ``` - 在 apache-tomcat-8.5.50-src ⽬录中创建 source ⽂件夹 - 将 conf、webapps ⽬录移动到刚刚创建的 source ⽂件夹中 **3、导⼊源码⼯程到IDE并进⾏配置** - 将源码⼯程导⼊到 IDEA 中 - 给 tomcat 的源码程序启动类 Bootstrap 配置 VM 参数,因为 tomcat 源码运⾏也需要加载配置⽂件等。 ``` -Dcatalina.home=/Users/yingdian/workspace/servers/apache-tomcat-8.5.50-src/source -Dcatalina.base=/Users/yingdian/workspace/servers/apache-tomcat-8.5.50-src/source -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=/Users/yingdian/workspace/servers/apachetomcat-8.5.50-src/source/conf/logging.properties ``` - 运⾏ Bootstrap 类的 main 函数,此时就启动了tomcat,启动时候会去加载所配置的 conf ⽬录下的server.xml等配置⽂件,所以访问8080端⼝即可,但此时我们会遇到的⼀个错误: > 原因是Jsp引擎Jasper没有被初始化,从⽽⽆法编译JSP,我们需要在tomcat的源码ContextConfig类中的configureStart⽅法中增加⼀⾏代码将 Jsp 引擎初始化 ```Java context.addServletContainerInitializer(new JasperInitializer(), null); ``` - 重启 Tomcat,正常访问即可。⾄此,Tomcat 源码构建完毕。 #### Tomcat 核心源码分析 Tomcat启动流程和Tomcat请求处理流程

Tomcat的启动流程

Tomcat请求处理流程

Tomcat请求执行流程
具体代码流程请自行在源码中进行调试 #### Nginx #### Nginx 概述与安装 **1、Nginx 到底是什么?** Nginx 是⼀个⾼性能的HTTP和反向代理web服务器,核⼼特点是占有内存少,并发能⼒强 **2、Nginx应用场景** - Http服务器(Web服务器) ``` 性能⾮常⾼,⾮常注重效率,能够经受⾼负载的考验。 ⽀持50000个并发连接数,不仅如此,CPU和内存的占⽤也⾮常的低,10000个没有活动的连接才占⽤2.5M的内存。 ``` - 反向代理服务器 ``` 浏览器客户端发送请求到反向代理服务器(⽐如Nginx),由反向代理服务器选择原始服务器提供服务获取结果响应,最终再返回给客户端浏览器 ``` - 负载均衡服务器 ``` 负载均衡,当⼀个请求到来的时候,Nginx反向代理服务器根据请求去找到⼀个原始服务器来处理当前请求,那么这叫做反向代理。那么,如果⽬标服务器有多台,找哪⼀个⽬标服务器来处理当前请求呢,这样⼀个寻找确定的过程就叫做负载均衡。 ⽣活中也有很多这样的例⼦,⽐如,我们去银⾏,可以处理业务的窗⼝有多个,那么我们会被分配到哪个窗⼝,这样的⼀个过程就叫做负载均衡。 ``` - 动静分离 ``` 动静分离就是讲动态资源和静态资源的请求处理分配到不同的服务器上,⽐较经典的组合就是Nginx+Tomcat架构(Nginx处理静态资源请求,Tomcat处理动态资源请求) ``` **3、Nginx 的特点** - 跨平台:Nginx可以在⼤多数类unix操作系统上编译运⾏,⽽且也有windows版本 - Nginx的上⼿⾮常容易,配置也⽐较简单 - ⾼并发,性能好 - 稳定性也特别好,宕机概率很低 **4、Nginx的安装** - 上传nginx安装包到linux服务器,nginx安装包(.tar⽂件)下载地址:http://nginx.org - 安装Nginx依赖,pcre、openssl、gcc、zlib(推荐使⽤yum源⾃动安装) ``` yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel ``` - 解包Nginx软件包 ``` tar -xvf nginx-1.17.8.tar ```` - 进⼊解压之后的⽬录 nginx-1.17.8 ``` cd nginx-1.17.8 ``` - 命令⾏执⾏./configure - 命令⾏执⾏ make - 命令⾏执⾏ make install,完毕之后在/usr/local/下会产⽣⼀个nginx⽬录 ``` 进⼊sbin⽬录中,执⾏启动nginx命令 cd nginx/sbin ./nginx ``` **5、Nginx主要命令** - ./nginx 启动nginx - ./nginx -s stop 终⽌nginx(当然也可以找到nginx进程号,然后使⽤kill -9 杀掉nginx进程) - ./nginx -s reload (重新加载nginx.conf配置⽂件) #### Nginx配置解析 Nginx的核⼼配置⽂件conf/nginx.conf包含三块内容:全局块、events块、http块 **1、全局块** 从配置⽂件开始到events块之间的内容,此处的配置影响nginx服务器整体的运⾏,⽐如worker进程的数量、错误⽇志的位置等

Nginx 全局块
**2、events块** events块主要影响nginx服务器与⽤户的⽹络连接,⽐如worker_connections 1024,标识每个workderprocess⽀持的最⼤连接数为1024

Nginx events块
**3、http块** http块是配置最频繁的部分,虚拟主机的配置,监听端⼝的配置,请求转发、反向代理、负载均衡等

Nginx http块
#### Nginx 反向代理 主要就是修改location,这⾥的nginx中server/location就好⽐tomcat中的Host/Context location 语法如下: ``` location [=|~|~*|^~] /uri/ { … } ``` 在nginx配置⽂件中,location主要有这⼏种形式: ``` 1)正则匹配 location ~ /lagou { } 2)不区分⼤⼩写的正则匹配 location ~* /lagou { } 3)匹配路径的前缀 location ^~ /lagou { } 4)精确匹配 location = /lagou { } 5)普通路径前缀匹配 location /lagou { } 优先级 4 > 3 > 2 > 1 > 5 ```

Nginx 反向代理配置实例
#### Nginx 负载均衡策略 - 轮询 默认策略,每个请求按时间顺序逐⼀分配到不同的服务器,如果某⼀个服务器下线,能⾃动剔除 ``` upstream lagouServer{ server 111.229.248.243:8080; server 111.229.248.243:8082; } location /abc { proxy_pass http://lagouServer/; } ``` - weight weight代表权重,默认每⼀个负载的服务器都为1,权重越⾼那么被分配的请求越多(⽤于服务器性能不均衡的场景) ``` upstream lagouServer{ server 111.229.248.243:8080 weight=1; server 111.229.248.243:8082 weight=2; } ``` - ip_hash 每个请求按照ip的hash结果分配,每⼀个客户端的请求会固定分配到同⼀个⽬标服务器处理,可以解决session问题 ``` upstream lagouServer{ ip_hash; server 111.229.248.243:8080; server 111.229.248.243:8082; } ```