Tomcat常见报错以及手动实现Tomcat
作者:mmseoamin日期:2023-12-27

一.Tomcat的简单启动

1.安装Tomcat

2.Tomcat启动

1. 双击 bin 目录下的 startup.bat 文件

2. 输入 http://localhost:8080/,显示如下界面代表安装成功, 默认在 8080 端口

3. 注意,不要关闭黑窗口,关闭了,tomcat 服务就停止了.

Tomcat常见报错以及手动实现Tomcat,第1张

3.曾经的启动失败案例

3.1Tomcat停止时报错(java.net.ConnectException: 拒绝连接 (Connection refused))

Tomcat常见报错以及手动实现Tomcat,第2张

问题:没有关闭之前的Tomcat服务

解决办法:pkill -9 java的方式强制关闭tomcat服务,然后重新启动。

3.2启动Tomcat时终端显示乱码

...\conf\logging.properties配置文件中默认设置的UTF-8改为GBK

3.3Tomcat无法启动:

  • 问题: Tomcat启动失败,通常会在启动脚本或日志中显示错误消息。
  • 解决办法:
    • 检查端口冲突,确保Tomcat所需的端口没有被其他进程占用。
    • 检查server.xml配置文件,确保没有错误配置。
    • 检查Java环境变量(如JAVA_HOME)是否正确设置。
    • 查看Tomcat的日志文件以获取详细错误信息。

    3.4内存溢出错误(OutOfMemoryError):

    • 问题: 应用程序使用的内存超出了可用内存限制。
    • 解决办法:
      • 增加Tomcat的JVM堆内存大小,通过修改catalina.sh或catalina.bat中的-Xmx参数来实现。
      • 使用内存分析工具(如VisualVM)来查找内存泄漏问题。
      • 当然还可以优化代码,减少内存。嘿嘿嘿

      3.5无法连接到数据库:

      • 问题: 应用程序无法连接到数据库。
      • 解决办法:
        • 检查数据库服务器是否正在运行。
        • 检查数据库连接字符串和凭据是否正确。
        • 确保数据库驱动程序JAR文件已正确放置在Tomcat的lib目录中。

        4.解决问题大致思路:

        4.1运行之后报错:

        打开...\logs日志文件查看日志,查看出错的地方并对症下药。

        4.2无法运行报错:

        当我们点击startup.bat之后,黑屏一闪而逝,那么我们可以在startup.bat的末尾敲击pause之后,报错,当我们再次点击之后就会出现对应的错误。没有问题就会出现你的对应的配置文件,jdk之类的。如果还无法解决,看看server.xml中的具体配置是不是缺少某个文件或者文件位置安装错误。

        二.Tomcat简单介绍

        Tomcat 目录结构

        Tomcat常见报错以及手动实现Tomcat,第3张

        1. server.xml 用于配置 tomcat 的基本设置(启动端口,关闭端口, 主机名)

        2. wex.xml 用于指定 tomcat 运行时配置(比如 servlet 等..)

        3. webapps 目录是存放 web 应用,就是网站,通常在我们的url后面添加/.....即可。

        例如:localhost:8080/web/text.txt就是访问webapps的text.txt文件

        三.Tomcat 服务中部署 WEB 应用

        1.什么是Web应用

        1.1 WEB应用是多个web资源的集合。简单的说,可以把web应用理解为硬盘上的一个目录, 这个目录用于管理多个web资源。

        1.2 Web应用通常也称之为web应用程序,或web工程,通俗的说 就是网站

        2.WEb应用组成

        一个 WEB 应用由多个 WEB 资源或其它文件组成,包括 html 文件、css 文件、js 文件、动 态 web 页面、java 程序、支持 jar 包、配置文件等。开发人员在开发 web 应用时,按照规 定目录结构存放这些文件。否则,在把 web 应用交给 web 服务器管理时,不仅可能会使 web 应用无法访问,还会导致 web 服务器启动报错

        3.JavaWeb程序/应用/工程目录结构

        Tomcat常见报错以及手动实现Tomcat,第4张

        4.部署方式

        4.1将 web 工程的目录拷贝到 Tomcat 的 webapps 目录

        1. news Web工程(目前都是静态资源 html, 图片)

        2. 将该news目录/文件夹 拷贝到 Tomcat 的webapps目录下

        3. 浏览器输入: http://ip[域名]:port/news/子目录../文件名

        4.2通过配置文件来部署

        1.在Tomcat 下的 conf 目录\Catalina\localhost\ 下,配置文件,比如hong.xml(提醒:知道 Tomcat通过配置,可以把一个web应用,映射到指定的目录,可以解决磁盘空间分配

        2.访问web工程: http://ip[域名]:port/hong/index.html 就表示访问 D:\album 目录下的 index.html

        4.3ROOT 的工程的访问

        1. 在浏览器地址栏中输入访问地址如下:http://ip[域名]:port,没有Web工程/应用名时,默认访问的是 ROOT 工程

        2. 在浏览器地址栏中输入的访问地址如下: http://ip[域名]:port/工程名/ ,没有资源名, 默认访问 index.jsp 页面

        4.4maven

        Tomcat常见报错以及手动实现Tomcat,第5张

        我们可以通过package打包成war格式或者jar格式返给webapps里面进行编译

        四.底层逻辑实现

        思考问题: Tomcat 底层实现 和 调用到 Servlet 流程?

        1.Tomcat 整体架构分析

        Tomcat 有三种运行模式(BIO, NIO, APR), 因为准备要说的是 Tomcat 如何接收客户端请求,解析请求, 调用 Servlet , 并返回结果的机制流程, 采用 BIO 线程模型来模拟.

        Tomcat常见报错以及手动实现Tomcat,第6张

        2.手动实现Tomcat 底层机制+ 自己设计 Servlet

        1.编写自己 Tomcat, 能给浏览器返回 Hi, Hong

        Tomcat常见报错以及手动实现Tomcat,第7张

        1.1分析

        Tomcat常见报错以及手动实现Tomcat,第8张

        1.2代码实现
        import java.io.*;
        import java.net.ServerSocket;
        import java.net.Socket;
        /**
         * @author hong
         * @version 1.0
         * 这是第一个版本的tomcat ,可以完成,接收浏览器的请求,并返回信息
         */
        public class HspTomcatV1 {
            public static void main(String[] args) throws IOException {
                //1. 创建ServerSocket, 在 8080端口监听
                ServerSocket serverSocket = new ServerSocket(8080);
                System.out.println("=======mytomcat在8080端口监听======");
                while (!serverSocket.isClosed()) {
                    //等待浏览器/客户端的连接
                    //如果有连接来,就创建一个socket
                    //这个socket就是服务端和浏览器端的连接/通道
                    Socket socket = serverSocket.accept();
                    //先接收浏览器发送的数据
                    //inputStream 是字节流=> BufferedReader(字符流)
                    InputStream inputStream = socket.getInputStream();
                    BufferedReader bufferedReader =
                            new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
                    String mes = null;
                    System.out.println("=======接收到浏览器发送的数据=======");
                    //循环的读取
                    while ((mes = bufferedReader.readLine()) != null) {
                        //判断mes的长度是否为0
                        if (mes.length() == 0) {
                            break;//退出while
                        }
                        System.out.println(mes);
                    }
                    //我们的tomcat会送-http响应方式
                    OutputStream outputStream = socket.getOutputStream();
                    //构建一个http响应的头
                    //\r\n 表示换行
                    //http响应体,需要前面有两个换行 \r\n\r\n
                    String respHeader = "HTTP/1.1 200 OK\r\n" +
                            "Content-Type: text/html;charset=utf-8\r\n\r\n";
                    String resp = respHeader + "hi, hong";
                    System.out.println("========我们的tomcat 给浏览器会送的数据======");
                    System.out.println(resp);
                    outputStream.write(resp.getBytes());//将resp字符串以byte[] 方式返回
                    outputStream.flush();
                    outputStream.close();
                    inputStream.close();
                    socket.close();
                    // //等会
                    // inputStream.close();
                    // socket.close();
                }
            }
        }

        2.使用 BIO 线程模型,支持多线程

        2.1BIO 线程模型介绍

        Tomcat常见报错以及手动实现Tomcat,第9张

        2.2分析

        Tomcat常见报错以及手动实现Tomcat,第10张

        2.3代码实现
        /**
         * @author hong
         * @version 1.0
         */
        public class HspTomcatV2 {
            public static void main(String[] args) throws IOException {
                //在8080端口监听
                ServerSocket serverSocket = new ServerSocket(8080);
                System.out.println("=======hongtomcatV2 在8080监听=======");
                //只要 serverSocket没有关闭,就一直等待浏览器/客户端的连接
                while (!serverSocket.isClosed()) {
                    //1. 接收到浏览器的连接后,如果成功,就会得到socket
                    //2. 这个socket 就是 服务器和 浏览器的数据通道
                    Socket socket = serverSocket.accept();
                    //3. 创建一个线程对象,并且把socket给该线程
                    //  这个是java线程基础
                    HspRequestHandler hspRequestHandler =
                            new HspRequestHandler(socket);
                    new Thread(hspRequestHandler).start();
                }
            }
        }
        

        3.处理 Servlet

        3.1Servlet 生命周期-回顾

        Tomcat常见报错以及手动实现Tomcat,第11张

        Tomcat常见报错以及手动实现Tomcat,第12张

        3.2分析

        浏览器请求 http://localhost:8080/hongCalServlet, 提交数据,完成计算 任务,如果 servlet 不存在,返回 404

        Tomcat常见报错以及手动实现Tomcat,第13张

        Tomcat常见报错以及手动实现Tomcat,第14张

        3.3代码
        package com.hspedu.tomcat;
        import com.hspedu.tomcat.handler.HspRequestHandler;
        import com.hspedu.tomcat.servlet.HspHttpServlet;
        import org.dom4j.Document;
        import org.dom4j.DocumentException;
        import org.dom4j.Element;
        import org.dom4j.io.SAXReader;
        import javax.servlet.Filter;
        import javax.servlet.http.HttpSession;
        import java.io.File;
        import java.io.IOException;
        import java.net.MalformedURLException;
        import java.net.ServerSocket;
        import java.net.Socket;
        import java.util.List;
        import java.util.concurrent.ConcurrentHashMap;
        /**
         * @author hong
         * @version 1.0
         * 第3版的Tomcat, 实现通过xml+反射来初始化容器
         */
        public class HspTomcatV3 {
            //1. 存放容器 servletMapping
            // -ConcurrentHashMap
            // -HashMap
            // key            - value
            // ServletName    对应的实例
            public static final ConcurrentHashMap
                    servletMapping = new ConcurrentHashMap<>();
            //2容器 servletUrlMapping
            // -ConcurrentHashMap
            // -HashMap
            // key                    - value
            // url-pattern       ServletName
            public static final ConcurrentHashMap
                    servletUrlMapping = new ConcurrentHashMap<>();
            //你可以这里理解session, tomcat还维护一个容器
            public static final ConcurrentHashMap
                    sessionMapping = new ConcurrentHashMap<>();
            
            //你可以这里理解filter, tomcat还维护了filter的容器
            public static final ConcurrentHashMap
                    filterUrlMapping = new ConcurrentHashMap<>();
            public static final ConcurrentHashMap
                    filterMapping = new ConcurrentHashMap<>();
            //变强..
            public static void main(String[] args) {
                HspTomcatV3 hspTomcatV3 = new HspTomcatV3();
                hspTomcatV3.init();
                //启动hsptomcat容器
                hspTomcatV3.run();
            }
            //启动HspTomcatV3容器
            public void run() {
                try {
                    ServerSocket serverSocket = new ServerSocket(8080);
                    System.out.println("=====hongtomcatv3在8080监听======");
                    while (!serverSocket.isClosed()) {
                        Socket socket = serverSocket.accept();
                        HspRequestHandler hspRequestHandler =
                                new HspRequestHandler(socket);
                        new Thread(hspRequestHandler).start();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //直接对两个容器进行初始化
            public void init() {
                //读取web.xml => dom4j =>
                //得到web.xml文件的路径 => 拷贝一份.
                String path = HspTomcatV3.class.getResource("/").getPath();
                //System.out.println("path= " + path);
                //使用dom4j技术完成读取
                SAXReader saxReader = new SAXReader();
                //困难->真的掌握
                try {
                    Document document = saxReader.read(new File(path + "web.xml"));
                    System.out.println("document= " + document);
                    //得到根元素
                    Element rootElement = document.getRootElement();
                    //得到根元素下面的所有元素
                    List elements = rootElement.elements();
                    //遍历并过滤到 servlet servlet-mapping
                    for (Element element : elements) {
                        if ("servlet".equalsIgnoreCase(element.getName())) {
                            //这是一个servlet配置
                            //System.out.println("发现 servlet");
                            //使用反射将该servlet实例放入到servletMapping
                            Element servletName = element.element("servlet-name");
                            Element servletClass = element.element("servlet-class");
                            servletMapping.put(servletName.getText(),
                                    (HspHttpServlet) Class.forName(servletClass.getText().trim()).newInstance());
                        } else if ("servlet-mapping".equalsIgnoreCase(element.getName())) {
                            //这是一个servlet-mapping
                            //System.out.println("发现 servlet-mapping");
                            Element servletName = element.element("servlet-name");
                            Element urlPatter = element.element("url-pattern");
                            servletUrlMapping.put(urlPatter.getText(), servletName.getText());
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //验证,这两个容器是否初始化成功
                System.out.println("servletMapping= " + servletMapping);
                System.out.println("servletUrlMapping= " + servletUrlMapping);
            }
        }
        
        3.4测试
        Tomcat常见报错以及手动实现Tomcat,第15张 

        Tomcat常见报错以及手动实现Tomcat,第16张