01 | Web容器学习路径

你好,我是李号双。在开篇词里我提到要成长为一名高级程序员或者架构师,我们需要提高自己知识的广度和深度。你可以先突破深度,再以点带面拓展广度,因此我建议通过深入学习一些优秀的开源系统来达到突破深度的目的。

我会跟你一起在这个专栏里深入学习Web容器Tomcat和Jetty,而作为专栏更新的第1篇文章,我想和你谈谈什么是Web容器,以及怎么学习Web容器。根据我的经验,在学习一门技术之前,想一想这两个问题,往往可以达到事半功倍的效果。

Web容器是什么?

让我们先来简单回顾一下Web技术的发展历史,可以帮助你理解Web容器的由来。

早期的Web应用主要用于浏览新闻等静态页面,HTTP服务器(比如Apache、Nginx)向浏览器返回静态HTML,浏览器负责解析HTML,将结果呈现给用户。

随着互联网的发展,我们已经不满足于仅仅浏览静态页面,还希望通过一些交互操作,来获取动态结果,因此也就需要一些扩展机制能够让HTTP服务器调用服务端程序。

于是Sun公司推出了Servlet技术。你可以把Servlet简单理解为运行在服务端的Java小程序,但是Servlet没有main方法,不能独立运行,因此必须把它部署到Servlet容器中,由容器来实例化并调用Servlet。

而Tomcat和Jetty就是一个Servlet容器。为了方便使用,它们也具有HTTP服务器的功能,因此Tomcat或者Jetty就是一个“HTTP服务器 + Servlet容器”,我们也叫它们Web容器。

其他应用服务器比如JBoss和WebLogic,它们不仅仅有Servlet容器的功能,也包含EJB容器,是完整的Java EE应用服务器。从这个角度看,Tomcat和Jetty算是一个轻量级的应用服务器。

在微服务架构日渐流行的今天,开发人员更喜欢稳定的、轻量级的应用服务器,并且应用程序用内嵌的方式来运行Servlet容器也逐渐流行起来。之所以选择轻量级,是因为在微服务架构下,我们把一个大而全的单体应用,拆分成一个个功能单一的微服务,在这个过程中,服务的数量必然要增加,但为了减少资源的消耗,并且降低部署的成本,我们希望运行服务的Web容器也是轻量级的,Web容器本身应该消耗较少的内存和CPU资源,并且由应用本身来启动一个嵌入式的Web容器,而不是通过Web容器来部署和启动应用,这样可以降低应用部署的复杂度。

因此轻量级的Tomcat和Jetty就是一个很好的选择,并且Tomcat它本身也是Spring Boot默认的嵌入式Servlet容器。最新版本Tomcat和Jetty都支持Servlet 4.0规范。

读到这里,我想你应该对Web容器有了基本的认识,可以结合平时工作再去细细体会一下。如果你对HTTP协议和Servlet依然是一头雾水,不用担心,在预习模块中我还会和你聊聊你应该掌握的HTTP协议和Servlet的相关知识,帮你打好学习的基础。

Web容器该怎么学?

Java Web技术发展日新月异,各种框架也是百花齐放。在从事Java Web开发相关的工作时,面对这些眼花缭乱的技术时你是否会感到一丝迷茫?可能有些初学者不知道从哪里开始,我身边还有些已经进入了这个行业,并且有了一定Java基础的人,对于系统设计的体会可能还不够深刻,编程的时候还停留在完成功能的层次。这样不仅业务上难有突破,对于个人成长也很不利。

为了打破这个瓶颈,就需要我们在深度上多下功夫,找准一个点,深挖下去,彻底理解它的原理和设计精髓。并且在深入学习Tomcat和Jetty这样的Web容器之前,你还需要掌握一定的基础知识,这样才能达到事半功倍的效果。

下面我列举一些在学习Web容器之前需要掌握的关键点,我建议你在学习专栏的同时,再去复习一下这些基础知识。你可以把这些基础知识当作成为架构师的必经之路,在专栏以外也要花时间深入进去。当然为了让你更好地理解专栏每期所讲的内容,重点的基础知识我也会在文章里帮你再梳理一遍。

操作系统基础

Java语言其实是对操作系统API的封装,上层应用包括Web容器都是通过操作系统来工作的,因此掌握相关的操作系统原理是我们深刻理解Web容器的基础。

对于Web容器来说,操作系统方面你应该掌握它的工作原理,比如什么是进程、什么是内核、什么是内核空间和用户空间、进程间通信的方式、进程和线程的区别、线程同步的方式、什么是虚拟内存、内存分配的过程、什么是I/O、什么是I/O模型、阻塞与非阻塞的区别、同步与异步的区别、网络通信的原理、OSI七层网络模型以及TCP/IP、UDP和HTTP协议。

