Tomcat学习

 1.3 Tomcat目录

  • bin下,startup.bat,  startup.sh, shutdown.bat, shutdown.sh
  • conf logging.properties 日志相关配置
  • conf server.xml 需要配置,部署应用端口指定
  • conf tomcat-users.xml 用户,角色以及功能
  • conf web.xml 全局的web.xml,局部覆盖全局

1.4 浏览器请求服务器流程

http 只定义了数据的组织各市,数据考TCP/IP 传输

 

1.5 Tomcat处理请求

 

  • http服务器
  • Tomcat是一个Servlet服务器

 

1.6 Servlet容器处理请求

Servlet 完成业务逻辑处理

1)Tomcat是一个http服务器

2)Tomcat是一个Servlet容器

 

加载servlet 就是反射

 

 

1.7 总体架构概述

1)和客户端浏览器交互,进行socket通信,将字节流和Request,Response 进行转换

2)Servlet容器处理业务逻辑

 

1.8 Connector,Coyote

1)封装了网络通信

2)使得容器组件与容器解耦

3)里面有各种IO模型(NIO) 和应用层协议 (HTTP)

 

1.10 容器组件Catalina

本质是servlet容器

 

一个Catalina实例:

  • 一个Server实例
  • 多个Service实例
  • 每个Service有多个Connector 和一个Container

 

1.11 配置

Connector 可以监听端口

Executor 增加共享线程池

一个服务器可以当做多个虚拟主机用,配置多个站点

 

1.16 context标签

配置一个Web应用

 

2.1 手写实现mini版Tomcat。思路分析

1)作为一个服务器,提供服务。接收请求(Socket 通信)。Minicat 1.0版本

package server;

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

public class Bootstrap {
    //端口号
    private int port = 8080;

    public int getPort() {
        return this.port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    //初始化操作
    public void start() throws IOException {
        // 浏览请求 http://localhost:8080, 返回一个固定的字符串到页面 "Hello Minicat"
        // 必须写好请求体,响应头header
        ServerSocket serverSocket = new ServerSocket(this.port);
        System.out.println("Mini cat start on : " + this.port);
        while (true) {
            Socket socket = serverSocket.accept();
            // 接收到请求
            OutputStream outputStream = socket.getOutputStream();
            String responseText = HttpProtocolUtil.getHttpHeader200("Hello Minicat".getBytes().length) + "Hello Minicat";
            outputStream.write(responseText.getBytes());
            socket.close();
        }
    }

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

 

package server;

//提供响应头信息,这里提供200 和 400的情况
public class HttpProtocolUtil {
    public static String getHttpHeader200(long length) {
        return "HTTP/1.1 200 OK\n" +
                "Content-Type: text/html \n" +
                "Content-Length: " + length + "\n" +
                "\r\n";
    }

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

 

2)请求信息封装成Request对象(Resonpse 对象)Minicat 2.0 版本

返回html静态资源文件

启动:

package server;

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;

    public int getPort() {
        return this.port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    //初始化操作
    public void start() throws IOException {
        // 浏览请求 http://localhost:8080, 返回一个固定的字符串到页面 "Hello Minicat"
        // 必须写好请求体,响应头header
        ServerSocket serverSocket = new ServerSocket(this.port);
        System.out.println("Mini cat start on : " + this.port);

        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;

//根据input stream
public class Request {
    private String method; // 请求方式 比如GET/POST

    private String url; // 例如 /,/index.html. 代表资源路径 读出来通过OutputStream输出
    private InputStream inputStream; // 输入流,其他属性,从输入流中解析出来

    public String getMethod() {
        return method;
    }

    public String getUrl() {
        return url;
    }

    public InputStream getInputStream() {
        return inputStream;
    }

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

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

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

    public Request(InputStream inputStream) throws IOException {
        this.inputStream = inputStream;
        int count = 0;
        while (count == 0) {
            count = inputStream.available();
        }

        byte[] bytes = new byte[count];
        inputStream.read(bytes);
        String inputStr = new String(bytes);
        String [] requests = inputStr.split("\\n");
        String firstLine = requests[0];
        String  [] firstSplit = firstLine.split(" ");
        this.method = firstSplit[0];
        this.url = firstSplit[1];
        // 可以看见请求头
        System.out.println("请求信息 method=" + this.method);
        System.out.println("请求信息 url=" + this.url);
    }
}
package server;

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

//Output Stream
public class Response {
    private OutputStream outPutStream;

    public Response() {
    }

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

    //path url, 根据url 获取静态资源绝对路径 进一步根据绝对路径读取该路径资源文件,最终通过输出流输出
    public void outputHtml(String path) throws IOException {
        // 获取静态资源文件的绝对路径
        String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);
        File file = new File(absoluteResourcePath);
        System.out.println("outputHtml absoluteResourcePath=" + absoluteResourcePath);
        if (file.exists() && file.isFile()) {
            System.out.println("here");
            // 读取敬爱资源,输出静态资源, 输出静态资源
            StaticResourceUtil.outputStaticResource(new FileInputStream(file), outPutStream);
        } else {
            // 输出404
            output(HttpProtocolUtil.getHttpHeader404());
        }
    }

    //使用输出流
    public void output(String content) throws IOException {
        outPutStream.write(content.getBytes());
    }

}
package server;

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();
        System.out.println("absolute " + absolutePath);
        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.getHttpHeader200(resourceSize).getBytes());
        //读取内容输出
        long written = 0; // 已经读取了内容长度

        while (written < resourceSize) {
            int byteSize = 1024;
            if (written + byteSize > resourceSize) {
                byteSize = (int) (resourceSize - written);
            }
            byte[] bytes = new byte[byteSize];
            inputStream.read(bytes);
            outputStream.write(bytes);
            outputStream.flush();
            written += byteSize;
        }
    }
}

 

