极客时间已完结课程限时免费阅读

答疑丨Java Web程序的运行时环境到底是怎样的?

答疑丨Java Web程序的运行时环境到底是怎样的?-极客时间

答疑丨Java Web程序的运行时环境到底是怎样的?

讲述:李智慧

时长10:20大小9.44M

今天是第一模块的最后一讲。在这一讲中,我们主要讲了软件的基础原理,今天,我将会针对这一模块中大家提出的普遍问题进行总结和答疑,让我们整理一下,再接着学习下一个模块的内容。

问题一

@小美
既然一个 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 容器
如果有外部请求发送到 Tomcat,也就是外部程序通过 80 端口和 Tomcat 进行 HTTP 通信的时候,Tomcat 会根据 war 包中的 web.xml 配置,决定这个请求 URL 应该由哪个 Servlet 处理,然后 Tomcat 就会分配一个线程去处理这个请求,实际上,就是这个线程执行相应的 Servlet 代码
我们回到小美同学的问题,Tomcat 启动的时候,启动的是 JVM 进程,这个进程首先是执行 JVM 的代码,而 JVM 会加载 Tomcat 的 class 执行,并分配一个主线程,这个主线程会从 main 函数开始执行。在主线程执行过程中,Tomcat 的代码还会启动其他一些线程,包括处理 HTTP 请求的线程。
而我们开发的应用是一些 class,被 Tomcat 加载到这个 JVM 里执行,所以,即使这里有多个应用被加载,也只是加载了一些 class,我们的应用被加载进来以后,并没有增加 JVM 进程中的线程数,也就是 web 应用本身和线程是没有关系的。
而 Tomcat 会根据 HTTP 请求 URL 执行应用中的代码,这个时候,可以理解成每个请求分配一个线程,每个线程执行的都是我们开发的 Web 代码。如果 Web 代码中包含了创建新线程的代码,Tomcat 的线程在执行代码时,就会创建出新的线程,这些线程也会被操作系统调度执行。
如果 Tomcat 的线程在执行代码时,代码抛出未处理的异常,那么当前线程就会结束执行,这时控制台看到的异常信息,其实就是线程堆栈信息,线程会把异常信息以及当前堆栈的方法都打印出来。事实上,这个异常最后还是会被 Tomcat 捕获,然后 Tomcat 会给客户端返回一个 500 错误。单个线程的异常不影响其他线程执行,也就是不影响其他请求的处理。
但是如果线程在执行代码的时候,抛出的是 JVM 错误,比如OutOfMemoryError,这个时候看起来是应用 crash,事实上是整个进程都无法继续执行了,也就是进程 crash 了,进程内所有应用都不会被继续执行了。
从 JVM 的角度看,Tomcat 和我们的 Web 应用是一样的,都是一些 Java 代码,但是 Tomcat 却可以加载执行 Web 代码,而我们的代码又不依赖 Tomcat,这也是一个很有意思的话题。Tomcat 是如何设计的,我将会在下个模块讲述。

问题二

