tomcat,servlet容器,web容器。。。

参考文章:

  1. 什么是servlet容器:https://blog.csdn.net/yw_1207/article/details/78706701
  2. 理解Servlet和Servlet容器、Web服务器等概念:
    https://blog.csdn.net/lz233333/article/details/68065749
  3. (主)Servlet源码
    https://blog.csdn.net/qq_19782019/article/details/80292110

1. 什么是web服务器

在了解servlet容器前,必须先了解web服务器

  1. 定义:web服务器使用http协议来传输数据。服务器完成的工作就是发送网页到客户端,传输过程遵循http协议,他指明了请求(request)消息和响应(response)消息的格式。

在这里插入图片描述

在这里我们可以发现,只靠web服务器,客户端只能向服务器请求静态网页,如果用户想要根据自己的输入来读取网页,也就是动态网页,是无法满足的,这时候,就需要我们的servlet容器了

例如tomcat:

在这里插入图片描述

2. 什么是servlet容器

  1. 定义:Servlet容器也叫做Servlet引擎,是Web服务器或应用程序服务器的一部分,用于在发送的请求和响应之上提供网络服务,解码基于 MIME的请求,格式化基于MIME的响应。Servlet没有main方法,不能独立运行,它必须被部署到Servlet容器中,由容器来实例化和调用 Servlet的方法(如doGet()和doPost()),Servlet容器在Servlet的生命周期内包容和管理Servlet。在JSP技术 推出后,管理和运行Servlet/JSP的容器也称为Web容器。

3. 什么是servlet

  1. 定义:是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。

    Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

  2. 工作模式:
    客户端发送请求至服务器
    服务器启动并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器
    服务器将响应返回客户端

  3. Servlet 的工作原理

    Servlet接口定义了Servlet与servlet容器之间的契约。这个契约是:Servlet容器将Servlet类载入内存,并产生Servlet实例和调用它具体的方法。但是要注意的是,在一个应用程序中,每种Servlet类型只能有一个实例

    用户请求致使Servlet容器调用Servlet的Service()方法,并传入一个ServletRequest对象和一个ServletResponse对象。ServletRequest对象和ServletResponse对象都是由Servlet容器(例如TomCat)封装好的,并不需要程序员去实现。

  4. ServletRequest,ServletResponse,ServletContext对象讲解

    ServletRequest中封装了当前的Http请求,因此,开发人员不必解析和操作原始的Http数据。ServletResponse表示当前用户的Http响应,程序员只需直接操作ServletResponse对象就能把响应轻松的发回给用户。

    对于每一个应用程序,Servlet容器还会创建一个ServletContext对象。这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象。

生命周期:

  1. init()在servlet生命周期的初始化阶段被调用,传递了一个实现了javax.javax.servlet.ServletConfig接口的对象,使得servlet能够从web application中获取初始化参数。
  2. servlet每接收一个请求,就会调用service()方法。每个请求的处理都在独立的线程中进行,service()方法判断请求的类型(doget(),dopost()),并把它发给相应的方法进行处理。
  3. 当需要销毁servlet对象时,就调用destroy()方法,该方法释放被占用的资源。

servlet容器和web服务器如何处理一个请求。

  1. web服务器接受到http请求
  2. web服务器将请求转发给servlet容器
  3. 如果容器中不存在所需的servlet,容器就会检索servlet,将其加载到容器的地址空间中。
  4. 容器调用servlet的init()方法对servlet进行初始化(该方法只会在servlet第一次被载入时调用)
  5. 容器调用servlet的service()方法来处理http请求:读取请求中的数据,创建一个响应,servlet会被保留在容器的地址空间中,继续处理其他的http请求
  6. web服务器将动态生成的结果返回到正确的地址。

在这里插入图片描述
在这里插入图片描述

httpService对象如何转到doget||dopost等方法处理

首先我们看源码

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            this.doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader("If-Modified-Since");
            if (ifModifiedSince < lastModified) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }
}

接下来我们再看看service方法是如何工作的,我们会发现在service方法中还是没有任何的服务逻辑,但是==却在解析HttpServletRequest中的方法参数,并调用以下方法之一:doGet,doPost,doHead,doPut,doTrace,doOptions和doDelete。==这7种方法中,每一种方法都表示一个Http方法。doGet和doPost是最常用的。所以,如果我们需要实现具体的服务逻辑,不再需要覆盖service方法了,只需要覆盖doGet或者doPost就好了。

