浅析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原理相关推荐

  1. Tomcat 原理篇

    TOMCAT 原理篇 一.Tomcat 组成(Tomcat 由以下组件组成) 1.server a) Server是一个Catalina Servlet容器: b) Server 可以包含一个或多个s ...

  2. 浅析bootstrap原理及优缺点

    网格系统的实现原理,是通过定义容器大小,平分12份(也有平分成24份或32份,但12份是最常见的),再调整内外边距,最后结合媒体查询,就制作出了强大的响应式网格系统 网格系统的实现原理,是通过定义容器 ...

  3. IDEA/Tomcat 原理:如何将 Tomcat 集成到 IDEA?实现由传统部署延伸到热部署 Java EE 项目?

    文章目录 前言 一.将 Tomcat 集成到 IDEA 中 1.1.进入 IDEA Tomcat 配置页面 1.2.配置并集成 Tomcat 1.3.配置完成说明 1.4.集成检查 1.4.1.检查主 ...

  4. tomcat原理及调优

    TOMCAT原理详解及请求过程 Tomcat: Tomcat是一个JSP/Servlet容器.其作为Servlet容器,有三种工作模式:独立的Servlet容器.进程内的Servlet容器和进程外的S ...

  5. Tomcat原理和源码分析

    Tomcat是什么? 首先看下官网的解释说明(看不懂的可以翻译一下),从第一句Tomcat是Java Servlet,JavaServer页,Java表达式语言和Java的WebSocket技术的一个 ...

  6. tomcat原理简要分析,java

    tomcat原理 tomcat位置 tomcat实际上是部署在服务器上的: tomcat作用 tomcat服务器是一个Servlet和JSP容器,它响应HTML页面的访问请求. 实际上Tomcat是A ...

  7. Tomcat原理系列之四:Tomat如何启动spring(加载web.xml)

    Tomcat原理系列之四:Tomat如何启动spring 熟悉的web.xml ContextLoaderListener Tomcat的初始化StandardContext.startInterna ...

  8. 手把手带你学会Odoo OWL组件开发(5):浅析OWL原理

    [本系列内容直达:] 手把手带你学习Odoo OWL组件开发(1):认识 OWL 手把手带你学会Odoo OWL组件开发(2):OWL的使用 手把手带你学会Odoo OWL组件开发(3):核心内容指南 ...

  9. IDEA部署tomcat原理小解

    IDEA部署tomcat原理小解 最近看到一个视频对IDEA部署启动tomcat有了新的理解.下面和大家分享一下.  1.tomcat部署项目的三种方式  在纯tomcat环境下,不使用开发工具tom ...

  10. 总结:SpringBoot内嵌Tomcat原理

    一.介绍 一般我们启动web服务都需要单独的去安装tomcat,而Springboot自身却直接整合了Tomcat,什么原理呢? 二.原理 SpringBoot应用只需要引入spring-boot-s ...

最新文章

  1. R语言使用ggplot2包使用geom_boxplot函数绘制基础分组箱图(输出多个分组、色彩配置、添加数据点)实战
  2. how Lordec maps the long reads to DeBruijn Graph
  3. pm2常用的命令用法介绍
  4. 【必备】jQuery性能优化的38个建议
  5. java密码学原型算法_java密码学原型算法实现——双线性对.pdf
  6. 在Eclipse中有效使用JUnit
  7. 深度学习之卷积神经网络 LeNet
  8. linux的常用操作——read函数和write函数
  9. Mybatisplus代码生成 之SpringBoot适配MYSQL和ORACLE
  10. greenplum 查询出来的数字加减日期_常用SQL系列之(八):列值累计、占比、平均值以及日期运算等
  11. MyApplicationToast工具类
  12. 终于研究出如何设置新版paypal付款时汇率损失方的问题了
  13. Java:JSON解析工具-org.json
  14. elasticsearch 之index API
  15. LINUX安装TensorRT及特别注意事项
  16. 计算机网络信息安全等级保护(等保)
  17. 「构建企业级推荐系统系列」推荐系统的人工调控
  18. Python修改图片像素颜色值
  19. 谈谈QUIC协议原理
  20. MySQL学习记录(11.9更新)

热门文章

  1. 哈理工OJ 1391 Orz odd(规律【没证出来】)
  2. ajaxsubmit php上传文件,使用ajaxSubmit方法实现多文件上传(异步)
  3. 解决ajaxSubmit无法传递自动回填和下拉框的数据
  4. 【RPA】PDF生成器FPDF(Python篇)
  5. 【算法精解】计数排序
  6. Tushare最好用的金融数据接口之一
  7. SQLyog 注册码(包含企业版注册码)
  8. python美女源代码_python程序员爬取百套美女写真集,同样是爬虫,他为何如此突出...
  9. 用科比一生的数据,回顾他的球场传奇
  10. idea中 Java xml注释缩进问题 解决方案