@黄海峰
有点难以想象,“Hash 表的时间复杂度为什么是 O(1)”这个问题居然有阿里大厂的面试官觉得难。
这不是一个疑问,但其实是一个有意思的话题,我们花一点时间讨论下,也许会对你的职业规划有所启发。
文中这个故事大概发生在 2009 年,整整十年前,那个时候互联网还不像今天这样炙手可热,提供的薪水也不像今天这样有竞争力,也没有 BAT 这样的专有名词指代所谓的互联网巨头。那个时候,计算机专业优秀的毕业生向往的是微软、Oracle、IBM 这样的外资 IT 巨头,退而求其次,国内好的 IT 公司是联想、用友这些企业。
事实上,那个时候在技术研发能力上,互联网公司的技术能力也是落后传统企业的,阿里巴巴最核心的数据存储依赖的是 IBM、Oracle、EMC 的解决方案,即所谓的 IOE。
所以在十年前的人才市场上,国内互联网公司的形象一般是:技术落后、薪水一般、加班严重、没有名气。可以说在人才市场的竞争中,相比国内外的 IT 巨头是落于下风的。
我个人感觉,互联网公司的崛起大概是在七八年前,移动互联网开始出现,互联网的渗透率得到加速,BAT 逐渐开始成为家喻户晓的名字,名气大涨。其次,经过前面时间的积累,互联网企业主导的各种分布式技术、大数据技术、移动互联网技术、云计算技术的风头超过传统 IT 巨头,阿里巴巴开始去 IOE,打造自己的云计算平台,成为先进技术的代表者;最主要的还是互联网企业盈利能力大幅增加,能够提供市场上更有竞争力的薪水和股票。
于是互联网企业在人才市场上开始变得灼手可热,BAT 这些企业开始被人称为“大厂”。我们今天感觉这些互联网巨头高高在上,人们纷纷向往。事实上,这个现象出现的时间非常短。今天这些企业有足够的名气和资源将自己营造得高高在上,可以在众多优秀的候选人中间挑来选去,仅仅在十年前,还不是这样的。
但是事情真正的吊诡之处还不在这里,当今这些互联网大厂的核心技术和业务模式在十几年前就已经奠定了,经过几年的摸索,大概在七八年前开始稳定成熟。也就是说,互联网企业的技术实力和商业能力是在这些企业还默默无闻的时候就发展起来的,而在这些企业成为明星之后,并没有什么突破性的进展。想想这些所谓的互联网大厂,最近几年,并没有什么值得称道的商业模式创新和技术创新。
也就是说,十多年前,可能是一些并不优秀的技术人员加入一个并不出名的公司,然后这些人开创出了一个杰出的事业。用马云的话说,就是“二流的人做一流的事”。然后公司开始挑选一流的人,但结果似乎只是在维持这个事业,并没有开创出更加杰出的事业。今天的 BAT 似乎成为当年的 IBM,历史好像进入了某种循环。
如果这就是事情的真相,我想你或许可以从其中得到某些启发,重新考虑下未来的职业规划。也许你会发现,你可能不需要追逐当前所谓的热门技术,而应该好好想想需要为自己的未来准备些什么。
最后,在第一模块中,我在每一篇文章的下面都留了几道思考题,各位同学在评论区都有很好的答案。但只有第五篇文章,我似乎没有看到比较准确的答案,我在这里回答一下。
RAID5 中,校验位之所以螺旋式地落在所有硬盘上,主要原因是因为如果将校验位记录在同一块硬盘上,那么对于其他多块数据盘,任何一块硬盘修改数据,都需要修改这个校验盘上的校验数据,也就是说,对于有 8 块硬盘的 RAID5 阵列,校验盘的数据写入压力是其他数据盘的 7 倍。而硬盘的频繁写入会导致硬盘寿命缩短,校验盘会频繁损坏,存储的整体可用性和维护性都会变差。
所以,作为软件架构师,当你在进行软件设计的时候,你不光需要考虑软件本身,你还需要了解软件的各种约束,硬盘的特性约束是一种,当然还有其他一些约束,我会在专栏的后面模块中继续讲解如何在各种约束下,设计出符合期望的软件系统。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 25

提建议

上一篇
07丨编程语言原理:面向对象编程是编程的终极形态吗?
下一篇
08丨软件设计的方法论:软件为什么要建模?
unpreview
 写留言