总之,HttpServlet有两个特性是GenericServlet所不具备的:

1.不用覆盖service方法,而是覆盖doGet或者doPost方法。在少数情况,还会覆盖其他的5个方法。

2.使用的是HttpServletRequest和HttpServletResponse对象。

request乱码的解决方法

在前面我们讲过,在service中使用的编码解码方式默认为:ISO-8859-1编码,但此编码并不支持中文,因此会出现乱码问题,所以我们需要手动修改编码方式为UTF-8编码,才能解决中文乱码问题,下面是发生乱码的具体细节:

在这里插入图片描述

解决post提交方式的乱码:request.setCharacterEncoding(“UTF-8”);

解决get提交的方式的乱码:

parameter = newString(parameter.getbytes(“iso8859-1”),“utf-8”);

Response的乱码问题

在这里插入图片描述
原因:response缓冲区的默认编码是iso8859-1,此码表中没有中文。所以需要更改response的编码方式:
在这里插入图片描述
通过更改response的编码方式为UTF-8,任然无法解决乱码问题,因为发送端服务端虽然改变了编码方式为UTF-8,但是接收端浏览器端仍然使用GB2312编码方式解码,还是无法还原正常的中文,因此还需要告知浏览器端使用UTF-8编码去解码。

在这里插入图片描述

上面通过调用两个方式分别改变服务端对于Response的编码方式以及浏览器的解码方式为同样的UTF-8编码来解决编码方式不一样发生乱码的问题。

response.setContentType(“text/html;charset=UTF-8”)这个方法包含了上面的两个方法的调用,因此在实际的开发中,只需要调用一个response.setContentType(“text/html;charset=UTF-8”)方法即可。

在这里插入图片描述
在这里插入图片描述

Response的工作流程

在这里插入图片描述

http请求连接

  1. http向服务器发出连接请求
  2. 新建tcp连接
  3. 服务器端发送http消息
  4. 服务器端接受到消息后,发回一个ack消息,然后发送html包
  5. 浏览器回复相应的ack包,解析html代码,并请求资源(js等)
  6. 服务器发送应答码
  7. 服务器关闭连接()

独立的Servlet容器

当我们使用基于Java技术的Web服务器时,Servlet容器作为构成Web服务器的一部分而存在。然而大多数的Web服务器并非基于Java,因此,就有了下面两种Servlet容器的工作模式。

2)进程内的Servlet容器
Servlet容器由Web服务器插件和Java容器两部分的实现组成。Web服务器插件在某个Web服务器内部地址空间中打开一个 JVM(Java虚拟机),使得Java容器可以在此JVM中加载并运行Servlet。如有客户端调用Servlet的请求到来,插件取得对此请求的控 制并将它传递(使用JNI技术)给Java容器,然后由Java容器将此请求交由Servlet进行处理。进程内的Servlet容器对于单进程、多线程 的服务器非常适合,提供了较高的运行速度,但伸缩性有所不足。

3)进程外的Servlet容器
Servlet容器运行于Web服务器之外的地址空间,它也是由Web服务器插件和Java容器两部分的实现组成的。Web服务器插件和Java容 器(在外部JVM中运行)使用IPC机制(通常是TCP/IP)进行通信。当一个调用Servlet的请求到达时,插件取得对此请求的控制并将其传递(使 用IPC机制)给Java容器。进程外Servlet容器对客户请求的响应速度不如进程内的Servlet容器,但进程外容器具有更好的伸缩性和稳定性。

相关面试题

1. Tomcat Connector的三种运行模式

https://blog.csdn.net/peerless_hero/article/details/53438808

1.1 bio(同步并阻塞)

bio是阻塞式io操作,tomcat默认就是在bio下运行的。

关于”阻塞式”的理解,我们回忆起org.apache.catalina.core.Catalina类中的await()方法,在执行ServerSocket.accept()方法后Tomcat容器会一直阻塞到有客户端连接才会返回。每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。

我先后通过两个不同的浏览器页面输入”localhost:8005/request1”和”localhost:8005/request2”,控制台打出如下信息:

十二月 02, 2016 8:58:57 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /request1 HTTP/1.1' received
十二月 02, 2016 8:59:17 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command '' received
十二月 02, 2016 8:59:33 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /request2 HTTP/1.1' received

bio会有如下的缺点:
1.当客户端多时,会创建大量的处理线程。每个线程都要占用栈空间和一些CPU时间。
2.阻塞可能带来频繁的上下文切换,而大部分的上下文切换是无意义的。
就一般而言,bio模式是三种运行模式中性能最低的一种。