总之一句话,基础扎实了,你学什么都快。关于操作系统的学习,我推荐你读一读《UNIX环境高级编程》这本经典书籍。

Java语言基础

Java的基础知识包括Java基本语法、面向对象设计的概念(封装、继承、多态、接口、抽象类等)、Java集合的使用、Java I/O体系、异常处理、基本的多线程并发编程(包括线程同步、原子类、线程池、并发容器的使用和原理)、Java网络编程(I/O模型BIO、NIO、AIO的原理和相应的Java API)、Java注解以及Java反射的原理等。

此外你还需要了解一些JVM的基本知识,比如JVM的类加载机制、JVM内存模型、JVM内存空间分布、JVM内存和本地内存的区别以及JVM GC的原理等。

这方面我推荐的经典书籍有《Java核心技术》《Java编程思想》《Java并发编程实战》《深入理解Java虚拟机:JVM高级特性与最佳实践》等。

Java Web开发基础

具备了一定的操作系统和Java基础,接下来就可以开始学习Java Web开发,你可以开始学习一些通用的设计原则和设计模式。这个阶段的核心任务就是了解Web的工作原理,同时提高你的设计能力,注重代码的质量。我的建议是可以从学习Servlet和Servlet容器开始。我见过不少同学跳过这个阶段直接学Web框架,这样做的话结果会事倍功半。

为什么这么说呢?Web框架的本质是,开发者在使用某种语言编写Web应用时,总结出的一些经验和设计思路。很多Web框架都是从实际的Web项目抽取出来的,其目的是用于简化Web应用程序开发。

我以Spring框架为例,给你讲讲Web框架是怎么产生的。Web应用程序的开发主要是完成两方面的工作。

  • 设计并实现类,包括定义类与类之间的关系,以及实现类的方法,方法对数据的操作就是具体的业务逻辑。

  • 类设计好之后,需要创建这些类的实例并根据类与类的关系把它们组装在一起,这样类的实例才能一起协作完成业务功能。

就好比制造一辆汽车,汽车是由零件组装而成的。第一步是画出各种零件的图纸,以及定义零件之间的接口。第二步把把图纸交给工厂去生产零件并组装在一起。因此对于Web应用开发来说,第一步工作是具体业务逻辑的实现,每个应用都不一样。而第二步工作,相对来说比较通用和标准化,工厂拿到零件的图纸,就知道怎么生产零件并按照零件之间的接口把它们组装起来,因此这个工作就被抽取出来交给Spring框架来做。

Spring又是用容器来完成这个工作的的,容器负责创建、组装和销毁这些类的实例,而应用只需要通过配置文件或者注解来告诉Spring类与类之间的关系。但是容器的概念不是Spring发明的,最开始来源于Servlet容器,并且Servlet容器也是通过配置文件来加载Servlet的。你会发现它们的“元神”是相似的,在Web应用的开发中,有一些本质的东西是不变的,而很多“元神”就藏在“老祖宗”那里,藏在Servlet容器的设计里。

Spring框架就是对Servlet的封装,Spring应用本身就是一个Servlet,而Servlet容器是管理和运行Servlet的,因此我们需要先理解Servlet和Servlet容器是怎样工作的,才能更好地理解Spring。

本期精华

今天我谈了什么是Web容器,以及该如何学习Web容器。在深入学习之前,你需要掌握一些操作系统、Java和Web的基础知识。我希望你在学习专栏的过程中多温习一下这些基础知识,有扎实的基础,再结合专栏深入学习Web容器就比较容易了。

等你深刻理解了Web容器的工作原理和设计精髓以后,你就可以把学到的知识扩展到其他领域,你会发现它们的本质都是相通的,这个时候你可以站在更高的角度来学习和审视各种Web框架。虽然Web框架的更新比较快,但是抓住了框架的本质,在学习的过程中,往往会更得心应手。

不知道你有没有遇到过这样的场景,当你在看一个框架的技术细节时,会突然恍然大悟:对啊,就是应该这么设计!如果你有这种感觉,说明你的知识储备起到了作用,你对框架的运用也会更加自如。

课后思考

请你分享一下你对Web容器的理解,或者你在学习、使用Web容器时遇到了哪些问题?

不知道今天的内容你消化得如何?如果还有疑问,请大胆的在留言区提问,也欢迎你把你的课后思考和心得记录下来,与我和其他同学一起讨论。如果你觉得今天有所收获,欢迎你把它分享给你的朋友。