精选留言(17)

  • Citizen Z
    2019-12-04
    成就一个人的,是事业所带来的社会效益。 衡量一个人成就的是他所带来贡献,而非多么华丽惊艳的技术实现。 能力是实现成就的条件,不是终极目标。 技术只是重要工具,应该精进,不必狂热。 做技术的其实挺容易迷失在无尽地追逐技术牛逼的道路上,诚然能带来快乐,却很容易造成思维封闭。想在事业上更上一层楼,要把眼光放在更大的局面上,小到帮助同事朋友,大到给公司战略提供建议、给开源社区贡献力量,虽然各方面能力暂时不一定能匹配得上,但是大的目标能让人豁然开朗,提高学习动力和做事动机。 个人经验,程序员应该敢于务虚、吹理论、玩圈子。不该将自己约束到一个码农的标签里,追求字面意义上的“talke cheap show code”,更不该用“我学技术就是为了挣钱”这种消极思想来对待职业发展,应该回归到“一个会计算机技术的职场人”的角色中。 归根结底,职业发展目标应该建立在事业的成功和价值的创造上,如此,能力、影响力、金钱会很自然地在过程中积累。 价值第一,效率第二,名利第三,目标向善,正向循环。
    展开
    共 2 条评论
    106
  • 考休
    2019-12-09
    程序员很容易有一种错觉,觉得这个世界是由技术推动的,其实不是,这个世界一直以来都是由资本推动的,技术只是资本的一种体现而已,工作中目的不应该是实现技术的最高难度,而应该是业务的最大价值呈现
    共 2 条评论
    31
  • 技术是伙伴
    2020-03-19
    最主要的是要去做一流的事,解决现实中大多人的痛点或问题
    9
  • Geek_2b3614
    2019-12-04
    真是有感而发呀。
    5
  • 未知
    2019-12-16
    “BAT 似乎成为当年的 IBM,历史好像进入了某种循环” 老师说的这句话感触很深。平时看公司比较核心的业务系统或者基础系统代码(新做的除外),代码一般都是很久以前的。这种现象应该都比较普遍,程序嘛,只要能满足目前的量并且没BUG,大家都没改重构的动力。但是如果从公司业务上来说,这种现象就比较危险:业务可以关系公司生死。大家都说乔布斯厉害,为什么厉害。感觉就是破、立。乔布斯二次回归时候,苹果已经从初创时候的高点落下来了,还差点卖身了。电脑不好卖加上金融危机,怎么办呢?但是他搞出了iPod。革了CD机的命,有搞出了Imac、iPhone。并且每个产品都标新立异。现在我们看,一个做电脑的公司做音乐播放器、做手机似乎正常。但是我们看看联想做手机、格力做手机、华为做手机。这些在发展初期都很困难,而且有的死了有的成功了。所以大公司转型或者插足新行业(那怕是同行业另一个领域),都比较谨慎,成不成完全未知,就像阿里云的初期一样。 一个产品能够做好,做大,流芳百世实属不易
    展开
    5
  • 李子木
    2019-12-14
    老师讲的真的很好,从第一节一直看下来我觉得对我这种想在IT领域深入学习的初学者解答了很多疑问,期待后面的更新!
    5
  • escray
    2020-10-18
    我觉的关于 Java Web 程序运行时环境的讲解是非常精彩的,可能是我读书少,之前一直没有搞明白。这部分内容相对偏底层,平时写代码的时候不一定用得上,但是了解之后,还是会有茅塞顿开的感觉。 至于互联网大厂的技术栈,最近在看淘宝技术发展之类的文章,也感觉有点奇怪,BAT 大厂的架构大多在几年前就已经发展成熟;然后近些年虽然人工智能、大数据、区块链之类的技术热点不断涌现,但是似乎也没有什么特别颠覆性的发展。 互联网大厂最终会像传统 IT 企业那样凋零么? 老师提出应该好好想想未来,我真的想不出来。只是感觉,如果可能的话,教育、医疗等垂直领域应该还有机会,面向企业的软件服务或者是互联网化似乎也有很多事情可做。编程从本质上来说,其实是帮助人们提高效率。
    展开
    2
  • 不要挑战自己的智商
    2021-11-11
    选择比努力重要
    1
  • FreezeSoul
    2020-03-28
    我们通过技术的手段为自己也为世界做出一点点贡献
  • 黄荣星
    2020-02-11
    智慧老师。按照文章说每一个请求Tomcat都会起一个新线程负责处理,那么如果该请求逻辑代码自行新建线程,是不是就是再创建一个线程了?是不是被一个请求都会新建一个新线程,那么代码里面就不需要自行新建一个新的线程?
    共 2 条评论
  • 不记年
    2020-01-31
    第五篇的答案原来是负载均衡啊
  • 灰灰
    2019-12-17
    打卡
  • 孙志强
    2019-12-17
    单线程OOM,会触发一次Full GC,如果能够回收掉,应该不会导致整个JVM 挂掉,使用的收集器是G1
  • Paul Shan
    2019-12-05
    tomcat进程的职责是监听80端口,分派线程处理请求,war包定义如何处理请求。 RAID5 假设有8块盘,校验那一部分写入是正常信息写入的7倍,所以要把校验信息分散到8块盘中,以使得每块盘寿命大致相等。每块盘有1/8的存储信息是其他7块盘(每块非校验信息是7/8)的校验,具体是把数据分块,每块盘轮流作为校验盘。
  • 奔奔奔跑
    2019-12-05
    哈哈,频繁写入的这个没考虑到,不过老师答疑hash表的问题我还是很有反思的,但是现在大部分公司要求高并发,用过这个redis,用过那个kafka,rocketmq,用过这个Api网关,用过那个etcd,为啥都这样呢
  • 俊伟
    2019-12-04
    目前技术给我的感觉是,很多都是在基于http协议上面进行开发。最近一直在想还有那些领域可以使用编程提高生产力。
    1
  • Demter
    2019-12-04
    希望多出点专栏