3)客户端请求资源,资源分为静态资源(html)和动态资源 (Servlet)

 

package server;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Bootstrap {
    //端口号
    private int port = 8080;

    private Map<String, HttpServlet> servletMap = new HashMap<>();

    public int getPort() {
        return this.port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    // 加载解析web.xml, 初始化Servlet
    private void loadServlet() {
        InputStream resourcesAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourcesAsStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (Element element : selectNodes) {
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletnameElement.getStringValue();
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletclassElement.getStringValue();
                servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //初始化操作
    public void start() throws Exception {
        // 浏览请求 http://localhost:8080, 返回一个固定的字符串到页面 "Hello Minicat"
        // 必须写好请求体,响应头header
        loadServlet();
        ServerSocket serverSocket = new ServerSocket(this.port);
        System.out.println("Mini cat start on : " + this.port);

        while (true) {
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            //从输入流获取请求信息, 网络存在延迟
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());
            if (servletMap.get(request.getUrl()) == null) {
                response.outputHtml(request.getUrl());
            } else {
                // 动态资源servlet请求
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request, response);
            }
            socket.close();
        }
    }

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

 

package server;

public class LagouServlet extends HttpServlet {
    @Override
    public void doGet(Request request, Response response) {
        String content = "<h1>LagouServeltGet</h1>";
        try {
            response.output(
                    HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content
            );
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void doPost(Request request, Response response) {

    }

    @Override
    public void init() throws Exception {

    }

    @Override
    public void destroy() throws Exception {

    }
}

 

package server;

public abstract class HttpServlet implements Servlet {
    public abstract void doGet(Request request, Response response);

    public abstract void doPost(Request request, Response response);

    @Override
    public void service(Request request, Response response) throws Exception {
        if ("GET".equalsIgnoreCase(request.getMethod())) doGet(request, response);
        else doPost(request, response);
    }
}

 

package server;

interface Servlet {
    void init() throws Exception;

    void destroy() throws Exception;

    void service(Request request, Response response) throws Exception;
}

 

4) 多线程改造

如果你请求了一个资源阻塞住,那么再有一个请求到你服务器,也同样会阻塞住。可以使用多线程进行改造. 来一个请求,给一个socket处理. 用线程池

package server;

import java.io.InputStream;
import java.net.Socket;
import java.util.Map;

public class RequestProcessor extends Thread {
    private Socket socket;
    private Map<String, HttpServlet> servletMap;

    public RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap) {
        this.socket = socket;
        this.servletMap = servletMap;
    }

    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();

            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            // 静态资源处理
            if (servletMap.get(request.getUrl()) == null) {
                response.outputHtml(request.getUrl());
            } else {
                // 动态资源servlet请求
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request, response);
            }

            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
package server;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Bootstrap {
    //端口号
    private int port = 8080;

    private Map<String, HttpServlet> servletMap = new HashMap<>();

    public int getPort() {
        return this.port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    // 加载解析web.xml, 初始化Servlet
    private void loadServlet() {
        InputStream resourcesAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourcesAsStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (Element element : selectNodes) {
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletnameElement.getStringValue();
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletclassElement.getStringValue();
                servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //初始化操作
    public void start() throws Exception {
        // 浏览请求 http://localhost:8080, 返回一个固定的字符串到页面 "Hello Minicat"
        // 必须写好请求体,响应头header
        loadServlet();
        ServerSocket serverSocket = new ServerSocket(this.port);
        System.out.println("Mini cat start on : " + this.port);

        ExecutorService service = Executors.newFixedThreadPool(10);

        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
            service.execute(requestProcessor);
        }
    }

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

 

4)资源返回给客户端浏览器

 

 

2,8

posted @ 2024-03-04 11:49  ylxn  阅读(1)  评论(0编辑  收藏  举报