Tomcat

一、Tomcat系统架构与原理剖析

1.浏览器请求服务器流程

2.Tomcat系统总体架构

2.1Tomcat请求处理大致流程

HTTP服务器接收到请求后将请求转交给Servlet容器,Servlet容器通过Servlet接口调用业务类

2.2Servlet处理流程

当用户请求某个URL资源时,

  1. HTTP服务器将请求信息封装成ServletRequest对象
  2. 根据请求URL和Servlet的映射关系,找到容器中相应的Servlet并调用
  3. 如果Servlet还没被加载,则通过反射机制创建这个Servlet,并调用init方法进行初始化
  4. 接着调用这个Servlet的service方法处理请求,将结果封装成ServletResponse对象
  5. 将ServletResponse对象返回给HTTP服务器,HTTP服务器返回给客户端

2.3Tomcat总体架构 

Tomcat有两个核心组件,连接器和容器

  • 连接器负责对外交流,处理Socket连接,将网络字节流与Request和Response对象进行转
  • 容器负责内部处理,加载和管理Servlet,处理具体请求

3.连接器组件Coyote

Coyote是Tomcat中连接器的组件名称,是对外的接口。客户端通过Coyote与服务器建立连接,发送请求并接收响应

  • 封装了底层的网络通信,负责具体协议(应用层,默认HTTP/1.1)和IO(传输层,默认NIO)相关内容
  • 使Catalina容器与请求协议、IO操作方式解耦
  • 将Socket输入封装成Request对象,进一步封装后交给Catalina容器处理。处理后,将响应通过Response对象写到输出流

组件作用

EndPoint

EndPoint 是 Coyote 通信端点,即通信监听的接⼝,是具体Socket接收和发

送处理器,是对传输层的抽象,因此EndPoint⽤来实现TCP/IP协议的

Processor

Processor 是Coyote 协议处理接⼝ ,如果说EndPoint是⽤来实现TCP/IP协

议的,那么Processor⽤来实现HTTP协议,Processor接收来⾃EndPoint的

Socket,读取字节流解析成Tomcat Request对象,并通过Adapter将其提交到容器处理,Processor是对应⽤层协议的抽象

ProtocolHandler

Coyote 协议接⼝, 通过Endpoint 和 Processor , 实现针对具体协议的处

理能⼒。Tomcat 按照协议和I/O 提供了6个实现类 : AjpNioProtocol ,

AjpAprProtocol, AjpNio2Protocol , Http11NioProtocol ,

Http11Nio2Protocol ,Http11AprProtocol

Adapter

由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat定义了⾃⼰的

Request类来封装这些请求信息。ProtocolHandler接⼝负责解析请求并⽣成

Tomcat Request类。但是这个Request对象不是标准的ServletRequest,不

能⽤Tomcat Request作为参数来调⽤容器。Tomcat设计者的解决⽅案是引

⼊CoyoteAdapter,这是适配器模式的经典运⽤,连接器调⽤

CoyoteAdapter的Sevice⽅法,传⼊的是Tomcat Request对象,

CoyoteAdapter负责将Tomcat Request转成ServletRequest,再调⽤容器

4.Tomcat Server容器Catalina

4.1Tomcat分层结构图及Catalina位置

Tomcat是一个有一系列可配置组件(conf/server.xml)组成的Web容器,Catalina是Tomcat的核心,其他模块都是为Catalina提供支撑的

 4.2Servlet容器Catalina结构

可以认为整个Tomcat是一个Catalina实例,Tomcat启动时会初始化这个实例,Catalina实例加载server.xml完成其他实例创建,创建并管理一个Server,Server创建并管理多个服务,每个服务又可以有多个Connector和一个Container

  • Catalina:负责解析Tomcat的配置文件(server.xml),来创建服务器Server组件并进行管理
  • Server:服务器表示整个Catalina Servlet容器及其他组件,负责组装并启动Servlet引擎、Tomcat连接器,Server通过实现LifeCycle接口,提供一种优雅的启动和关闭系统的方式
  • Service:服务是Server内部组件,一个Server包含多个Service,将多个Connector组件绑定到一个Container容器
  • Container:负责处理用户请求并返回响应的Servlet容器

4.3Container组件具体结构