1.2NIO(同步非阻塞)

  1. nio定义: nio(non-blocking I/O)是非阻塞I/O操作。nio是一个基于缓冲区并能提供非阻塞I/O操作的Java API,它拥有比bio更好的并发运行性能。

  2. 如何修改为nio:要让Tomcat以nio模式来运行也比较简单,我们只需要修改下server.xml文件:
    在这里插入图片描述

NIO的工作原理包括:

1.由一个专门的线程来处理所有的 I/O 事件、并负责分发
2.事件驱动机制,而不再同步地去监视事件。
3.线程之间通过 ==wait,notify ==等方式通讯。保证每次上下文切换都是有意义的,减少无谓的线程切换。

NIO采用了==双向通道(channel)==进行数据传输,而不是单向的流(stream)。在通道上我们可以注册指定的事件,一共有以下四种事件:

1.服务端接收客户端连接事件OP_ACCEPT(16)
2.客户端连接服务端事件 OP_CONNECT(8)
3.读事件 OP_READ(1)
4.写事件 OP_WRITE(4)

服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道上的事件。以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,BIO这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达则处理这些事件;如果没有感兴趣的事件到达则处理线程会一直阻塞,直到感兴趣的事件到达为止。

1.3APR(异步非阻塞IO)

apr(Apache portable Run-time libraries/Apache可移植运行库)是Apache HTTP服务器的支持库。在apr模式下,Tomcat将以JNI(Java Native Interface)的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大提高Tomcat对静态文件的处理性能。Tomcat apr是在Tomcat上运行高并发应用的首选模式。

如果Tomcat不在apr模式下运行,在启动Tomcat的时候,我们可以在日志信息中看到类似如下信息:

十二月 02, 2016 8:24:09 下午 org.apache.catalina.core.AprLifecycleListener init
信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: 

而要让Tomcat以apr模式运行

对于Tomcat 7.0.30开始向后的版本,只需要再次修改protocol为”org.apache.coyote.http11.Http11AprProtocol”即可。而对于Tomcat 7.0.30之前的版本,还需要以下三个组件的支持:
1.APR library[APR库]
2.JNI wrappers for APR used by Tomcat (libtcnative)[Windows操作系统上一个名为tcnative-1.dll的动态链接库文件]
3.OpenSSL libraries[OpenSSL库]

Tomcat有几种部署方式?

在Tomcat中部署Web应用的方式主要有如下几种:

  1. 利用Tomcat的自动部署。

把web应用(war文件)拷贝到webapps目录。Tomcat在启动时会加载目录下的应用,并将编译后的结果放入work目录下。

  1. 使用Manager App控制台部署。

在tomcat主页点击“Manager App” 进入应用管理控制台,可以指定一个web应用的路径或war文件。

  1. 修改conf/server.xml文件部署。

修改conf/server.xml文件,增加Context节点可以部署应用。

  1. 增加自定义的Web部署文件。

在conf/Catalina/localhost/ 路径下增加 xyz.xml文件,内容是Context节点,可以部署应用。

tomcat容器是如何创建servlet类实例?用到了什么原理?

当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对 xml文件进行解析,并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过 反射的方式实例化。(有时候也是在第一次请求时实例化)
在servlet注册时加上1如果为正数,则在一开始就实例化,如果不写或为负数,则第一次请求实例化。

Tomcat顶层架构小结

Tomcat中只有一个Server,一个Server可以有多个Service,一个Service可以有多个Connector和一个Container;

Server掌管着整个Tomcat的生死大权;

Service 是对外提供服务的;

Connector用于接受请求并将请求封装成Request和Response来具体处理;

Container用于封装和管理Servlet,以及具体处理request请求;

4个子容器的作用分别是:
Engine:引擎,用来管理多个站点,一个Service最多只能有一个Engine;
Host:代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点;
Context:代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml文件;
Wrapper:每一Wrapper封装着一个Servlet;

Container如何处理请求的

Container处理请求是使用Pipeline-Valve管道来处理的!(Valve是阀门之意)

Pipeline-Valve是责任链模式,责任链模式是指在一个请求处理的过程中有很多处理者依次对请求进行处理,每个处理者负责做自己相应的处理,处理完之后将处理后的结果返回,再让下一个处理者继续处理。

发布了36 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/s_xchenzejian/article/details/102790354