如今tomcat源码量很是大,想读懂那么多人写的代码很耗时间,这里记下我学习tomcat的一点笔记java
Tomcat是Web服务器git
几个月前我本科没毕业的时候,还不知道web服务器和应用服务器的区别github
用我本身的话描述,本身的程序里带jar文件来支持JavaEE组件的是web服务器web
从socket开始bootstrap
我以为不少中间件源码读起来很困难,由于咱们只看到了最终的产品,中间发展的过程以及增长的功能咱们并不清楚.浏览器
Tomcat如今的源码量至关的大,即便读一些解读的文章再调试也不容易理解透彻,并且在业务团队里也不必死抠每一个技术细节,因此我以为应该按部就班tomcat
我先写一个最简单的Socket,启动时只要用浏览器访问 localhost:8080 就能够看到socket 传来的信息,并能看返回当前系统时间
收到的消息 服务器
返回的结果
多线程
Socket改进app
一个socket虽然很简单,但咱们能够明白HTTP协议的原理
若是咱们把它改进一下,input封装到Request里,output封装到response里,就好理解多了
我在github上找到一个挺有意思的东东
https://github.com/dasanjos/java-WebServer
我稍微改动了一下这个代码,让它变得更直观些
它就是把我上面写的代码改为线程池管理,并把socket经过handler传给线程,分为Request和Response,支持了本地文件的读写
RequestHandler处理过程
执行效果
虽然和上面的代码相比不过就是封装了一下罢了
但线程池把socket监听请求以及io流的处理给分开了,其实这个过程就是tomcat的connector和container经过HttpProcessor传递socket的过程的简化,Main里管线程池的代码咱们能够封装到一个叫Connector里面,有请求就从池子里取个线程,让它合成Request和Response,把它上交给.....容器
Classloader原理
其实作到上一步,基本能够把这坨代码当webServer跑了,但它不支持servlet,也没有webapps目录和Context额,并且咱们都是打war包的,war包里的那些类tomcat是怎么调用的?
由于tomcat的classloader是封装到Context里面的,这个加载过程很差解释,我就写一个简单的例子说明一下:
这个目录结构至关简单,folder下有个编译好的B类,咱们启动tomcat而后启动服务的过程能够视为Main类跑时把B类也加载进去
为了更有说服力,咱们能够先ps -ef|grep java 肯定 folder不在咱们的classpath里面
而后VM参数加上-verbose:class 亲眼看看B这个class文件是怎么被加载进去的
在学校的时候配java环境变量,老是要把rt.jar 这堆玩意加到classpath里,时间久了基本背下来要加哪些包了
Java的classloader分为 bootstrap classloader,extension classloader和system classloader,其实rt.jar里的那些java核心类就是bootstrap classloader给加进去的,咱们能够执行如下这个代码看看bootstrap classloader都加载了什么
Tomcat结构
写了这么多却一直没提tomcat有些三纸无驴的感受,但其实写到这里,咱们本身就能写个很是粗陋的支持servlet的webServer相似物了
我学习tomcat源码不只是由于好奇,为了方便定位问题,也是为了了解一点它的设计思想
我画了个草图,并按本身当即标注了一下这些组件是干啥的,大体说明一下tomcat的结构
固然,这里面还有一些Loader, Pipeline ,Valve ,Repository 这类的小东西我没标,否则图就太乱了
由一个Socket忽然变得这么复杂稍微有点过分不天然,也不要紧,我在万能的github上又找到一个过分天然的项目
https://github.com/luminocean/Tommycat
这个项目麻雀虽小五脏俱全,虽然没Server.xml配置文件,但tomcat该有的它基本都有,只是容器只有Context和Wrapper,但看懂这个基本就明白tomcat的设计思想了
这个项目下面有个Mushroom的项目,主项目跑起来,会把Mushroom放到Context容器里,并经过Loader加载里面编译好的class
这里没有filter,因此若是访问的uri直接能找到文件就直接返回文件内容,找不到就会从ContextValve的map里找加载好的Servlet而后invoke调用
这里面还有个LifeCycle接口,就是我们常说的生命周期,每一个容器都实现了这个接口里的方法
我读源码的办法
我是先debug,边debug边看涉及到的类结构,由于这是多线程的,单纯debug不是很好理解
我把主要的类抠出来,只保留类里重要的域,再debug一遍就能明白类之间的调用关系