Container组件包含如下分层父子关系的组件:

  • Engine:表示整个Catalina的Servlet引擎,用来管理多个虚拟站点,一个Service至多一个Engine,但一个Engine可包含多个Host
  • Host:代表一个虚拟主机,可以给Tomcat配置多个虚拟主机地址,一个虚拟主机可包含多个Context
  • Context:表示一个Web应用程序,一个Web应用可包含多个Wrapper
  • Wrapper:表示一个Servlet,容器的最底层,不包含子容器

二、Tomcat服务器核心配置详解

通过标签对conf/server.xml进行配置

主要标签结构

Server标签

Service标签 

Executor标签 

Connector标签

Engine标签 

 Host标签

可理解为一台服务器部署多个站点

Context标签 

用于配置一个Web应用

docBase:Web应⽤⽬录或者War包的部署路径。可以是绝对路径,也可以是相对于 Host appBase的相对路径。

path:Web应⽤的Context 路径。如果我们Host名为localhost, 则该web应⽤访问的根路径为:

http://localhost:8080/web_demo。

 三、手写mini版Tomcat

启动类

package server;

import util.HttpProtocolUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Bootstrap {

    private int port = 8080;

    private void start() throws IOException {
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println(">>>MiniCat Start...");
        /**
         * 1.0版本
         * 浏览器请求 http://loacalhost:8080,返回"Hello MiniCat"
         */
        /*while (true) {
            Socket socket = serverSocket.accept();
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello MiniCat";
            String response = HttpProtocolUtil.buildHttpHeader200(data.getBytes().length) + data;
            outputStream.write(response.getBytes());
            socket.close();
        }*/

        /**
         * 2.0版本
         * 封装Request和Response对象,返回html静态资源文件
         */
        while (true) {
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());
            response.outputHtml(request.getUrl());
            socket.close();
        }
    }

    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

请求对象

package server;

import java.io.IOException;
import java.io.InputStream;

public class Request {
    private String method;
    private String url;
    private InputStream inputStream;

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    public Request(InputStream inputStream) throws IOException {
        this.inputStream = inputStream;
        int available = 0;
        while (available == 0) {
            available = inputStream.available();
        }
        byte[] bytes = new byte[available];
        inputStream.read(bytes);
        String requestStr = new String(bytes);
        String firstLineStr = requestStr.split("\\n")[0];
        String[] firstLine = firstLineStr.split(" ");
        String method = firstLine[0];
        String url = firstLine[1];
        this.method = method;
        this.url = url;
    }
}

响应对象

package server;

import util.HttpProtocolUtil;
import util.StaticResourceUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Response {
    private OutputStream outputStream;

    public Response(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public void output(String content) throws IOException {
        outputStream.write(content.getBytes());
    }

    public void outputHtml(String path) throws IOException {
        String absolutePath = StaticResourceUtil.getAbsolutePath(path);
        File file = new File(absolutePath);
        if (file.exists() && file.isFile()) {
            StaticResourceUtil.outputStaticResource(new FileInputStream(file), outputStream);
        } else {
            output(HttpProtocolUtil.buildHttpHeader404());
        }
    }
}

封装响应码工具类

package util;

public class HttpProtocolUtil {

    public static String buildHttpHeader200(int contentLength) {
        return "HTTP/1.1 200 OK \n" +
                "Content-Type: text/html \n" +
                "Content-Length: " + contentLength + " \n" +
                "\r\n";
    }

    public static String buildHttpHeader404() {
        String str404 = "<h1>404 Not Found</h1>";
        return "HTTP/1.1 404 Not Found \n" +
                "Content-Type: text/html \n" +
                "Content-Length: " + str404.length() + " \n" +
                "\r\n" + str404;
    }
}

静态资源访问工具类

package util;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class StaticResourceUtil {

    public static String getAbsolutePath(String path) {
        String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
        return absolutePath.replaceAll("\\\\", "/") + path;
    }

    public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {
        int count = 0;
        while (count == 0) {
            count = inputStream.available();
        }
        int resourceSize = count;
        outputStream.write(HttpProtocolUtil.buildHttpHeader200(resourceSize).getBytes());
        int written = 0;
        int byteSize = 1024;
        byte[] bytes = new byte[byteSize];
        while (written < resourceSize) {
            if (written + byteSize > resourceSize) {
                byteSize = resourceSize - written;
                bytes = new byte[byteSize];
            }
            inputStream.read(bytes);
            outputStream.write(bytes);
            outputStream.flush();
            written += byteSize;
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值