Tomcat学艺
Tomcat,熟悉的陌生人。我们所有的项目都运行在它上面,而我们却往往对它视而不见。现实中也是如此,我们周围充满了空气,我们无时无刻不在呼吸,但你从来没关心过它。同样的,Tomcat于我们而言,也只是在创建环境或者运行项目爆出各种错误时,才会去看看它。
上世纪90年代在大洋彼岸,有一家名唤SUN的公司,创造了一门全新的语言,叫Java。经过短短几年的发展,一跃成为市场上最炙手可热的语言。随后又悟出“Java13绝技”,也就是所谓的JavaEE规范:JDBC,JNDI,EJB,RMI,JSP,Servlets,XML,JMS,Java IDL,JTS,JTA,JavaMail,JAF。
在大洋彼岸还有一家公司,准确来说它是一个组织,专门搞开源的,叫Apache。这家公司搞出了一个叫Tomcat的服务器。这个名字取得真好啊。中国有个词叫三脚猫,专门来吐槽别人功夫不到家。巧了,Tomcat也没完全实现JavaEE规范。13种核心技术,Tomcat只实现了俩:Servlet和JSP。而其他服务器比如JBoss、Weblogic啥的都是完全支持的。所以人们往往更愿意叫Tomcat为轻量级的服务器,也有叫它Servlet/JSP容器的。
听到这,你不禁大叫:不对啊,我记得自己写的程序里有用到JDBC啊,还可以运行哩!
啊,那是因为你导了JDBC包...但是你安装了Tomcat后另外导过Servlet/JSP的包吗?没有嘛!人家实现了Servlet/JSP规范,都整到自己源码里了。
说到这,我也是泪流满面。因为我才发现自己也是个三脚猫。上面“Java13绝技”我特么也就学过JDBC/XML/JSP/Servlet...所以我更愿意称自己是JavaWeb程序员,而不是JavaEE程序员。JavaEE其实很重,我拿不动,打扰了。
我们为什么需要服务器?
在我看来,服务器最本质的作用有两个:
- 将资源对外暴露
- 配合各种传输协议进行响应输出
假设现在有个问题:
给你两台电脑,不通过蓝牙/QQ/微信,也不通过网盘或USB等可移动设备做中介,你要如何把一张图片从一台电脑传到另一台?
听到这个问题,我估计大部分非科班的朋友都要懵。因为如果后期没有刻意去学习计算机网络,我们对于网络的了解基本仅限于基础班4小时的“网络编程”讲解。而大家平时又太习惯地址栏键入"www.baidu.com",无脑一回车就上网冲浪了。现在突然让你去访问隔壁的电脑,确实有点束手无策。
解决这个问题的方法可能有多种,这里介绍其中一种:通过服务器访问。
请先了解以下三个概念:
- IP:电子设备(计算机)在网络中的唯一标识,一个IP对应一台实体电脑
- 端口:应用程序在计算机中的唯一标识,一个端口只能被唯一程序占用
- 传输协议:数据传输的规则
中国有14亿人口,每个人都有唯一的身份标识:身份证,用以精确定位某个个体。同样的,网络上有几十亿台电脑,每台电脑都有自己的一串特有IP(不同局域网内可以相同),也就是说一个IP代表一台特定的实体电脑。比如《唐伯虎点秋香》中华安的编号是9527,而华府的管家从不叫他名字,而是直接喊“9527”。因为“9527”就是华安。
虽然根据IP可以精准定位一台电脑,但是还不足以让我们访问这台电脑。就好比你知道了我的门牌号,但是我没给你开门。所谓的门,就是一个端口,而端口的背后是应用程序。
一般来说,一个端口可以定位一个软件,但一个软件可以占用多个端口(你家的门,只属于你家,但可以有前后门)。在现实生活中,你家的门如果被别人占用了或者堵了,客人(请求)就进不去了。而在程序中,如果有两个程序的端口相同,就会发生端口冲突,也就是所谓的“端口占用”。端口占用的后果往往是程序无法启动,更遑论运行。
关于端口,再举个例子:
微信和QQ都是腾讯公司的,你的电脑上同时装了这两个软件。为什么我用QQ给你发消息,你的微信收不到?正常人看起来很傻的问题,实际上并不是那么简单。
每个应用程序都有自己的端口号(可能有多个),它们一旦运行,就要去监听这些端口。每个程序都是电脑的囚犯,看不到外面的世界,而端口就是给这些囚犯送饭的窗口。应用程序们整天躲在电脑里盯着自己的端口们,祈求着别的计算机来访时能送个大鸡腿(Request请求)。
其实QQ这些软件属于C/S架构,已经为我们屏蔽了太多底层,什么IP、端口全部都是自动封装的。相比来说,B/S架构更直观一些。比如用浏览器访问百度:
想要访问一台服务器,必须知道它的IP。但我们人类不擅长记忆长串数字,于是人类搞了所谓的域名来指向IP。但实际请求时,最终还是要换算成IP去访问。总得来说有两种换算的途径:1.本机的hosts文件 2.DNS服务器
不知道有没有细心的朋友注意到了下面的细节:
即使DNS解析域名得到对应的IP后,Request请求里还是会带上host。为什么?
因为:域名!=IP。
实际上一个IP可以对应多个域名。也就是说一台实体服务器(大铁柜),理论上可以有多个域名(虚拟主机)。实体服务器和网站是两个概念。IP只是对应实体服务器,而域名对应具体的网站。
比如上面百度服务器,虽然看起来115.239.210.27这个IP完全等同于http://www.baidu.com,但也有可能这个IP对应的服务器上配置了两个虚拟主机:www.baidu.com和tieba.baidu.com。所以即使找到了IP对应的服务器实体,Request请求还是要带上host主机名,以确定是哪个虚拟主机。
通过DNS解析域名得到IP,然后根据IP+host找到服务器
另外,如果两个域名对应同一个IP,那么必须设置其中一个域名为默认的,不然同一台服务器有两个虚拟主机,我该访问谁?
已经知道IP,就无需DNS解析,可直接访问服务器。若这个IP对应的服务器有两个虚拟主机,而用户Request请求行中又没有指定host,则会访问默认主机(因此服务器要事先指定默认主机!Tomcat默认localhost)
最后,再用Tomcat举个例子。比如,现在我有一台笔记本电脑(一个实体服务器),它的本机IP是192.168.112.1,我在上面装了Tomcat。如果Tomcat不改动配置,则默认只有一个虚拟主机localhost(默认主机)。接着我开发了一个JavaWeb程序demo1部署到Tomcat,然后我同事在浏览器输入下方地址
192.168.112.1:8080/demo1/index.html
访问我的电脑。虽然没有带host,但是localhost是默认的,于是访问它。
最后必须解释的是,上面的百度服务器只是举个例子,实际上百度搜索和百度贴吧的IP是不同的,也就是说它们不在同一台服务器上。通常来说,一个IP对应一台服务器,服务器上只有一个主机。拿到IP基本就可以确定要访问哪个网站。
3个容易混淆的小概念
我们经常开口闭口“服务器”、“服务器”的,其实“服务器”是个很容易引发歧义的概念,我能想到的就有3点:
- 软件概念的服务器和硬件概念的服务器
软件概念上,只要是一台硬件配置正常、装有操作系统、插着电能上网,并且安装特定软件的电脑,都可以称为服务器。比如你要学习数据库了,于是你装了MySQL服务端,那么此时你的电脑就是一个MySQL服务器。然后你又装了SVN服务端,那么此时你的电脑既是MySQL服务器,又是SVN服务器。Tomcat服务器同理。
硬件概念上,服务器本质上也是一台电脑,只不过配置高的同时长相丑了点,基本就是一个冰冷的大铁柜。我们的笔记本电脑既能看片又能玩游戏,而它们基本上专机专用。
- Web服务器?Web容器?
其实,Tomcat服务器 = Web服务器 + Servlet/JSP容器(Web容器)。
Web服务器的作用是接收客户端的请求,给客户端作出响应。但是很明显,服务器不止静态资源呀,所以客户端发起请求后,如果是动态资源,Web服务器不可能直接把它响应回去(比如JSP),因为浏览器只认识静态资源。所以对于JavaWeb程序而言,还需要JSP/Servlet容器,JSP/Servlet容器的基本功能是把动态资源转换成静态资源。我们JavaWeb工程师需要使用Web服务器和JSP/Servlet容器,而通常这两者会集于一身,比如Tomcat。
Web服务器接收、响应客户端请求,Web容器装载Servlet/JSP,让它们去处理动态资源[来自尚硅谷]
所以刚才我们画的百度服务器,其实细节还可以更丰满些:
百度服务器细节图
访问百度完整的流程
- 我们开发的Web应用都是半成品!
我们写代码的时候,都知道相同代码最好抽取成公共方法以复用。现在我们来想一想,上百上千的Web应用有什么共性吗?首先,资源肯定不同,无法抽取。比如优酷主打视频,知乎基本都是文字。其次,业务也肯定不同,比如百度主要是搜索,淘宝是电商。但是有一点是一样的,这些网站都需要“接收用户请求”+“响应用户请求”。
嗯?桥多麻袋!!这两个概念,好像哪里见过!不错,就是上面的Web服务器。仔细回想一下,我们开发JavaWeb时,你操心过如何接收HTTP请求和响应HTTP请求吗?显然没有嘛!因为你一直忙着debug。
所以,我们用Java开发的Web应用只是一个半成品,类似于一个插件,而服务器则像一个收发器:
什么是动态资源?
其实对于何谓动态资源,我也没有很精准的概念。要讲清楚一个东西是什么,有时是比较难的事。不如先说它不是什么。
首先,动态资源不等同于动态页面。所谓动态页面,就是页面会动,而会动的页面不一定是动态资源。比如我可以用JQuery执行一段代码,让一个Div不断放大缩小,但是很显然它还是一个HTML页面。
所谓动态资源,其实最显著的特征就是它能动态地生成HTML!比如JSP。动态资源有个“特色”:它的数据是“可拼装”的、而且“可以随时间变化”。下面用号称可以抗住8个明星同时出轨的新浪服务器举个例子:
突然,新浪《花花世界》专栏的小编发现,原来和bravo1988有绯闻的不是刘亦菲,而是佟丽娅,于是打开专栏做了修改:
此时,粉丝们再次打开《花花世界》专栏,看到的就是:佟丽娅深夜买醉bravo。
上面这个例子很好地说明了动态资源(JSP)的两个特性:
- 可拼装:${name}"深夜买醉bravo"
- 随时间变化:刘亦菲→佟丽娅
那么为什么说HTML就是静态资源呢?我也可以修改HTML页面使它发生改变啊!很好,很有想法。那么请小编先学会Linux,然后远程连接服务器进入到Tomcat目录下修改吧。
动态资源和静态资源虽然都在服务器里,但是动态资源包含变量(“可拼装”特性),而变量维系着数据库和程序之间的联系。
如果把JSP比作电子广告牌,变量比作一根电线,而电线连接着一台电脑(数据库服务器)。那么只要电脑上重新编辑文本,广告牌的内容也会变,此谓动态。而静态资源就像一张布告,当初写什么就是什么,任他风吹雨打,都不会再改变了。
Tomcat架构
Tomcat的目录结构就不再多说,每个机构的培训视频都会强调。
先看一下我的Tomcat目录(免安装版),我把它放在了F盘的develop文件夹下:
重点介绍一下Tomcat的架构:
嗯?这个connector的画法,好像哪里见过?
其实这张图,应该结合Tomcat的一个配置文件(Server.xml)来看:
简略解释一下xml里的配置:
- Server.xml文件中的配置结构和Tomcat的架构是一一对应的。根目录是
,代表服务器, 下面有且仅有1个 ,代表服务。 下有两个 ,代表连接(需要的话可以再加)。
其实这个Connector就是我们在上面讨论百度服务器时画过的端口。大家可以看到Tomcat默认配置了两个端口,一个是HTTP/1.1协议的,一个是AJP/1.3协议(我也不知道是啥)。前者专门处理HTTP请求。- 当我们在浏览器输入"http://localhost:8080/demo/index.html"时,浏览器是以HTTP协议发送的,当这个请求到了服务器后,会被识别为HTTP类型,于是服务器就找来专门处理HTTP的Connector,它的默认端口正是上门Server.xml配置的8080。
- 与Connector平级的还有个
(Tomcat引擎),也就是说 有两个孩子,小儿子是 ,大儿子是 。Connector的作用说穿了就是监听端口,如果用户访问地址是“localhost:8080/xx/xx”,那就由监听8080端口的Connector负责,如果是"https://www.baidu.com",那么就是443端口处理。其实Connector也不处理实际业务,它只是个孩子。但它会负责把客人(请求)带到哥哥Engine那,然后Engine会处理。 下面有个Host,代表主机。
配置介绍到这里,要先停一下,讲个故事:
从前有个国家,叫The United States of Tomcat。
国王Service是个爱猫的人,家庭和谐美满。他立了一个太子,叫做Engine。
另外还有还几个很小的儿子,不过都是亲王(Connector),未来可能还要再生几个Connector。
由于Tomcat国实在太小了,全城上下就几个人,所以亲王Connector们被派去守城门。
为了让太子得到锻炼,早日继承衣钵,国王Service告诉亲王儿子们:他国使者若来拜访,你们就带他去你们哥哥Engine那去,他会处理一切。
Engine贵为太子,有好几处府邸,比如HOST1,HOST2,未来可能再建几座府邸(新建虚拟主机),但是Engine说了,我一般都在localhost待着,来这找我便可。
每一座府邸里,都有好几个厢房(Context:我们开发的Web应用)。Engine会根据来访使者的通关文牒(localhost:8080/myWeb/index.html)安排他们去哪个房间(myWeb),拿什么礼品(index.html)。
最后结合文件目录再看一次Tomcat架构:
对JavaWeb(6):浅谈JSP的补充
有两个Servlet很重要,但是在“浅谈JSP”中没有提到。
- DefaultServlet:该出手时就出手
TestDefaultServlet
上面案例中,我新建一个动态web工程,只是写了一个HTML,没有编写Servlet,甚至一句Java代码都没写。但是启动Tomcat后我却可以通过浏览器访问到刚才编写的haha.html。这是为何?
我们知道,对于像Tomcat这样的Servlet容器来说,任何一个请求的背后肯定有个Servlet在默默处理:
所以这次也不例外,肯定也有对应的Servlet处理了本次请求。既然不是我们写的,那只能是Tomcat提供的。查看Tomcat下的conf目录,除了我们熟悉的Server.xml,还有个web.xml。
不错,我们每个动态web工程都有个web.xml,而conf里的这个,是它们的“老爹”。它里面的配置,如果动态web工程没有覆盖,就会被“继承”下来。我们会发现,conf/web.xml里配置了一个DefaultServlet:
DefaultServlet的作用:最低级匹配,当没有对应的Servlet处理当前请求时,才轮到它处理。要么找到并响应请求的资源,要么给出404页面。
- JspServlet:JSP的卸妆师傅
我们都知道JSP是“化了浓妆”的Servlet,但是好不容易伪装成了一个JSP,是谁帮它卸妆的呢?另外,大家仔细想想,一般来说JavaWeb阶段我们访问资源有三种“形式”:
localhost:8080/demo/AServlet:很明显,我们手动写了一个AServlet处理它
localhost:8080/demo/haha.html:虽然我们没写,但是Tomcat自己准备了DefaultServlet
localhost:8080/demo/index.jsp:我擦,谁来处理?
对呀,细思恐极,这*.jsp的资源,谁来处理?其实就是JspServlet。它的作用简而言之就是:
- 首先,根据请求路径找到JSP
- 然后,将它“翻译成”Servlet
刚才带大家看conf/web.xml时,我把它隐藏了,因为同时讲解DefaultServlet和JspServlet会比较乱。强烈建议大家现在暂停一下,打开本机的Tomcat找到conf/web.xml看一下。下面是JspServlet的配置:
所以最后总结一下Tomcat处理请求的几种方式:
动手实现"Tomcat"
最后,还有个很无聊的问题留给大家思考:JavaSE阶段,我们无论做什么,都是上来先敲main()。学了JavaWeb后,我想问问,你有多久没敲main()了?她去哪了呢?
动手实现Tomcat(黑马公开课):链接见评论区置顶。