《深入拆解Tomcat & Jetty》笔记

极客时间《深入拆解Tomcat & Jetty》笔记

03_你应该知道的Servlet规范和Servlet容器

1.HTTP服务器怎么知道要调用哪个Java类的哪个方法呢。最直接的做法是在HTTP服务器代码里写一大堆if else逻辑判断,HTTP服务器的代码跟业务逻辑耦合在一起了,如果新加一个业务方法还要改HTTP服务器的代码。

2.那该怎么解决这个问题呢?面向接口编程是解决耦合问题的法宝,于是有一伙人就定义了一个接口,各种业务类都必须实现这个接口,这个接口就叫Servlet接口,有时我们也把实现了Servlet接口的业务类叫作Servlet。

3.但是这里还有一个问题,对于特定的请求,HTTP服务器如何知道由哪个Servlet来处理呢?Servlet又是由谁来实例化呢?显然HTTP服务器不适合做这个工作,否则又和业务类耦合了。于是,还是那伙人又发明了Servlet容器,Servlet容器用来加载和管理业务类。HTTP服务器不直接跟业务类打交道,而是把请求交给Servlet容器去处理,Servlet容器会将请求转发到具体的Servlet,如果这个Servlet还没创建,就加载并实例化这个Servlet,然后调用这个Servlet的接口方法。因此Servlet接口其实是Servlet容器跟具体业务类之间的接口

Web应用:一般来说,我们是以Web应用程序的方式来部署Servlet的,而根据Servlet规范,Web应用程序有一定的目录结构,在这个目录下分别放置了Servlet的类文件、配置文件以及静态资源,Servlet容器通过读取配置文件,就能找到并加载Servlet。

Servlet规范里定义了ServletContext这个接口来对应一个Web应用。Web应用部署好后,Servlet容器在启动时会加载Web应用,并为每个Web应用创建唯一的ServletContext对象。你可以把ServletContext看成是一个全局对象,一个Web应用可能有多个Servlet,这些Servlet可以通过全局的ServletContext来共享数据,这些数据包括Web应用的初始化参数、Web应用目录下的文件资源等。由于ServletContext持有所有Servlet实例,你还可以通过它来实现Servlet请求的转发。

扩展机制

1.Filter过滤器的工作原理:Web应用部署完成后,Servlet容器需要实例化Filter并把Filter链接成一个FilterChain。当请求进来时,获取第一个Filter并调用doFilter方法,doFilter方法负责调用这个FilterChain中的下一个Filter。

2.Listener监听器:当Web应用在Servlet容器中运行时,Servlet容器内部会不断的发生各种事件,如Web应用的启动和停止、用户请求到达等。 Servlet容器提供了一些默认的监听器来监听这些事件,当事件发生时,Servlet容器会负责调用监听器的方法。当然,你可以定义自己的监听器去监听你感兴趣的事件,将监听器配置在web.xml中。比如Spring就实现了自己的监听器,来监听ServletContext的启动事件,目的是当Servlet容器启动时,创建并初始化全局的Spring容器。

Filter是干预过程的,它是过程的一部分,是基于过程行为的。

Listener是基于状态的,任何行为改变同一个状态,触发的事件是一致的。

Servlet本质上是一个接口,实现了Servlet接口的业务类也叫Servlet。Servlet接口其实是Servlet容器跟具体Servlet业务类之间的接口。Servlet接口跟Servlet容器这一整套规范叫作Servlet规范,而Servlet规范使得程序员可以专注业务逻辑的开发。

05-08 Tomcat系统架构 & 启动流程

连接器负责外部交流,容器负责内部处理。连接器处理Socket通信和应用层协议的解析,得到Servlet请求;而容器则负责处理Servlet请求。

1.连接器:EndPoint接收到Socket连接后,生成一个SocketProcessor任务提交到线程池去处理,SocketProcessor的Run方法会调用Processor组件去解析应用层协议,Processor通过解析生成Request对象后,会调用Adapter的Service方法。

2.适配器:Tomcat设计了多层容器是为了灵活性的考虑,灵活性具体体现在一个Tomcat实例(Server)可以有多个Service,每个Service通过多个连接器监听不同的端口,而一个Service又可以支持多个虚拟主机。一个URL网址可以用不同的主机名、不同的端口和不同的路径来访问特定的Servlet实例。请求的链式调用是基于Pipeline-Valve责任链来完成的,这样的设计使得系统具有良好的可扩展性,如果需要扩展容器本身的功能,只需要增加相应的Valve即可。

09-10 比较:Jetty架构特点

 

1.Tomcat在整体上跟Jetty很相似,它们的第一个区别是Jetty中没有Service的概念,Tomcat中的Service包装了多个连接器和一个容器组件,一个Tomcat实例可以配置多个Service,不同的Service通过不同的连接器监听不同的端口;而Jetty中Connector是被所有Handler共享的。

2.在Tomcat中每个连接器都有自己的线程池,而在Jetty中所有的Connector共享一个全局的线程池。

11-13 阶段一总结

1.组件化及可配置化(面向接口编程、职责链管理组装组件)

 

2.父子组件进行生命周期管理(一是父组件负责子组件的创建、启停和销毁。这样只要启动最上层组件,整个Web容器就被启动起来了;二是把组件状态的转变定义成一个事件,通过观察者模式,创建监听器去监听容器的状态变化去实现相应的动作,这些监听器其实是组件生命周期过程中的“扩展点”。)

