1. Tomcat的安装和配置环境变量
安装
到官网下载需要的Tomcat版本的压缩包即可
注意:Tomcat 9 对应的JRE必须>=JRE 8;Tomcat 8 7同理
然后解压到指定的文件夹;目录名为:“apache-tomcat-[version]”
(WINDOWS和Linux)一样的效果
配置环境变量
配置JRE环境变量,变量名为:CATALINA_HOME(required)和CATALINA_BASE(optional)
前者必须,后者可选
CATALINA_HOME:tomcat根目录的路径
CATALINA_BASE:用于Advanced Configuration - Multiple Tomcat Instances 多个Tomcat实例
==================================================
Advanced Configuration - Multiple Tomcat Instances
==================================================
In many circumstances, it is desirable to have a single copy of a Tomcat
binary distribution shared among multiple users on the same server. To make
this possible, you can set the CATALINA_BASE environment variable to the
directory that contains the files for your 'personal' Tomcat instance.
When running with a separate CATALINA_HOME and CATALINA_BASE, the files
and directories are split as following:
In CATALINA_BASE:
* bin - Only the following files:
* setenv.sh (*nix) or setenv.bat (Windows),
* tomcat-juli.jar
The setenv scripts were described above. The tomcat-juli library
is documented in the Logging chapter in the User Guide.
* conf - Server configuration files (including server.xml)
* lib - Libraries and classes, as explained below
* logs - Log and output files
* webapps - Automatically loaded web applications
* work - Temporary working directories for web applications
* temp - Directory used by the JVM for temporary files (java.io.tmpdir)
In CATALINA_HOME:
* bin - Startup and shutdown scripts
The following files will be used only if they are absent in
CATALINA_BASE/bin:
setenv.sh (*nix), setenv.bat (Windows), tomcat-juli.jar
* lib - Libraries and classes, as explained below
* endorsed - Libraries that override standard "Endorsed Standards"
libraries provided by JRE. See Classloading documentation
in the User Guide for details.
This is only supported for Java <= 8.
By default this "endorsed" directory is absent.
Tomcat官方说:CATALINA_HOME相当于Tomcat的安装目录,而CATALINA_BASE则是Tomcat的工作目录
所以CATALINA_BASE的存在就是为了实现多个Tomcat实例可以同时运行(而不采取copy多个Tomcat的副本的方法)
每个Tomcat实例的私有文件夹:(conf配置文件,logs日志,webapps自己的app目录,work自己的临时工作区间,temp存放JVM的临时文件)
- conf - Server configuration files (including server.xml)
- logs - Log and output files
- webapps - Automatically loaded web applications
- work - Temporary working directories for web applications
- temp - Directory used by the JVM for temporary files (java.io.tmpdir)
2. 同时运行多个Tomcat实例的正确姿势
-
找到Tomcat的安装目录,然后把Tomcat的私有工作目录全部拷贝到新的(每个Tomcat实例文件夹下)
私有工作目录如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0RoI3Mzj-1570847392385)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569894952277.png)]
拷贝到Tomcat实例文件夹下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RDZepeP4-1570847392387)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569894978489.png)]
创建bin目录:bin目录下创建新的startup.bat启动脚本,和把tomcat-juli.jar拷贝过来
startup.bat简易写法如下:(主要是配置CATALINA_HOME和CATALINA_BASE)
set "TITLE=Tomcat9-1"
cd..
set "CATALINA_BASE=%cd%"
set "CATALINA_HOME=D:\my_heart_note\tomcat\apache-tomcat-9.0.26"
set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat"
CALL %EXECUTABLE% start
set "TITLE=Tomcat9-2"
cd..
set "CATALINA_BASE=%cd%"
set "CATALINA_HOME=D:\my_heart_note\tomcat\apache-tomcat-9.0.26"
set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat"
CALL %EXECUTABLE% start
TITLE是设置Tomcat窗口名字
还要修改端口号:三个端口号(Shutdown的,connector的,AJP的)
引发的各个问题解决
-
引发了窗口的乱码问题:因为我们窗口名字默认为Tomcat,但是到了我们的多个Tomcat实例的时候,窗口名字就不默认了。所以手动设置TITLE;
-
然后我想要实现的是修改Tomcat窗口的字符集。而不是修改Tomcat打印日志的字符集,去适应Windows的gbk;而是适应我们的UTF-8
-
所以决定修改Windows对于某个窗口的字符集。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B8Qnm7RC-1570847392388)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569895509349.png)]
十进制修改为65001
左侧Console下面的项为窗口名字,如:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzSc1SDT-1570847392389)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569895545382.png)]
效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8xGEAMv-1570847392391)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569895773026.png)]
厉害吧!
上面的效果是我修改了每个Tomcat实例中的webapp里的ROOT文件夹里的index.jsp首页的title
原理是:
在每个Tomcat实例中的conf配置文件中,web.xml配置了一些关于应用处理的信息(Servlet相关配置)
<servlet>
<!--DefaultServlet-->
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<!--JSPServlet-->
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
mapping
<servlet-mapping>
<servlet-name>default</servlet-name>
<!--处理每个 根 请求/-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
<!-- ==================== Default Welcome File List ===================== -->
<!-- When a request URI refers to a directory, the default servlet looks -->
<!-- for a "welcome file" within that directory and, if present, to the -->
<!-- corresponding resource URI for display. -->
<!-- If no welcome files are present, the default servlet either serves a -->
<!-- directory listing (see default servlet configuration on how to -->
<!-- customize) or returns a 404 status, depending on the value of the -->
<!-- listings setting. -->
<!-- -->
<!-- If you define welcome files in your own application's web.xml -->
<!-- deployment descriptor, that list *replaces* the list configured -->
<!-- here, so be sure to include any of the default values that you wish -->
<!-- to use within your application. -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
意思是如果没有精准匹配到的Servlet,就会走DefaultServlet,就会调用到welcome-file-list中的网页
3. Tomcat server.xml中体现出来的架构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OklaP7Fh-1570847392393)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569909095997.png)]
<Server port="9005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
//提供的一种服务Service:名字为Catalina
<Service name="Catalina">
//这个服务下有多个连接器Connector,可以连接这个服务
<Connector port="9090" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="9009" protocol="AJP/1.3" redirectPort="8443" />
//这个Service的底层由Engine支持,Engine里由主机或者虚拟主机构成
(提供应用webapp支持)
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
//Host主机(或者在这里配置虚拟主机也可以,name为主机名,appBase为webapp目录的路径
<Host name="localhost" appBase="webapps"
//unpackWARs:是否解压War包;//autoDeploy是否自动部署(热部署,一放上去就自动部署,发布)
unpackWARs="true" autoDeploy="true">
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
4. 配置虚拟目录
虚拟目录:映射到别的文件夹中
比如我们想127.0.0.1/blog/index 和 127.0.0.1/sky_chou/index
表现是:两个webapp目录blog和sky_chou,然后webapp里有各自的app应用
而不是把所有的app都部署在同一个webapp下(同时还能实现修改域名的功能)
原理是把域名请求 映射到 docBase配置的映射路径下。
(缺点:要重启服务器)
第一种方法 - 修改server.xml
在HOST标签里,添加<Context path="请求访问路径(基于上一级的Host) docBase=“映射的webapp路径” />
第二种方法 - 修改conf文件夹里的虚拟主机文件夹(里的*.xml)
原理:因为Tomcat会为每个服务 在conf文件夹中创建这个服务的配置文件
如:Service里的Catalina服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Vz0GijA-1570847392394)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569930181265.png)]
conf里有Catalina文件夹
里面有Host主机的文件夹:localhost
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IzSrgW1Z-1570847392396)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569930215980.png)]
我们想要完成热部署(不重启就能实现虚拟主机的映射),创建一个*.xml ( *就是path属性)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uhNVzwhT-1570847392397)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569930236394.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WYBNBrMM-1570847392399)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569930281658.png)]
调用的姿势:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ErMCY0D9-1570847392400)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569930297748.png)]
注意:这个blog目录并不在原来的webapps里,而是在自己配置的目录中
5. 配置虚拟主机
只是用来模拟一台主机上,可以通过多个域名去访问不同的webapp应用(从而达到区分)
但是实际上(域名是需要DNS解析服务的)
实际上我们可以实现:通过注册自己的多个域名,然后在Tomcat上配置对应的域名 和虚拟主机
方法:添加HOST
6. Tomcat的目录结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HIa6dx7c-1570847392402)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569978452956.png)]
bin:是很多脚本(包括windows,linux适用的)和一些jar包,以及exe可执行的tomcat应用程序
conf:是配置文件所存放的地方:有server.xml(Tomcat服务器的配置),web.xml(默认的web应用配置信息,比如一些默认的Servlet,以及JSPServlet)。
tomcat-users.xml (Tomcat用户权限管理)
lib目录:存放了很多加载所需要的jar包
logs:日志存放的地方
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LyVhzUKU-1570847392402)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569978781635.png)]
catalina:是访问了这个catalina服务的日志记录。(包含了详细的日志信息和报错信息)
localhost_access_log : 记录了访问这个主机的所有请求以及HTTP状态码等信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oM6agjti-1570847392404)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1569978884347.png)]
7. Tomcat源码部署
首先下载源码,解压到某个文件夹
然后以此,构建Maven项目。通过POM文件引入启动Tomcat仍需要的Jar包
以及相关文件夹的移动
然后创建Main启动类为Bootstrap
配置JVM启动参数
修改源码(初始化JSP解析器)
在ContextConfig类中的configureStart方法里,在webConfig()后面添加,初始化JSP解析器
protected synchronized void configureStart() {
// Called from StandardContext.start()
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.start"));
}
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.xmlSettings",
context.getName(),
Boolean.valueOf(context.getXmlValidation()),
Boolean.valueOf(context.getXmlNamespaceAware())));
}
webConfig();
//初始化JSP解析器
context.addServletContainerInitializer(new JasperInitializer(),null);
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>apache-tomcat-9.0-src</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<finalName>Tomcat9.0</finalName>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxrpc</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.6.1</version>
</dependency>
</dependencies>
</project>
注意:Tomcat 9 里面的JDTCompiler源码里有CompilerOptions.VERSION_1_9这种高于1.9版本的,如果没有安装这些版本的话,识别不了
Tomcat9中有部分代码使用CompilerOptions.VERSION_1_9
来判断java虚拟机版本,里面涉及1.9版本的jvm,我机器上识别不了。
在java/org/apache/jasper/compiler/JDTCompiler.java
类中把上述代码找出,删除即可,总共有3处。
参考链接:https://blog.csdn.net/linxdcn/article/details/72811928
8. 为什么用源码启动Tomcat 9的时候,控制台输出的中文全部都是乱码
如何发现编码是哪种乱码所导致的呢?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S4OBaPFd-1570847392405)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1570003119787.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uDI0VcEm-1570847392406)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1570003125879.png)]
一步步debug的过程找寻原因:
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HOmcuZVo-1570847392407)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1570002765099.png)]
-
找到这个国际化配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TNKIMBcw-1570847392408)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1570002802066.png)]
-
继续debug下去,sm.getString()底层调用的ResourceBundle实现类去读取的Properties文件
我们看下面的文档注释(知道ResourceBundle只会按ISO8859-1去读取,要想读取非ISO8859-1编码的内容,需要以UNICODE形式去填写properties文件。
* <p>
* The implementation of a {
@code PropertyResourceBundle} subclass must be
* thread-safe if it's simultaneously used by multiple threads. The default
* implementations of the non-abstract methods in this class are thread-safe.
*
* <p>
* <strong>Note:</strong> PropertyResourceBundle can be constructed either
* from an InputStream or a Reader, which represents a property file.
* Constructing a PropertyResourceBundle instance from an InputStream requires
* that the input stream be encoded in ISO-8859-1. In that case, characters
* that cannot be represented in ISO-8859-1 encoding must be represented by Unicode Escapes
* as defined in section 3.3 of
* <cite>The Java™ Language Specification</cite>
* whereas the other constructor which takes a Reader does not have that limitation.
-
效果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T70Jpttl-1570847392410)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1570002943894.png)]
9. Tomcat整体架构
9.1 Http工作原理
客户端->服务端(在浏览器输入url)
用的是HTTP协议
所以在传输层先通过TCP协议与服务器建立三次握手
连接完成后,客户端再封装请求数据为HTTP报文格式
然后从高层往底层封装,传输到服务器那边去。
服务器拆包,解析HTTP报文。
然后服务端调用服务器软件程序去处理这个HTTP请求,(比如Tomcat,调用不同的Servlet去处理请求)
然后把响应数据封装成HTTP报文,再传回去。
9.2 Servlet容器工作流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YQfaHx7A-1570847392412)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1570004188277.png)]
为了解耦,HTTP服务器并不直接调用Servlet去处理每个用户请求。而是交给了更为专业的Servlet容器(Tomcat)去处理请求。
首先,把用户请求的HTTP报文,解析封装成一个ServletRequest对象,然后传给Servlet容器,根据这个对象里的url,通过事先在(web.xml或者注解上定义的映射关系),找到对应的Servlet进行处理。(这里利用了反射技术,动态地创建相应的Servlet)
把ServletRequest传给Servlet之后,Servlet再调用核心方法Service()进行逻辑的处理,根据不同的请求格式,如POST,GET分别处理(doPost(),doGet() )
然后再把响应信息封装到servletResponse对象中,最后通过HTTP服务的处理,加上响应头,封装成HTTP报文传回给客户端。
9.3 Tomcat整体架构-两大组件
从宏观上来看,且从数据流转的角度来看,Tomcat最外面的两个核心组件就是Connector连接器和Container容器
-
Connector:负责处理Socket请求,然后把请求数据封装成Request对象;还把Response中的响应数据封装成HTTP报文返回给客户端。(相当于HTTP服务器,不处理具体的逻辑业务,只负责转发和接收)
-
Container容器(Servlet容器):主要是拿到Connector传过来的Request对象,通过解析里面的url,
找到对应的Servlet进行处理。先检查这个Servlet是否已经加载(如果加载过了,就直接用),没有加载过的话,就通过反射进行创建);所以从这里可以看得出来Servlet是单例的。
然后Servlet调用Service()方法进行处理,把响应数据封装成Response对象传回给Connector
(中间还要经过catalina容器)
(Catalina容器接收的是ServletRequest对象)
(所以在Connector组件和Catalina之间还有好多组件)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TLRrZZWo-1570847392413)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1570005479858.png)]
解耦!
9.4 Connector组件的IO模型
应用层协议
HTTP/1.1 | AJP(Apache服务器) | HTTP/2.0 |
---|---|---|
传输层
NIO(非阻塞IO) | NIO2.0(异步IO,JDK7.0后支持) | APR(C/C++网络通讯库) |
---|---|---|
“阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解,提供一个从分布式系统角度的回答。
1.同步与异步
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。
换句话说,就是由调用者主动等待这个调用的结果。
而异步则是相反,调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用*发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。
典型的异步编程模型比如Node.js
举个通俗的例子:
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。
\2. 阻塞与非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
还是上面的例子,
你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
如果是关心阻塞 IO/ 异步 IO, 参考 Unix Network Programming View Book
–
还是2014年写的以解释概念为主,主要是同步异步 阻塞和非阻塞会被用在不同层面上,可能会有不准确的地方,并没有针对 阻塞 IO/ 异步 IO 等进行讨论,大家可以后续看看这两个回答:
9.5 Connector重要组件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sGXMV2JV-1570847392414)(D:\my_heart_note\tomcat\笔记\Tomcat.assets\1570009028902.png)]
Endpoint:Coyote的通信断点,是具体Socket接收和发送处理器,是对传输层的抽象,因此,Endpoint是用来实现TCP/IP协议的。
Tomcat没有Endpoint接口,而是定义了AbstractEndpoint,里面定义了两个内部类:Acceptor和SocketProcessor
Acceptor用来监听Socket请求,SocketProcessor用来处理接收到的Socket请求,它实现了Runnable接口,
/**
* Thread used to accept new connections and pass them to worker threads.
*/
protected Acceptor<U> acceptor;
在run()方法里调用协议处理组件Processor进行处理。为了提高处理能力,SocketProcessor被提交到线程池执行,这个线程池叫Executor。Tomcat扩展了原生的Java线程池。java.concurrent包下的。
protected abstract SocketProcessorBase<S> createSocketProcessor(
SocketWrapperBase<S> socketWrapper, SocketEvent event);
这是抽象类SocketProcessorBase
@Override
public final void run() {
synchronized (socketWrapper) {
if (socketWrapper.isClosed()) {
return;
}
doRun();
}
}
这是NIOEndpoint的内部类SocketProcessor
// ---------------------------------------------- SocketProcessor Inner Class
/**
* This class is the equivalent of the Worker, but will simply use in an
* external Executor thread pool.
*/
protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
super(socketWrapper, event);
}
@Override
protected void doRun() {
NioChannel socket = socketWrapper.getSocket();
SelectionKey key = socket.getIOChannel().keyFor(socket.getSocketWrapper().getPoller().getSelector());
Poller poller = NioEndpoint.this.poller;
if (poller == null) {
socketWrapper.close();
return;
}
try {
int handshake = -1;
try {
if (key != null) {
if (socket.isHandshakeComplete()) {
// No TLS handshaking required. Let the handler
// process this socket / event combination.
handshake = 0;
} else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
event == SocketEvent.ERROR) {
// Unable to complete the TLS handshake. Treat it as
// if the handshake failed.
handshake = -1;
} else {
handshake = socket.handshake(key.isReadable(), key.isWritable());
// The handshake process reads/writes from/to the
// socket. status may therefore be OPEN_WRITE once
// the handshake completes. However, the handshake
// happens when the socket is opened so the status
// must always be OPEN_READ after it completes. It
// is OK to always set this as it is only used if
// the handshake completes.
event = SocketEvent.OPEN_READ;
}
}
} catch (IOException x) {
handshake = -1;
if (log.isDebugEnabled()) log.debug("Error during SSL handshake",x);
} catch (CancelledKeyException ckx) {
handshake = -1;
}
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
if (event == null) {
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {