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

news2024/10/7 6:44:14

目录

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

完成小案例

运行效果

此项目用maven至于怎么配置在下一篇文章

创建cal.html

CalServlet.java 

web.xml

WebUtils

问题:

Tomcat 整体架构分析

测试分析:

抓包情况

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

分析+代码实现

● 分析示意图

代码实现

TomcatV1 

问题分析:

需求分析/图解

● 分析示意图

 代码实现

RequestHandler类

TomcatV2 

问题分析:

分析+代码实现

● 分析示意图

 WyxRequestHandler 

wyxResponse

 wyxRequest 

wyxServlet接口

wyxHttpServlet

wyxCalServlet

WebUtils

 wyxTomcatV3 


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

完成小案例

运行效果

 

此项目用maven至于怎么配置在下一篇文章

创建cal.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>计算器</title>
</head>

<body>
    <h1>计算器</h1>
    <form action="/calServlet" method="get">
        num1:<input type="text" name="num1"><br />
        num2:<input type="text" name="num2"><br />
        <input type="submit" value="提交">
    </form>
</body>

</html>

CalServlet.java 

public class CalServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response) throws ServletException, IOException {
        //接收提交的数据进行计算
        //复制当前行 ctrl+alt+下光标
        String strNum1 = request.getParameter("num1");
        String strNum2 = request.getParameter("num2");

        //把strNum1 和 strNum2 转成 int
        int num1 = WebUtils.parseInt(strNum1, 0);
        int num2 = WebUtils.parseInt(strNum2, 0);
        int result = num1 + num2;

        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("<h1>" + num1 + " + " + num2 + " = " + result + "<h1>");
        writer.flush();
        writer.close();

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

web.xml

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <servlet>
        <servlet-name>CalServlet</servlet-name>
        <servlet-class>com.servlet.CalServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>CalServlet</servlet-name>
        <url-pattern>/calServlet</url-pattern>
    </servlet-mapping>
</web-app>

WebUtils

public class WebUtils {

    /**
     * 将一个字符串数字,转成 int, 如果转换失败,就返回传入 defaultVal
     * @param strNum
     * @param defaultVal
     * @return
     */
    public static int parseInt(String strNum, int defaultVal) {

        try {
            return Integer.parseInt(strNum);
        } catch (NumberFormatException e) {
            System.out.println(strNum + " 格式不对,转换失败");
        }

        return defaultVal;
    }


}

问题:

Tomcat 底层实现 和 调用到 Servlet 流程?

我们的目标: 不用 Tomcat, 不用系统提供的 Servlet, 模拟 Tomcat 底层实现并能调用我们自己设计的 Servle, 也能完成相同的功能

Tomcat 整体架构分析

● 说明: Tomcat 有三种运行模式(BIO, NIO, APR), 因为核心讲解的是 Tomcat 如何接

收客户端请求,解析请求, 调用 Servlet , 并返回结果的机制流程, 采用 BIO 线程模型来模拟.

 

测试分析:

浏览器 http://localhost:8080/cal.html 1.

浏览器输入 http://localhost:8080/cal.html

抓包情况

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

实现任务阶段

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

基于 socket 开发服务端-流程

分析+代码实现

● 分析示意图 

代码实现

TomcatV1 

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TomcatV1 {
    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,";
            System.out.println("========我们的 tomcat 给浏览器会送的数据 ======");
            System.out.println(resp);
            outputStream.write(resp.getBytes());//将 resp 字符串以 byte[] 方式返回
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            socket.close();
        }
    }
}

问题分析:

没有使用 BIO 线程模型,没有实现多线程,性能差

实现任务阶段 2- 使用 BIO 线程模型,支持多线程

BIO 线程模型介绍

需求分析/图解

  1. 需求分析如图, 浏览器请求 http://localhost:8080, 服务端返回 hi后台
  2. wtomcat 使用 BIO 线程模型,支持多线程=> 对前面的开发模式进行改造
  3. 分析+代码实现

● 分析示意图

 

 代码实现

RequestHandler类

import java.io.*;
import java.net.Socket;

public class RequestHandler implements Runnable {

    //定义Socket
    private Socket socket = null;

    public RequestHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {

        //这里我们可以对客户端/浏览器进行IO编程/交互
        try {
            InputStream inputStream = socket.getInputStream();

            //把inputStream -> BufferedReader -> 方便进行按行读取
            BufferedReader bufferedReader =
                    new BufferedReader(new InputStreamReader(inputStream, "utf-8"));

            //不同的线程在和浏览器和客户端交互
            System.out.println("当前线程= " + Thread.currentThread().getName());

            System.out.println("=========tomcatv2 接收到的数据如下=========");
            String mes = null;


            while ((mes = bufferedReader.readLine()) != null) {
                //如果长度为0 ""
                if (mes.length() == 0) {
                    break; //退出
                }
                System.out.println(mes);
            }
            //构建一下http响应头
            //返回的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 + "<h1>hi</h1>";
            System.out.println("========tomcatv2返回的数据是=========");
            System.out.println(resp);
            //返回数据给我们的浏览器/客户端-> 封装成http响应
            OutputStream outputStream = socket.getOutputStream();
           // resp.getBytes() 是把字符串转成字节数组
            outputStream.write(resp.getBytes());
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            socket.close();


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //最后一定确保socket要关闭
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

TomcatV2 

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

        }
    }
}

问题分析:

Tomcat V2 只是简单返回结果,没有和 Servlet、web.xml 关联

实现任务阶段 3- 处理 Servlet

分析+代码实现

● 分析示意图

 

 WyxRequestHandler 

package com.wyxdu.tomcat.handler;


import com.wyxdu.tomcat.WyxTomcatV3;
import com.wyxdu.tomcat.http.WyxRequest;
import com.wyxdu.tomcat.http.WyxResponse;

import com.wyxdu.tomcat.servlet.WyxHttpServlet;
import com.wyxdu.tomcat.utils.WebUtils;


import java.io.*;
import java.net.Socket;


public class WyxRequestHandler implements Runnable {

    //定义Socket
    private Socket socket = null;

    public WyxRequestHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {

        //这里我们可以对客户端/浏览器进行IO编程/交互
        try {
       
            WyxRequest wyxRequest = new WyxRequest(socket.getInputStream());
     

            //这里我们可以同HspResponse对象,返回数据给浏览器/客户端
            WyxResponse wyxResponse = new WyxResponse(socket.getOutputStream());

     
            //1. 得到 uri => 就是 servletUrlMapping 的 url-pattern
            String uri = wyxRequest.getUri();



            //=====================新增业务逻辑==========
            //(1) 判断uri是什么资源 => 工具方法
            //(2) 如果是静态资源,就读取该资源,并返回给浏览器 content-type text/html
            //(3) 因为目前并没有起到tomcat, 不是一个标准的web项目
            //(4) 把读取的静态资源放到 target/classes/cal.html
            //过滤,拦截 , 权限等待 => Handler.... => 分发
            if(WebUtils.isHtml(uri)) {//就是静态页面
                String content = WebUtils.readHtml(uri.substring(1));
                content = wyxResponse.respHeader + content;
                //得到outputstream , 返回信息(静态页面)给浏览器
                OutputStream outputStream = wyxResponse.getOutputStream();
                outputStream.write(content.getBytes());
                outputStream.flush();
                outputStream.close();
                socket.close();
                return;
            }


            //有了filter机制,可以理解再调用servlet之前,先匹配filter
            //1. 根据request对象封装的uri
            //2. 到 filterUrlMapping 去匹配
            //3. 如果匹配上就调用 filterMapping 对应的filer对象doFilter()
            //4. 如果没有匹配上,就直接走我们后的servlet/jsp/html.

            String servletName = WyxTomcatV3.servletUrlMapping.get(uri);
            if (servletName == null) {
                servletName = "";
            }
            //2. 通过uri->servletName->servlet的实例 , 真正的运行类型是其子类 WyxCalServlet
            WyxHttpServlet wyxHttpServlet =
                    WyxTomcatV3.servletMapping.get(servletName);
            //3. 调用service , 通过OOP的动态绑定机制,调用运行类型的 doGet/doPost
            if (wyxHttpServlet != null) {//得到
                wyxHttpServlet.service(wyxRequest, wyxResponse);
            } else {
                //没有这个servlet , 返回404的提示信息
                String resp = wyxResponse.respHeader + "<h1>404 Not Found</h1>";
                OutputStream outputStream = wyxResponse.getOutputStream();
                outputStream.write(resp.getBytes());
                outputStream.flush();
                outputStream.close();
            }

            
            socket.close();


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //最后一定确保socket要关闭
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

wyxResponse

public class wyxResponse {

    private OutputStream outputStream = null;

    //写一个http的响应头 => 先死后活
    public static final String respHeader = "HTTP/1.1 200 OK\r\n" +
            "Content-Type: text/html;charset=utf-8\r\n\r\n";

    //在创建 wyxResponse 对象时,传入的outputStream是和Socket关联的
    public wyxResponse(OutputStream outputStream) {
        this.outputStream = outputStream;
    }
    //当我们需要给浏览器返回数据时,可以通过HspResponse 的输出流完成
    //
    public OutputStream getOutputStream() {
        return outputStream;
    }
}

 wyxRequest 

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;

/**
 * 1. wyxRequest 作用是封装http请求的数据
 * get /WyxCalServlet?num1=10&num2=30
 * 2. 比如 method(get) 、 uri(/hspCalServlet) 、 还有参数列表 (num1=10&num2=30)
 * 3. wyxRequest 作用就等价原生的servlet 中的HttpServletRequest
 * 4. 一会走代码
 * 5. 这里考虑的是GET请求
 */
public class wyxRequest {

    private String method;
    private String uri;
    //存放参数列表 参数名-参数值 => HashMap
    private HashMap<String, String> parametersMapping =
            new HashMap<>();
    private InputStream inputStream = null;


    //构造器=> 对http请求进行封装 => 可以写的代码封装成方法
    //inputStream 是和 对应http请求的socket关联
    public wyxRequest(InputStream inputStream) {
        this.inputStream = inputStream;
        //完成对http请求数据的封装..
        encapHttpRequest();
    }

    /**
     * 将http请求的相关数据,进行封装,然后提供相关的方法,进行获取
     */
    private void encapHttpRequest() {
        System.out.println("wyxRequest init()");
        try {
            //inputstream -> BufferedReader
            BufferedReader bufferedReader =
                    new BufferedReader(new InputStreamReader(inputStream, "utf-8"));

            //读取第一行
            /**
             * GET /hspCalServlet?num1=10&num2=30 HTTP/1.1
             * Host: localhost:8080
             * User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Fi
             */
            String requestLine = bufferedReader.readLine();
            //GET - /WyxCalServlet?num1=10&num2=30 - HTTP/1.1
            String[] requestLineArr = requestLine.split(" ");
            //得到method
            method = requestLineArr[0];
            //解析得到 /WyxCalServlet
            //1. 先看看uri 有没有参数列表
            int index = requestLineArr[1].indexOf("?");
            if (index == -1) { //说明没有参数列表
                uri = requestLineArr[1];
            } else {
                //[0,index)
                uri = requestLineArr[1].substring(0, index);
                //获取参数列表->parametersMapping
                //parameters => num1=10&num2=30
                String parameters = requestLineArr[1].substring(index + 1);
                //num1=10 , num2=30 .... parametersPair= ["num1=10","num2=30" ]
                String[] parametersPair = parameters.split("&");
                //防止用户提交时 /hspCalServlet?
                if (null != parametersPair && !"".equals(parametersPair)) {
                    //再次分割 parameterPair = num1=10
                    for (String parameterPair : parametersPair) {
                        //parameterVal ["num1", "10"]
                        String[] parameterVal = parameterPair.split("=");
                        if (parameterVal.length == 2) {
                            //放入到 parametersMapping
                            parametersMapping.put(parameterVal[0], parameterVal[1]);
                        }
                    }
                }
            }
            //这里不能关闭流 inputStream 和 socket关联
            //inputStream.close();

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

    //request对象有一个特别重要方法
    public String getParameter(String name) {
        if (parametersMapping.containsKey(name)) {
            return parametersMapping.get(name);
        } else {
            return "";
        }
    }

    public String getMethod() {
        return method;
    }

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

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    @Override
    public String toString() {
        return "wyxRequest{" +
                "method='" + method + '\'' +
                ", uri='" + uri + '\'' +
                ", parametersMapping=" + parametersMapping +
                '}';
    }
}

wyxServlet接口

public interface wyxServlet {

    void init() throws Exception;

    void service(wyxRequest request, wyxResponse response) throws IOException;

    void destroy();
}

wyxHttpServlet

public abstract class wyxHttpServlet implements wyxServlet {

    @Override
    public void service(wyxRequest request, wyxResponse response) throws IOException {
        //说明 equalsIgnoreCase 比较字符串内容是相同,不区别大小写
        if("GET".equalsIgnoreCase(request.getMethod())) {
            //这里会有动态绑定
            this.doGet(request,response);
        } else if("POST".equalsIgnoreCase(request.getMethod())) {
            this.doPost(request,response);
        }
    }

    //这里我们使用的了模板设计模式 => java 基础的 抽象类专门讲过模板设计模式
    //让HspHttpServlet 子类 wyxCalServlet 实现

    public abstract void doGet(wyxRequest request, wyxResponse response);
    public abstract void doPost(wyxRequest request, wyxResponse response);
}

wyxCalServlet

public class wyxCalServlet extends wyxHttpServlet {
    @Override
    public void doGet(wyxRequest request, wyxResponse response) {
        //java基础的 OOP 的动态绑定机制..
        //写业务代码,完成计算任务
        int num1 = WebUtils.parseInt(request.getParameter("num1"), 0);
        int num2 = WebUtils.parseInt(request.getParameter("num2"), 0);

        int sum = num1 + num2;

        //返回计算结果给浏览器
        //outputStream 和 当前的socket关联
        OutputStream outputStream = response.getOutputStream();
        String respMes = wyxResponse.respHeader
                + "<h1>" + num1 + " + " + num2 + " = " + sum + " wyxTomcatV3 - 反射+xml创建</h1>";
        try {
            outputStream.write(respMes.getBytes());
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(wyxRequest request, wyxResponse response) {
        this.doGet(request, response);
    }

    @Override
    public void init() throws Exception {

    }

    @Override
    public void destroy() {

    }
}

WebUtils

public class WebUtils {

    //将字符串转成数字方法
    public static int parseInt(String strNum, int defaultVal) {
        try {
            return Integer.parseInt(strNum);
        } catch (NumberFormatException e) {
            System.out.println(strNum + " 不能转成数字");
        }
        return defaultVal;
    }

    //判断uri是不是html文件
    public static boolean isHtml(String uri) {

        return uri.endsWith(".html");
    }

    //根据文件名来读取该文件->String
    public static String readHtml(String filename) {
        String path = com.wyxdu.utils.WebUtils.class.getResource("/").getPath();
        StringBuilder stringBuilder = new StringBuilder();

        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(path + filename));
            String buf = "";
            while ((buf = bufferedReader.readLine()) != null) {
                stringBuilder.append(buf);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return stringBuilder.toString();
    }
}

 wyxTomcatV3 

package com.wyxdu.tomcat;

import com.wyxdu.tomcat.handler.wyxRequestHandler;
import com.wyxdu.tomcat.servlet.wyxHttpServlet;
import org.dom4j.Document;
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.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 第3版的Tomcat, 实现通过xml+反射来初始化容器
 */
public class wyxTomcatV3 {

    //1. 存放容器 servletMapping
    // -ConcurrentHashMap
    // -HashMap
    // key            - value
    // ServletName    对应的实例

    public static final ConcurrentHashMap<String, wyxHttpServlet>
            servletMapping = new ConcurrentHashMap<>();


    //2容器 servletUrlMapping
    // -ConcurrentHashMap
    // -HashMap
    // key                    - value
    // url-pattern       ServletName

    public static final ConcurrentHashMap<String, String>
            servletUrlMapping = new ConcurrentHashMap<>();


    //你可以这里理解session, tomcat还维护一个容器
    public static final ConcurrentHashMap<String, HttpSession>
            sessionMapping = new ConcurrentHashMap<>();
    

    //你可以这里理解filter, tomcat还维护了filter的容器
    public static final ConcurrentHashMap<String, String>
            filterUrlMapping = new ConcurrentHashMap<>();

    public static final ConcurrentHashMap<String, Filter>
            filterMapping = new ConcurrentHashMap<>();
    
    public static void main(String[] args) {
        wyxTomcatV3 wyxTomcatV3 = new wyxTomcatV3();
        wyxTomcatV3.init();
        //启动wyxtomcat容器
        wyxTomcatV3.run();
    }


    //启动WyxTomcatV3容器
    public void run() {

        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            System.out.println("=====wyxtomcatv3在8080监听======");
            while (!serverSocket.isClosed()) {
                Socket socket = serverSocket.accept();
                wyxRequestHandler wyxRequestHandler =
                        new wyxRequestHandler(socket);
                new Thread(wyxRequestHandler).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //直接对两个容器进行初始化
    public void init() {
        //读取web.xml => dom4j =>
        //得到web.xml文件的路径 => 拷贝一份.
        String path = wyxTomcatV3.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<Element> 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(),
                            (wyxHttpServlet) 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);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/443347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

十个高质量工具网站推荐,AI自动抠图换背景,任意背景自动融合

AI 背景更换是一种利用生成式人工智能创建新图像背景的软件工具。与传统方法需要移除原有的背景并更换新的不同&#xff0c;AI背景生成器使用先进的算法生成与前景完美融合的全新背景。这项技术彻底改变了图像编辑的方式&#xff0c;为设计提供了更多的创造自由和灵活性。 特点…

数据结构--B树、B+树

数据结构--B树、B树 1. 什么是B树2.建立B树的要求3.什么是B树4.Mysql里面为什么使用B树作为索引结构&#xff1f; 1. 什么是B树 B树是一种数据结构&#xff0c;用于在硬盘或其他非易失性存储介质上快速存储和访问大量数据。它是一种平衡树&#xff0c;其每个节点可以存储多个键…

zabbix SNMP traps 监控案例

目标 根据H3C网络设备 发送 SNMP trap 信息进行网络端口的告警。 具体过程 继上次配置的trap 方式进行监控一个案例。 其中log数据中的内容是&#xff1a; 20230330.163810 ZBXTRAP 192.168.21.148 UDP: [192.168.21.148]:52289->[172.18.18.2]:1162 DISMAN-EVENT-MIB::…

Keil5软件安装方法(兼容stm32与c51方法)

目录 一、下载软件包 二、安装软件 1、安装C51v960a.exe (1&#xff09;右键以管理员权限运行程序 &#xff08;2&#xff09;开始安装软件 &#xff08;3&#xff09;勾选协议 &#xff08;4&#xff09;选择安装路径 &#xff08;5&#xff09;填写名字与邮箱 &#xff0…

我国元宇宙行业分析:政策、技术、资金助推行业探索多元化应用场景

1.元宇宙行业概述、特征及产业链图解 元宇宙是人类运用数字技术构建的&#xff0c;由现实世界映射或超越现实世界&#xff0c;可与现实世界交互的虚拟世界&#xff0c;具备新型社会体系的数字生活空间&#xff0c;主要具有沉浸式体验、开放性、虚拟身份、不断演化、知识互动、…

c/c++:指针,指针定义和使用,指针大小4字节,野指针,空指针*p=NULL

c/c:指针&#xff0c;指针定义和使用&#xff0c;指针大小4字节&#xff0c;野指针&#xff0c;空指针*pNULL 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;此时学会c的话&#xff0c; 我所知道的周边的会c的同学&#xf…

VMware开机自启虚拟机系统

一、前提 wmware开机自启&#xff0c;安装完毕wmware不用管&#xff0c;默认该软件以及相关服务就是开机自启准备waware虚拟机&#xff08;一般都linux&#xff0c;我用centos7&#xff0c;你随意&#xff09; 二、脚本 脚本命令如下&#xff0c;等待30秒&#xff08;给服务自启…

NXP公司K20+PF8100实现硬件窗口看门狗

Kinetis K20 72 MHz MCU系列为中等性能的Kinetis产品组合提供了可扩展的入门级产品&#xff0c;具有差异化的集成&#xff0c;配备高精度模拟集成和灵活的低功耗功能。其相关资源可在NXP的官网获得。 PF81/PF82为PMIC系列专为高性能处理应用而设计&#xff0c;如娱乐中控、车载…

阅读完synchronized和ReentrantLock的源码后,我竟发现其完全相似

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#x1f4d5;系列专栏&#xff1a;Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙…

【数据结构】七大排序之快速排序详解(挖坑法快排,非递归快排,二路快排,三路快排)

目录 1.快速排序核心思路 2.挖坑法快速排序&#xff08;递归&#xff09; 2.1步骤 2.2代码&#xff08;详细注释&#xff09; 3.非递归快排&#xff08;用栈实现快速排序&#xff09; 3.1思路 3.2代码 4.二路快排 4.1思路 4.2代码 5.三路快排 5.1思路 5.2代码 1.快速…

大白话chatGPT及其原理之快速理解篇

大白话chatGPT及其原理之快速理解篇 从GPT名字理解chatGPTchatGPT三步曲 声明&#xff1a;本文为原创&#xff0c;未经同意请勿转载&#xff0c;感谢配合&#x1f604; chatGPT今年年初的时候是非常火爆的&#xff0c;现在也有很多相关的应用和插件。当然现在也有很多新的技术出…

老宋 带你五分钟搞懂vue

Vue 1.1 什么是框架 任何编程语言在最初的时候都是没有框架的&#xff0c;后来随着在实际开发过程中不断总结『经验』&#xff0c;积累『最佳实践』&#xff0c;慢慢的人们发现很多『特定场景』下的『特定问题』总是可以『套用固定解决方案』。于是有人把成熟的『固定解决方案…

袋鼠云春季生长大会圆满落幕,带来数实融合下的新产品、新方案、新实践

4月20日&#xff0c;以“数实融合&#xff0c;韧性生长”为主题的袋鼠云春季生长大会圆满落幕。 在春季生长大会中&#xff0c;袋鼠云带来了数实融合趋势下的最新行业沉淀、最佳实践经验和行业前瞻性的产品发布。从大数据基础软件“数栈”、到低代码数字孪生世界“易知微”&…

离散数学-考纲版-01-命题逻辑

文章目录 1. 命题逻辑的等值演算与推理演算参考1.1 命题1.2 常用联结词1.3 命题公式命题公式的分类-重言式-矛盾式-可满足式等价关系式-逻辑等价 logically equivalent 1.4 命题的等值演算与推理基本等价式逻辑蕴涵重言式 logically implication重言蕴涵推到归结法 1.5 命题公式…

log4j2日志简单使用

log4j2日志使用 1、log4j2介绍 Apache Log4j2是对Log4j的升级版&#xff0c; log4j2借鉴了logback的一些优秀的设计&#xff0c;并且修复了一些问题&#xff0c;因此带来了一些重大的提升&#xff0c;主要有&#xff1a; 1、异常处理&#xff1a;在logback中&#xff0c;Appe…

Makefile通用模板

工程目录 假如我们有以下目录结构&#xff1a; . ├── inc │ ├── add.h │ └── sub.h ├── main.c └── src├── add.c└── sub.c文件中的内容如下&#xff1a; //main.c #include <stdio.h> #include "add.h" #include "sub.h&q…

Mysql 学习(六)Mysql的数据目录

数据库中数据的存放 Mysql中 InnoDB 和 MyISAM 这样的存储引擎都是把数据存储到磁盘上的&#xff0c;而我们把这种存放到磁盘上的东西叫做文件系统&#xff0c;当我们想读取对应数据的时候&#xff0c;就会把数据从文件系统上加载&#xff0c;并且处理返回给我们&#xff0c;当…

每日学术速递4.19

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Visual Instruction Tuning 标题&#xff1a;可视化指令调优 作者&#xff1a;Haotian Liu, Chunyuan Li, Qingyang Wu, Yong Jae Lee 文章链接&#xff1a;https://arxiv.org/ab…

Midjourney:一步一步教你如何使用 AI 绘画 MJ

一步一步如何使用 Midjourney 教程&#xff1a;教学怎么用 MJ&#xff1f; 一、Midjourney&#xff08;MJ&#xff09;是什么&#xff1f; Midjourney是一款使用文字描述来生成高质量图像的AI绘画工具。这篇文章主要介绍了Midjourney及其用途&#xff0c;并针对Midjourney的使…

python 定时任务执行命令行

1.使用场景&#xff1a; 定时执行jmeter脚本&#xff0c;通过python定时器隔一段时间执行命令行命令。 2.库&#xff1a; os、datetime、threading &#xff08;1&#xff09;利用threading.Timer()定时器实现定时任务 Timer方法说明Timer(interval, function, argsNone, k…