浅析tomcat原理
浅析tomcat原理
上上个星期,看了一下how tomcat works这本书,今天捡起来看一会,发现忘得有点快,特地写点东西,加深一下记忆。因为书讲的是tomcat4,5的内容,比较旧了,所以和最新的tomcat的差距还是有点大的。而且还没看完,以后再补充吧。
而java实现web最简单的方式就是对socket和serversocket的封装。也是早期tomcat的实现方式。
计算机网络之间的通信是基于端口之间通信,对于服务器,从端口读取数据,也就是inputStream, 往端口写数据,也就是outputStream。
serverSocket = new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();
HttpServletRequest是请求,也就是客户端往服务器端口发送消息,服务器从端口读取数据,所以HttpServletRequest封装了上面的inputStream。 同理HttpServletResponse封装了上面的outputStream。
ServerSocket 监听本机端口, 我们可以去浏览器输入: http://127.0.0.1:8080 然后后台读取程序中读取一个叫做index.html的文件(FileInputStream->bytes),output.write(bytes, 0, lenth). 将文件内容输出到端口,网页中就会显示内容了。是不是有点熟悉。当然tomcat肯定不是这么简单的实现,但是最基本的原理就是如此。
当然这个是静态资源,如果是动态servlet的处理,大致是通过类加载器,加载进内存,反射得到对象。然后根据request请求信息,判断是请求静态文件还是动态servlet,然后执行servlet的service(request, response)方法;
Connector 连接器
直接跳到连接器,有点太快了,书中还有很多小细节,门面设计模式, response中getWriter方法就是封装了outputStream… 推荐去看一下。
连接器,顾名思义,就是用来处理连接的。
private int port = 8080;private ServerSocket serverSocket = null;
这是我直接在tomcat4源码中HttpConnector拷贝过来的两个参数,然后再来看看这个类的一个重要方法。
public void run() {// Loop until we receive a shutdown commandwhile (!stopped) {Socket socket = null;try {socket = serverSocket.accept();...} catch () {...}HttpProcessor processor = createProcessor();...processor.assign(socket);...}...}
一个循环,等待连接的到来,得到一个socket对象, 然后创建一个处理器,去处理这个socket的请求。然后继续等待下一个连接accept()。
当然如果tomcat这样顺序进行,那肯定是不行的,所以一个连接器有一个processor(处理器)的对象池。而每个处理器实现了runnable接口,在创建的时候就启动了线程,然后阻塞自己,直到自己调用了assign() 方法,就是上面的那个方法,然后执行处理request的方法,处理servlet还是静态资源。完成后继续阻塞自己。
所以tomcat的connector有多个processor,请求来了调用一个processor去执行,而本身不需要等待这个processor完成,继续接收下一个请求。是一种异步实现的感觉。这样可以处理多个请求,而无需阻塞。这也是早期bio的解决方案。现在的解决方案应该是nio了。
final class HttpProcessor implements Lifecycle, Runnable {public void run() {while (!stopped) {Socket socket = await();if (socket == null)continue;try {process(socket);} catch (Throwable t) {log("process.invoke", t);}connector.recycle(this);}synchronized (threadSync) {threadSync.notifyAll();}}
}
就是在await()方法阻塞,然后释放,执行process完成,循环继续阻塞。
processor 处理器
首先看看这个processor做了什么吧。
- 创建HttpServletRequest, HttpServletResponse对象。
- 解析连接
- 解析请求
- 解析头部, 给request.setHeader。
- 隐式调用了servlet的service方法。 虽然从这里开始,但并不是直接调用。
具体是怎么解析的请求,我没有深入去了解,大概就是拆分字符串,截取之类的吧,还是看看这个类最重要的方法。process() 方法。
private void process(Socket socket) {...try {input = new SocketInputStream(socket.getInputStream(),connector.getBufferSize());} catch (Exception e) {...}keepAlive = true;while (!stopped && ok && keepAlive) {finishResponse = true;try {request.setStream(input);request.setResponse(response);output = socket.getOutputStream();response.setStream(output);response.setRequest(request);((HttpServletResponse) response.getResponse()).setHeader("Server", SERVER_INFO);} catch (Exception e) {...}...//这里解析请求,给丰富request和response。try {...if (ok) {// 重点。 画上!!!connector.getContainer().invoke(request, response);}} catch (ServletException e) {...}}
大致就是上面,可以看出,request和response封装了输入输出流,然后解析请求, 调用了container的invoke(request, response)方法。所以我们希望见到的service方法就藏在这里面了。
Container 容器
这个可以说是tomat中最被人熟知的东西之一。tomcat4中有四大容器,以我的理解简单介绍一下, 可能有点不对。
- Engine 引擎, 启动一个tomcat服务,也就是启动一个引擎
- host 虚拟主机, 一个Engine启动,下面项目都会启动,localhost:8080/work1,work2
- context 上下文, 一个项目对应一个上下文,通过map映射到不同的servlet。
- wrapper 包装器, 一个wrapper对应一个servlet。
上面的四个都实现了Container接口,就先来谈谈wrapper吧。connector中有个方法,setContainer(Container container); 如果一个wrapper被一个connector绑定,那么回到上面画重点的方法。
connector.getContainer().invoke(request, response);
wrapper 包装器
所以点进wrapper类中找寻这个方法,发现没有,这个方法的实现在他的父类ContainerBase中有实现。
public void invoke(Request request, Response response) throws IOException, ServletException {pipeline.invoke(request, response);}
出现了一个新的东西,叫做管道(pipeline)。点下去,会发现很麻烦,先来捋一捋Container中这些的关系。
container 中包含了一个pipeline, 而pipeline执行invoke方法是创建了一个pipelineContext对象,并执行invokeNext方法。 然后pipelineContext对象就会去执行Valve的invoke方法。当然,执行方式有点像递归,一直往下,直到执行basicvalve的invoke方法,这个方法中会隐式执行service方法,然后再回去执行上一个valve。看看basicvalve的invoke方法。
public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException {...StandardWrapper wrapper = (StandardWrapper) getContainer();ServletRequest sreq = request.getRequest();ServletResponse sres = response.getResponse();Servlet servlet = null;HttpServletRequest hreq = null;if (sreq instanceof HttpServletRequest)hreq = (HttpServletRequest) sreq;HttpServletResponse hres = null;if (sres instanceof HttpServletResponse)hres = (HttpServletResponse) sres;...try {if (!unavailable) {// 划重点。。。servlet = wrapper.allocate();}} catch (ServletException e) {...}ApplicationFilterChain filterChain = createFilterChain(request, servlet);try {...if ((servlet != null) && (filterChain != null)) {// 划重点2。。。filterChain.doFilter(sreq, sres);}...}}
通过getContainer得到wrapper, wrapper.allocate(). 这个方法中通过反射之类的得到了servlet对象,返回。然后创建一个过滤器链, 调用doFilter方法传递request和response。我们再看看doFilter方法。
if ((request instanceof HttpServletRequest) &&(response instanceof HttpServletResponse)) {servlet.service((HttpServletRequest) request, (HttpServletResponse) response);} else {servlet.service(request, response);}
这个方法中,终于找到了调用service方法了。当然上面说的都是wrapper。一个wrapper只对应一个servlet,但是一个项目肯定有多个servlet,那么这就涉及到我们熟悉的Context(上下文)了。
context 上下文
我们再回到connector,connector.setContainer(context). 如果connector设置的容器是上下文。再来分析一下。想起我们之前的那个方法。
connector.getContainer().invoke(request, response);
context的invoke和wrapper一样,pipeline.invoke, 然后同样是pipelineContext.invokeNext。 那么不一样的地方在哪里呢? 那就是basicvalve, 我们称为contextValve。 我们看看contextValve的invoke方法。
public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException {...Context context = (Context) getContainer();Wrapper wrapper = null;try {wrapper = (Wrapper) context.map(request, true);} catch (IllegalArgumentException e) {badRequest(requestURI, (HttpServletResponse) response.getResponse());return;}if (wrapper == null) {notFound((HttpServletResponse) response.getResponse());return;}response.setContext(context);wrapper.invoke(request, response);}
相当简洁, 就是context从request中读取到类似 /IndexServlet, 然后就会去map中搜索,找到处理这个IndexServlet的servlet。也就是wrapper,执行wrapper的invoke,又回到了上面wrapper的方法,记得上面的说的吗,一个wrapper只处理一个servlet。而一个上下文对应多个servlet。 而这里的map就是我们经常写的xml映射关系。
<servlet-mapping> <servlet-name>IndexServlet</servlet-name> <url-pattern>/IndexServlet</url-pattern> </servlet-mapping>
当然这个map有一个专门的类ContextMapper, 这个就不深究如何映射的。还有上面类加载,反射servlet的方法,其实也有一个专门的接口叫Loader。我也没看太懂。
然后至于host和engine的实现,没看完,看看以后看完了有时间再来补上。原理应该差不多。
浅析tomcat原理相关推荐
- Tomcat 原理篇
TOMCAT 原理篇 一.Tomcat 组成(Tomcat 由以下组件组成) 1.server a) Server是一个Catalina Servlet容器: b) Server 可以包含一个或多个s ...
- 浅析bootstrap原理及优缺点
网格系统的实现原理,是通过定义容器大小,平分12份(也有平分成24份或32份,但12份是最常见的),再调整内外边距,最后结合媒体查询,就制作出了强大的响应式网格系统 网格系统的实现原理,是通过定义容器 ...
- IDEA/Tomcat 原理:如何将 Tomcat 集成到 IDEA?实现由传统部署延伸到热部署 Java EE 项目?
文章目录 前言 一.将 Tomcat 集成到 IDEA 中 1.1.进入 IDEA Tomcat 配置页面 1.2.配置并集成 Tomcat 1.3.配置完成说明 1.4.集成检查 1.4.1.检查主 ...
- tomcat原理及调优
TOMCAT原理详解及请求过程 Tomcat: Tomcat是一个JSP/Servlet容器.其作为Servlet容器,有三种工作模式:独立的Servlet容器.进程内的Servlet容器和进程外的S ...
- Tomcat原理和源码分析
Tomcat是什么? 首先看下官网的解释说明(看不懂的可以翻译一下),从第一句Tomcat是Java Servlet,JavaServer页,Java表达式语言和Java的WebSocket技术的一个 ...
- tomcat原理简要分析,java
tomcat原理 tomcat位置 tomcat实际上是部署在服务器上的: tomcat作用 tomcat服务器是一个Servlet和JSP容器,它响应HTML页面的访问请求. 实际上Tomcat是A ...
- Tomcat原理系列之四:Tomat如何启动spring(加载web.xml)
Tomcat原理系列之四:Tomat如何启动spring 熟悉的web.xml ContextLoaderListener Tomcat的初始化StandardContext.startInterna ...
- 手把手带你学会Odoo OWL组件开发(5):浅析OWL原理
[本系列内容直达:] 手把手带你学习Odoo OWL组件开发(1):认识 OWL 手把手带你学会Odoo OWL组件开发(2):OWL的使用 手把手带你学会Odoo OWL组件开发(3):核心内容指南 ...
- IDEA部署tomcat原理小解
IDEA部署tomcat原理小解 最近看到一个视频对IDEA部署启动tomcat有了新的理解.下面和大家分享一下. 1.tomcat部署项目的三种方式 在纯tomcat环境下,不使用开发工具tom ...
- 总结:SpringBoot内嵌Tomcat原理
一.介绍 一般我们启动web服务都需要单独的去安装tomcat,而Springboot自身却直接整合了Tomcat,什么原理呢? 二.原理 SpringBoot应用只需要引入spring-boot-s ...
最新文章
- R语言使用ggplot2包使用geom_boxplot函数绘制基础分组箱图(输出多个分组、色彩配置、添加数据点)实战
- how Lordec maps the long reads to DeBruijn Graph
- pm2常用的命令用法介绍
- 【必备】jQuery性能优化的38个建议
- java密码学原型算法_java密码学原型算法实现——双线性对.pdf
- 在Eclipse中有效使用JUnit
- 深度学习之卷积神经网络 LeNet
- linux的常用操作——read函数和write函数
- Mybatisplus代码生成 之SpringBoot适配MYSQL和ORACLE
- greenplum 查询出来的数字加减日期_常用SQL系列之(八):列值累计、占比、平均值以及日期运算等
- MyApplicationToast工具类
- 终于研究出如何设置新版paypal付款时汇率损失方的问题了
- Java:JSON解析工具-org.json
- elasticsearch 之index API
- LINUX安装TensorRT及特别注意事项
- 计算机网络信息安全等级保护(等保)
- 「构建企业级推荐系统系列」推荐系统的人工调控
- Python修改图片像素颜色值
- 谈谈QUIC协议原理
- MySQL学习记录(11.9更新)
热门文章
- 哈理工OJ 1391 Orz odd(规律【没证出来】)
- ajaxsubmit php上传文件,使用ajaxSubmit方法实现多文件上传(异步)
- 解决ajaxSubmit无法传递自动回填和下拉框的数据
- 【RPA】PDF生成器FPDF(Python篇)
- 【算法精解】计数排序
- Tushare最好用的金融数据接口之一
- SQLyog 注册码(包含企业版注册码)
- python美女源代码_python程序员爬取百套美女写真集,同样是爬虫,他为何如此突出...
- 用科比一生的数据,回顾他的球场传奇
- idea中 Java xml注释缩进问题 解决方案