Java Web程序在Tomcat上是如何运行的

一个JVM是一个进程,JVM上跑Tomcat,Tomcat上可以部署多个应用。这样的话,每个跑在Tomcat上的应用是一个线程吗?如果一个应用crash了,其他应用也会crash吗?

理解程序运行时的执行环境,直观感受程序是如何运行的,对我们开发和维护软件很有意义。我们以上面这个问题为例,看下Java Web程序的运行时环境是什么样的,来梳理下进程、线程、应用、Web容器、Java虚拟机和操作系统之间的关系。

我们用Java开发Web应用,开发完成,编译打包以后得到的是一个war包,这个war包放入Tomcat的应用程序路径下,启动Tomcat就可以通过HTTP请求访问这个Web应用了。

在这个场景下,进程是哪个?线程有哪些?Web程序的war包是如何启动的?HTTP请求如何被处理?Tomcat在这里扮演的是什么角色?JVM又扮演什么角色?

首先,我们是通过执行Tomcat的Shell脚本启动Tomcat的,而在Shell脚本里,其实启动的是Java虚拟机,大概是这样一个Shell命令:

java org.apache.catalina.startup.Bootstrap "$@" start 

所以我们在Linux操作系统执行Tomcat的Shell启动脚本,Tomcat启动以后,其实在操作系统里看到的是一个JVM虚拟机进程。这个虚拟机进程启动以后,加载class进来执行,首先加载的就这个org.apache.catalina.startup.Bootstrap类,这个类里面有一个main()函数,是整个Tomcat的入口函数,JVM虚拟机会启动一个主线程从这个入口函数开始执行

主线程从Bootstrap的main()函数开始执行,初始化Tomcat的运行环境,这时候就需要创建一些线程,比如负责监听80端口的线程,处理客户端连接请求的线程,以及执行用户请求的线程。创建这些线程的代码是Tomcat代码的一部分。

初始化运行环境之后,Tomcat就会扫描Web程序路径,扫描到开发的war包后,再加载war包里的类到JVM。因为Web应用是被Tomcat加载运行的,所以我们也称Tomcat为Web容器或者Servlet容器

如果有外部请求发送到Tomcat,也就是外部程序通过80端口和Tomcat进行HTTP通信的时候,Tomcat会根据war包中的web.xml配置,决定这个请求URL应该由哪个Servlet处理,然后Tomcat就会分配一个线程去处理这个请求,实际上,就是这个新线程执行了相应的Servlet代码。如何分配线程呢?Tomcat使用了线程池,在用户发起的一个访问web资源(servlet或者jsp页面)的请求过来时,如果线程池里面有空闲的线程,那么会在线程池里面取一个工作线程来处理该请求,一旦工作线程当前在处理请求(在工作线程中调用servlet的service方法或者doGet/doPost方法),其他请求就不会被分配到该工作线程上,直到该请求处理完成。请求处理完成后,会将工作线程重新加入线程池。

Tomcat启动之初,会根据配置server.xml等xml及webapps下部署的app,在内存中建立起一个全局的host->context->servlet的映射关系,Tomcat有一个connector组件,负责接收socket,并进行HTTP协议的解析,然后从进程全局的线程池中随机获取一个线程,根据解析出的URL,在前面的映射关系中定位到相应的servlet进行业务处理。

Tomcat启动的时候,启动的是JVM进程,这个进程首先是执行JVM的代码,而JVM会加载Tomcat的class执行,并分配一个主线程,这个主线程会从main函数开始执行。在主线程执行过程中,Tomcat的代码还会启动其他一些线程,包括处理HTTP请求的线程。

而我们开发的应用其实就是一些类, 这些类被Tomcat加载到JVM里执行,所以,即使这里有多个应用被加载,也只是多加载了一些类而已,我们写的类被加载进来以后,并没有增加JVM进程中的线程数,也就是Web应用本身和线程是没有关系的

而Tomcat会根据HTTP请求的URL执行应用中的代码,这个时候,可以理解成每个请求分配一个线程,每个线程执行的都是我们开发的Web代码。如果Web代码中包含了创建新线程的代码,Tomcat的线程在执行代码时,就会创建出新的线程,这些线程也会被操作系统调度执行

如果Tomcat的线程在执行代码时,代码抛出了未处理的异常,那么当前线程就会结束执行,这时控制台看到的异常信息,其实就是线程堆栈信息,线程会把异常信息以及当前堆栈的方法都打印出来。事实上,这个异常最后还是会被Tomcat捕获,然后Tomcat会给客户端返回一个500错误。单个线程的异常不会影响其他线程执行,也就是不影响其他请求的处理。但是如果线程在执行代码的时候,抛出的是JVM错误,比如OutOfMemoryError,这个时候看起来是应用crash,事实上是整个进程都无法继续执行了,也就是进程crash了,进程内所有应用都不会被继续执行了

从JVM的角度看,Tomcat和我们的Web应用是一样的,都是一些Java代码,但是Tomcat却可以加载并执行Web代码,而我们的代码又不依赖Tomcat。

那么将Web应用部署在Tomcat这样的容器下有什么好处呢?我觉得可以使开发者聚焦业务逻辑,而不用去关心HTTP协议方面的事情。比如,普通的HTTP请求就是一段有格式的文本,服务器需要去解析这段文本才能知道用户请求的内容是什么,如果我们为了写一个Web应用,还要去解析HTTP协议相关的内容,那会增加很多工作量。如果我们写的Web应用不大,不夸张的说,项目中对HTTP提供支持的代码会比业务代码还要多,这岂不是得不偿失。而在现实中,有现成的框架可用,并不需要自己造轮子。如果我们基于Servlet规范实现Web应用的话,HTTP协议的处理过程就不需要我们参与了。这些工作交给Servlet容器就行了,我们只需要关心业务逻辑怎么实现即可。Spring MVC就是基于Servlet实现的框架

  • 14
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值