目录
- @[TOC](目录)
- 1. 前言
- 2. 逻辑推导
- 3. 关键问题:
- 4. servlet
- 5. 问题思考
- 6. 图解与说明
- 7. 优化
- servlet缓存池
- servlet线程安全
- 8. 应用联想
- 9. 推荐阅读
- 10. 关键信息记录
1. 前言
阅读之前,请先理解以下概念:
- web服务器:如tomcat,它是web应用的载体。由于我们平时老是说“客户端与服务端进行交互”,然后容易误以为我们写的web应用就是服务器,这种理解是错误的。web服务器相当于一个容器,装载着我们写的web应用,与浏览器进行交互的是web服务器,处理逻辑的是我们的web应用。
- webApp(web应用):
- servlet:web应用中处理业务逻辑的类(或者说处理request返回response的类)
- HTTP:这个自行百度,主要理解三次握手、请求与响应
2. 逻辑推导
从已有知识推导服务器逻辑: start–>
- 浏览器与服务器直接通过HTTP协议进行数据传输==》
- HTTP协议分为请求报文和响应报文,浏览器发送请求报文,服务器返回响应报文==》
- 请求报文中关键数据包括:请求路径(统一资源定位符),请求体(发送的数据包);响应报文中关键数据包括:响应体(响应的数据包)==》
- 所以,大概逻辑如下==》
- 浏览器通过IO流发送请求资源路径和请求数据包给服务端==》
- 服务端获取IO流,得到请求路径和请求数据包==》
- 根据请求路径找到对应的请求资源,并处理响应的数据包==》
- 将上述过程的结果封装到响应体中,通过IO流返回。
end<–
3. 关键问题:
- 如何利用统一资源定位符找到对应的服务器资源?
- IO流传输的是字节流,html页面最终是怎样渲染到浏览器的?
回答: 如果是静态的html页面,则直接根据uri找到服务器中的静态页面,然后通过IO流返回。 直接返回的是字符串,这个解析过程,只要带上对应的响应协议,浏览器会自动渲染。
4. servlet
问:如果是xxx.html结尾,显然只需要读取服务器html资源然后响应即可,但是对于?username=xx&psw=xxx这样的uri,需要业务逻辑来处理,该如何解决?
答:显然,这种写法也是HTTP规范之一,因此,也可以通过遵守规范而正确解析。服务器只做转发功能(web),而逻辑功能,则是服务来完成(webApp),这类webApp,就是servlet。(server Applet的缩写) 关键逻辑如下:
- 浏览器发送的/project/login?username=xx&psw=xxx,到达web服务器
- web服务器根据HTTP协议,首先解析uri问号的前半部分/project/login,然后建立uri与servlet的映射关系(这就是web.xml的由来),并通过解析web.xml反射得到servlet,然后解析得到参数键值对并封装到request中(同时初始化响应的response请求头,避免由webApp来拼接响应头协议)
- 通过servlet.service(request,response)调用webapp中的servlet,执行相关的do方法。
关键知识: 这里面有两大规范:HTTP协议、servlet规范 HTTP协议制定浏览器与服务器之间的交互规则,体现在HTTP请求与HTTP响应上。 servlet规范制定了服务器与webApp之间的交互规则。体现在:webApp的servlet实现servlet规范,然后在web服务器中,直接通过servlet接口调用具体实现。
5. 问题思考
为什么需要web.xml文件(或者相关的注解)? web.xml文件中定义了uri与servlet的映射关系,如果不在配置文件中定义,那就只能在web服务器启动类的解析过程中预先定义所有uri与servlet的关系映射,这显然不可能。只能通过反射获取。
6. 图解与说明
- 端口开发JDK 的ServerSocket已提供相应方法
- socket网络编程主要包含端口监听、请求监听、解析输入流得到请求、构建输出流进行响应。
- 线程调度器即线程池,每个请求都会从池中拿一个线程进行执行,可以用ExecutorService创建
- servlet需要解析web.xml文件得到url与servlet映射关系,然后解析浏览器传递过来的url,用反射获取对应的servlet进行执行。
7. 优化
servlet缓存池
如果解析完url之后就直接根基web.xml中的映射关系反射获取servlet,这样必定出现servlet爆炸(每个相同的url都会创建一个servlet)。 因此需要建立一个servlet缓存池。 逻辑: 维护一个url:servlet缓存池。 请求过来之后,先从池中拿,拿不到,再反射创建一个,同时放到池中。
servlet线程安全
对于相同的url,servlet都是同一个,即会有多个用户同时使用同一个servlet对象的情况,自然可能存在线程安全。
一般只用一种方式进行规范,那就是不要定义会变化的servlet的成员变量。 非要使用的话,请用TheadLocal
8. 应用联想
springmvc的dispatchservlet也是一个servlet,只不过他不直接处理请求与响应,而是做请求与响应的中转站。
springboot中的web开发,controller也有线程安全问题,原因与servlet相同,解决办法也相同。
9. 推荐阅读
TomCat架构分析
Tomcat连接器
Tomcat配置文件详解(配置文件基本展现了Tomcat的架构,建议重视)
servlet工作原理
另外,还可以打开百度图片,搜索 “tomcat架构图” “tomcat时序图” 等图。
10. 关键信息记录
- 在初始化过程中,tomcat大量使用观察者模式,以便于实现链式初始化
- 容器层级之间基于pipeline(管道)和valve(阀门)的形式处理request(类似过滤器、责任链)相当于提供了可在每层容器之间设置过滤器的功能
- tomcat没有遵循双亲委派类加载机制,而是自定义类加载顺序。原因:tomcat本身是一个java程序(即便它运行着多个不同的java项目),如果还是按照双亲委派,则多个不同的项目可能发生冲突,比如说不同项目中相同的类名。因此,基本要求就是不同的webapp应该用各自不同的类加载器。 (延伸阅读)