3.组件的设计的与实现,Tomcat和Jetty都大量采用了骨架抽象类和模板模式。抽象骨架类实现了一些通用逻辑,并且会定义一些抽象方法,这些抽象方法由子类实现,抽象骨架类调用抽象方法来实现骨架逻辑。(从Java 8开始允许接口有default方法,这样我们可以把抽象骨架类的通用逻辑放到接口中去)

(12-tomcat启动优化方法)

服务接入层:反向代理Nginx;API网关Node.js。

业务逻辑层:Web容器Tomcat、Jetty;应用层框架Spring、Spring MVC和Spring Boot;ORM框架MyBatis;

数据缓存层:内存数据库Redis;消息中间件Kafka。

数据存储层:关系型数据库MySQL;非关系型数据库MongoDB;文件存储HDFS;搜索分析引擎Elasticsearch。

其中每一层都要支持水平扩展和高可用,比如业务层普遍采用微服务架构,微服务之间需要互相调用,于是就出现了RPC框架:Spring Cloud和Dubbo。

除此之外,还有两个非常重要的基础组件:Netty和Zookeeper,其中Netty用于网络通信,Zookeeper用于分布式协调。

14-NioEndpoint组件:Tomcat如何实现非阻塞IO?

UNIX系统下的I/O模型有5种:同步阻塞I/O、同步非阻塞I/O、I/O多路复用、信号驱动I/O和异步I/O。

所谓的I/O就是计算机内存与外部设备之间拷贝数据的过程。我们知道CPU访问内存的速度远远高于外部设备,因此CPU是先把外部设备的数据读到内存里,然后再进行处理。请考虑一下这个场景,当你的程序通过CPU向外部设备发出一个读指令时,数据从外部设备拷贝到内存往往需要一段时间,这个时候CPU没事干了,你的程序是主动把CPU让给别人?还是让CPU不停地查:数据到了吗、数据到了吗…这就是I/O模型要解决的问题。

比如网络数据读取,会涉及两个对象,一个是调用这个I/O操作的用户线程,另外一个就是操作系统内核。一个进程的地址空间分为用户空间和内核空间,用户线程不能直接访问内核空间。当用户线程发起I/O操作后,网络数据读取操作会经历两个步骤:

1.用户线程等待内核将数据从网卡拷贝到内核空间。

2.内核将数据从内核空间拷贝到用户空间。

15-19 io/nio/apr/excutor/websocket代码分析

webSocket

WebSocket的名字里带有Socket,那Socket是什么呢?网络上的两个程序通过一个双向链路进行通信,这个双向链路的一端称为一个Socket。一个Socket对应一个IP地址和端口号,应用程序通常通过Socket向网络发出请求或者应答网络请求。Socket不是协议,它其实是对TCP/IP协议层抽象出来的API。但WebSocket不是一套API,跟HTTP协议一样,WebSocket也是一个应用层协议。为了跟现有的HTTP协议保持兼容,它通过HTTP协议进行一次握手,握手之后数据就直接从TCP层的Socket传输,就与HTTP协议无关了。浏览器发给服务端的请求会带上跟WebSocket有关的请求头,比如Connection: Upgrade和Upgrade: websocket。

jetty使用EatWhatYouKill策略利用cpu缓存来提高性能,据说有8倍。

20-22 阶段二总结

- 对象池(对象池技术可以减少频繁创建和销毁对象带来的成本,实现对象的缓存和复用。如果你的系统需要频繁的创建和销毁对象,并且对象的创建代价比较大,这种情况下,一般来说你会观察到GC的压力比较大,占用CPU率比较高,这个时候你就可以考虑使用对象池了)

- 高效并发

1.首先是减少资源浪费,比如要减少线程的阻塞,因为阻塞会导致资源闲置和线程上下文切换,Tomcat和Jetty通过合理的I/O模型和线程模型减少了线程的阻塞。

2.另外系统调用会导致用户态和内核态切换的过程,Tomcat和Jetty通过缓存和延迟解析尽量减少系统调用,另外还通过零拷贝技术避免多余的数据拷贝。

3.在实际编程过程中要尽量避免使用锁,比如可以用原子变量和CAS操作来代替锁。如果实在避免不了用锁,也要尽量减少锁的范围和强度,比如可以用细粒度的对象锁或者低强度的读写锁。

一次Socket read系统调用的过程:首先CPU在用户态执行应用程序的代码,访问进程虚拟地址空间的用户空间;read系统调用时CPU从用户态切换到内核态,执行内核代码,内核检测到Socket上的数据未就绪时,将进程的task_struct结构体从运行队列中移到等待队列,并触发一次CPU调度,这时进程会让出CPU;当网卡数据到达时,内核将数据从内核空间拷贝到用户空间的Buffer,接着将进程的task_struct结构体重新移到运行队列,这样进程就有机会重新获得CPU时间片,系统调用返回,CPU又从内核态切换到用户态,访问用户空间的数据。

23-26 容器详解(类加载)

- Host容器:热加载与热部署

- Context容器:类加载打破双亲委托,实现优先加载Web应用目录下的类,然后再加载其他目录下的类,这也是Servlet规范的推荐做法。

- Context容器:Tomcat的Context组件为每个Web应用创建一个WebAppClassLoarder类加载器,由于不同类加载器实例加载的类是互相隔离的,因此达到了隔离Web应用的目的,同时通过CommonClassLoader/SharedClassLoader等父加载器来共享第三方JAR包。而共享的第三方JAR包可以通过设置线程上下文加载器来加载特定Web应用的类。

- Context容器:Servlet规范中最重要的就是Servlet、Filter和Listener“三兄弟”。

27-30 阶段三总结

- 对比jetty

- 结合springboot

- springboot中的设计模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值