tomcat流程
起始
刚开始做项目的时候就觉得tomcat这个东西很神奇,就给项目打一个包就能当一个网站来访问,那时候就产生了一个疑惑,为什么tomcat要用servlet来搭配着使用才行,而且为什么servlet没有主方法就能直接运行出结果来,当时接触java时间并不是很长,对这些问题也没有深究;
然后后来学习springboot的时候就了解到Springboot里面是内置了tomcat的,当时就想tomcat是不是就是用java来写的,于是我就百度了一下,发现tomcat确实是java来写的。然后我就自己看了一下tomcat的源码
首先tomcat最原始的启动方法就是双击startup.bat来启动,我用sublime打开了这个脚本看了看,发现他指向了catalina.bat这个文件,然后我去catalina.bat里面详细的看了一下,我本身对shell脚本并不是很熟悉,所以很多东西也看不太懂,但是我觉得既然他是java写的肯定要依赖java的代码,我就在这个文件里找了找jar,果然找到了bootstrap.jar这个jar包。
然后我用idea查看了一下这个包的源码,果然在Bootstrap这个类中找到了启动的main方法,然后我看了一下他的启动流程
** 启动阶段**
在启动阶段,它会扫描webapp目录下的所有的子目录和子文件,然后把class文件挑选出来,拿到每个java类的类路径,拿到类路径之后用反射里的class.forName()方法来获取类信息。
拿到类信息之后会遍历一遍查看哪个类里面有@WebServlet注解,然后把这些java文件挑选出来之后用newInstance()来创建类的实例,就相当于new操作,(要能解释为什么不用new而用newInstance()),然后生成了实例之后就可以拿到这个实例的方法,因为我们开发者自己写servlet的话如果随便写的话可能每个人的方法都是不一样的,所以tomcat他给了我们两个方法doGet和doPost,这个的用意就是tomcat给我们定义了一个规范,我们按照这个规范完善doget和dopost那么tomcat就可以通过拿到的方法来调用我们写的方法。
拿到方法之后筛选出doGet和doPost然后同时拿到注解里写的路径的值,然后就把他们放到了hashmap里面,key是路径,value就是对象的实例信息,
** 接受请求阶段**
tomcat是通过socket监听的8080端口(默认),我看源码的时候发现这里的socket和之前的socket不太一样,后来百度查了一下才知道这里是引用的netty框架里面的socket。tomcat借助socket拿到http请求之后把请求放到了线程池中,
用线程池的原因:
如果没有线程池的话,每过来一个请求就要开启一个线程,如果某一瞬间来了几万条甚至几十万条请求的话,就会开启大量的线程,有可能内存就不足以支撑这么多线程从而崩掉。而且,线程的创建和销毁都有巨大的开销,每一个线程都有自己的栈针,方法执行完毕出栈,这样就伴随着栈空间的申请和回收。
而回收栈空间虽然是批量回收的,但是回收的时候会中断其他的线程,占用了大量的CPU的资源。由于栈不是立即回收,所以在回收前会积累大量的栈空间垃圾。尤其是在高并发的场景下更容易出发Full GC,从而影响到整体的性能。而我们引入线程池的话,来大量请求的时候我们可以直接放入队列中,线程池里的线程的run方法是死循环,每次循环都会从队列里获取任务从而达到重复利用线程的目的。如果队列满了,我们还可以执行拒绝策略,这样就减轻了服务器的压力。
然后线程池里每个线程的工作大概就是先把http字符信息封装成HttpRequset对象和HttpResponse对象,同时提取出请求的url地址,然后把url的ip,端口和项目名称都去掉,剩下的就是对应的servlet或者前端静态资源的地址,然后如果请求的是前端的资源,他会根据请求的路径去相应的目录下找前端的文件,之后按照相应的编码读取里面的字符串返回给前端;如果请求的是servlet,那么会根据url寻找启动阶段缓存的HashMap匹配servlet实例和method对象,提取出来之后用代理的invoke方法来完成对servlet的调用。
Tomcat请求过程(来自资料总结)
1.用户点击网页内容,请求被发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得。
2.Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的回应。
3.Engine获得请求,匹配所有的虚拟主机Host。
4.Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机),Host获得请求,匹配它所拥有的所有的Context。Host匹配到对应请求路径的Context(如果匹配不到就把该请求交给路径名为“ ”的Context去处理)。
5.匹配的Context获得请求,在它的mapping table中寻找出对应的Servlet。
6.构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用Servlet的doGet()或doPost(),执行业务逻辑、数据存储等程序。
7.Context把执行完之后的HttpServletResponse对象返回给Host。
8.Host把HttpServletResponse对象返回给Engine。
9.Engine把HttpServletResponse对象返回Connector。
10.Connector把HttpServletResponse对象返回给客户Browser。
## tomcat容器是如何创建servlet类实例?用到了什么原理?
当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对xml文件进行解析,
并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化。
(有时候也是在第一次请求时实例化)在servlet注册时加上如果为正数,则在一开始就实例化,
如果不写或为负数,则第一次请求实例化。
Tomcat工作模式?
Tomcat是一个JSP/Servlet容器。其作为Servlet容器,有三种工作模式:独立的Servlet容器、进程内的Servlet容器和进程外的Servlet容器。
进入Tomcat的请求可以根据Tomcat的工作模式分为如下两类:
Tomcat作为应用程序服务器:请求来自于前端的web服务器,这可能是Apache, IIS, Nginx等;
Tomcat作为独立服务器:请求来自于web浏览器;