精选留言

  • 蔡伶

    2019-05-15 08:55:08

    打卡

    先说下听完老师课程的感受:经典不会随着时间而消逝。java和servlet规范已经发布20多年、操作系统和网络协议以及html更是经过了几十年的洗礼,现在依然是业内最核心的技术基础,毫不动摇。

    课程的理解:当前web技术涉及的知识包括这样几层,
    第一层:核心规范相当于宪法,主要包括servlet规范、网络协议等;
    第二层:主流技术支撑相当于各类法律,包括java语言、各类中间件等;
    第三层:基于各行业的业务应用和框架,相当于行政法规地方法规。
    规范是基础,具体实现可以用java也可以用python等等,行业应用和框架更是可以百花齐放。
    那我们的学习一定是从具体技术入手,从规范和体系结构统筹安排,最后再落实到实现。是一个自底向上再由上向下的一个过程,也是一个由薄到厚再由厚到薄的过程。
    作者回复

    说的很好👍

    2019-05-15 09:19:54

  • 今夜秋风和

    2019-05-14 21:55:20

    应用程序的上下文,这个概念总是感觉理解不透彻
    作者回复

    简单可以这里理解:

    我们把Spring的IOC容器理解为一个工厂,这个工厂负责创建组装你的Bean。

    但是我们怎么向IOC容器中放入Bean呢?可能通过配置文件或者注解或者其他方式,于是容器除了做创建、组装Bean的工作,还需要去做解析配置文件或者注解的工作,于是把容器换个说法,叫应用上下文。

    2019-05-15 00:28:36

  • G

    2019-05-13 20:22:26

    你说的所有spring. 都应该说springMVC
    作者回复

    嗯嗯,SpringMVC是Spring的子集,我就说成Spring了~

    2019-05-13 20:36:21

  • 贤蛋蛋

    2019-05-15 04:27:05

    请问为什么说http是超文本传输协议,文本两字的含义是什么?http2.0所说的二进制帧,为什么说是二进制,和1.1格式上的本质区别是什么?再往下一层到TCP能否都看成二进制帧?
    作者回复

    文本可以理解为只有文字信息的文档,超文本是带有超链接的文档,可以链接到另一个文档,或一张图...

    HTTP1.1是文本协议,HTTP2.0是二进制协议。

    文本协议的协议数据是由ACSII字符组成的,比如文章里的HTTP请求的例子:请求行、请求头和请求体,我们一眼就看出什么意思。这是因为协议里的每个Byte都是用ACSII字符来解释的。

    二进制协议的的每个Byte完全由协议本身来定义,比如一个Byte有8个Bit,这8个Bit可能有不同的意思(比如代表长度或者其他标志位),不一定代表一个ACSII字符。

    TCP是二进制协议。


    2019-05-15 09:13:18

  • 落幕

    2019-05-14 08:59:53

    和一群优秀的人共同进步,这种感觉很好。
  • 凌霄

    2019-05-14 23:02:04

    遇到过一个偶发的tomcat8问题,请求到tomcat后,nio长连接,到了20秒后超时后才自动断开连接,返回结果内容正常,抓包发现和正常的比少了最后的回车换行。
    作者回复

    你的应用程序设置的响应长度Content-Length与实际响应数据长度不符,可能长了那么一丢丢,这样Tomcat一直在等你的数据呢。

    2019-05-17 18:20:39

  • 飞向云端

    2019-05-19 09:58:02

    什么叫内嵌方式运行servlet容器,老师有时间普及一下。
    作者回复

    就是你的程序比如SpringBoot直接调用Web容器的提供的API去创建一个Web容器(HTTP服务器和Servlet容器),同时你的程序注册一个Servlet到Servlet容器中,比如SpringMVC的DispatcherServlet,这样请求到达时,Servlet容器负责调用你的Servlet。

    2019-05-19 10:30:11

  • 熊斌

    2019-11-14 23:08:59

    工作几年之后才明白 万变不离其宗,所以一直努力往技术的最底层走。跟着大佬探寻常用技术的本质。双11刚买的课,每天看一两节
  • 林炳强

    2019-06-23 21:37:16

    Spring 框架就是对 Servlet 的封装。。。
    这句话我感觉容易造成误解。Spring MVC只是Spring的一部分,只有Spring MVC是Servlet的实现,其他的并没有。
  • yy_java

    2019-05-18 02:05:22

    请问老师,操作系统基础 除了您推荐的那本书以外还有其他薄点的书籍推荐吗?
    作者回复

    😑,我理解这本书是有点厚,但还是建议读经典的比较好,你不需要一口气读完,专栏讲到了某个点再去看看相关的部分~

    2019-05-18 09:49:16

  • 拒绝

    2019-05-13 18:19:12

    还停留在使用容器的阶段,并不清楚其原理,例如:一个请求到一个响应返回,其涉及到的设计模式,以及为什么这样做,这样做的好处是什么;我能在容器的基础上做一些自定义的扩展吗?希望在专栏收获到这些。
    作者回复

    对的,这个专栏会学习Tomcat&Jetty为什么设计成这样,设计者是怎么考虑问题的;

    Tomcat和Jetty做为中间件,可扩展性非常强,你可以通过它们定制自己的Web容器!

    2019-05-13 19:07:32

  • balsamspear

    2020-09-15 17:57:26

    **思考题**

    请你分享一下你对 Web 容器的理解,或者你在学习、使用 Web 容器时遇到了哪些问题?

    Web 容器是 HTTP 服务器 + Servlet 容器

    1. HTTP 服务器负责处理 HTTP 请求(接收请求、返回请求结果)
    2. Servlet 容器负责把 HTTP 请求分派给对应的 servlet 程序处理,并把结果返回给 HTTP 服务器

    问题

    1. 如何解析请求?
    2. HTTP 服务器线程、Servlet 容器线程和 servlet 程序线程的关系?
    3. Web 容器能支持什么量级的并发,由哪一块决定?HTTP 服务器、Servlet 容器还是 servlet 程序?
  • chibohe

    2019-05-27 09:22:35

    如果我的服务全都是rpc调用,不涉及http调用,可以不部署在tomcat或者jetty容器中吗?还有你说的Spring都应该指的是SpringMVC吧?
    作者回复

    可以的,比如Dubbo。文中的Spring可以理解为SpringMVC。

    2019-05-27 13:32:04

  • Monday

    2019-05-16 00:02:08

    操作系统还是我的痛,但也不是一两天就能补充得了的。只好边学本专栏边学操作系统了。希望不要因为操作系统的缘故拖了学习本专栏的后腿😃😃
    作者回复

    不会的,咱们这边都有具体应用场景,根据实际运用场景开学理解起来会容易些

    2019-05-16 13:27:43

  • Jaswine

    2019-05-15 16:53:12

    带着问题学知识,学习的时候问自己几个为什么,为什么这么设计?为什么不那么设计?在自己解答这些为什么的时候,最后发现都是计算机基础知识决定的,操作系统,网络,数据结构和算法
    作者回复

    嗯嗯,除了学,还要思考和总结

    2019-05-15 18:26:52

  • Mr.tt

    2020-08-27 00:54:27

    说说对Web容器的理解:所谓容器就是一个装东西的东西,比如水容器,就是装水的,而我们的web容器就是装web的,从这里就可以看出,tomcat等这类容器是可以部署多个web项目的。在深入一点点,一个能作为水容器的要求是什么?不能有漏洞对不对,那么作为一个web容器它的要求是什么呢?一.能够处理网络请求,二.能返回数据。首先能够处理网络请求(作为http服务器),这个意味着它能够监听端口,能够操作操作系统里的网络请求,并把这些请求和我们的web项目关联起来,交给我们的web项目处理这些请求,我们项目要怎么处理这些请求呢?什么样的程序能够处理这些请求呢?有没有什么规范呢?答案是有的,就是servlet规范。这个规范和tomat有啥关系?tomcat就是一个servlet容器。所以tomcat其实是一个http服务器+servlet服务器的集合
  • 刘三通

    2019-05-19 11:30:58

    Spring应用本身就是一个Servlet容器
    作者回复

    Spring是Bean容器,不是Servlet容器,它不负责加载和实例化Servlet。

    2019-05-19 12:35:52

  • 小可爱(๑• . •๑)

    2019-05-14 17:02:47

    期待不断更新,希望老师能够讲清楚,让大家都理解
    作者回复

    后面的内容基本是先回顾基础知识,再谈Tomcat&Jetty如何在真实场景下运用这些基础知识,这样理解起来比较容易。

    2019-05-14 17:45:00

  • Rumble

    2020-01-15 20:58:17

    我天,1楼的大佬也太6了,没去从政真的是浪费人才
  • Demon.Lee

    2019-12-23 12:49:58

    老师,我们现在微服务部署是按spring boot 内嵌的方式处理的,打包成jar,然后放在docker容器里跑,一个docker容器给1G内存,20个微服务就是20个G,现在想把docker容器去掉,比如部署4-5个tomcat,每个tomcat里面放置5-4个微服务war包,这样能节约内存么?如何部署比较合适呢(一个tomcat里面放几个app.war合适)?文章里面说:"由应用本身来启动一个嵌入式的 Web 容器,而不是通过 Web 容器来部署和启动应用,这样可以降低应用部署的复杂度。",到底那种方式更好呢,我有点糊涂了。我想着一起每个docker里面都会启动一个jvm,现在一个tomcat才会启动一个jvm吧,感觉更节约内存。另外,想着把几个微服务war包里面都公用的一些common包放在tomcat的lib下,不知道是否可行?还望老师解答下,或者给些思路。