当前位置: 首页 > news >正文

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

1. 前言

先看一个小案例, 引出对 Tomcat 底层实现思考

1.1 完成小案例

在这里插入图片描述
在这里插入图片描述
● 快速给小伙伴完成这个小案例
0. 我们准备使用 Maven 来创建一个 WEB 项目, 老师先简单给小伙伴介绍一下 Maven 是什么, 更加详细的使用,我们还会细讲, 现在先使用一把
在这里插入图片描述
在这里插入图片描述

  1. 先创建一个 Maven 的 Web 项目 hsp-tomcat
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

补充:如何配置阿里 maven 镜像
(1) 把 maven的安装目录\conf\settings.xml 拷贝默认的 maven 配置目录
(2) C:\Users\Administrator.m2 目录 settings.xml
(3) 修改 C:\Users\Administrator.m2\settings.xml , 增加如下的部分

<mirrors>
<!-- mirror
| Specifies a repository mirror site to use instead of a given repository. The
repository that
| this mirror serves has an ID that matches the mirrorOf element of this mirror.
IDs are used
| for inheritance and direct lookup purposes, and must be unique across the
set of mirrors. |
<mirror>
<id>mirrorId</id>
<mirrorOf>repositoryId</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://my.repository.com/repo/path</url>
</mirror>
--><mirror><id>alimaven</id><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url><mirrorOf>central</mirrorOf></mirror>
</mirrors>

(4) 到此 ok

  1. 修改 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!--解读1. dependency 表示依赖, 也就是我们这个项目需要依赖的 jar 包2. groupId 和 artifactId 被统称为坐标, 是为了去定位这个项目/jar3. groupId: 一般是公司 比如 com.baidu , 这里是 avax.servlet4. artifactId 一般是项目名, 这里是 javax.servlet-api5. 这样的化就可以定位一个 jar 包6. version 表示你引入到我们项目的 jar 包的版本是 3.1.07. scope: 表示作用域,也就是你引入的 jar 包的作用范围8. provided 表示在 tomcat 本身是有这个 jar 的,因此在编译,测试使用,但是在打包发布就不用要带上9. 在默认情况下, 引入的 jar 会到 中央仓库去下载 https://mvnrepository.com/10. 会下载到哪里到你指定的目录 C:\Users\Administrator\.m2\repository11. 有时为了下载更快, 往往配置镜像,12. 在 默 认 的 路 径 下 拷 贝 一 份 setting.xml 到C:\Users\Administrator\.m2\settings.xml13. 指定默认的阿里云镜像<mirrors><mirror><id>alimaven</id><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url><mirrorOf>central</mirrorOf></mirror></mirrors>--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency></dependencies>
  1. 创建 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>
  1. 创建 java 目录,存放 java 源文件在这里插入图片描述
  2. 创建 CalServlet.java
    在这里插入图片描述
  3. 修改 web.xml , 配置 Servlet
<!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.hspedu.servlet.CalServlet</servlet-class></servlet><servlet-mapping><servlet-name>CalServlet</servlet-name><url-pattern>/calServlet</url-pattern></servlet-mapping>
</web-app>
  1. 修改 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 转成 intint 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);}
}
  1. 创 建 工 具 类 WebUtils.java
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;}}
  1. 配置 tomcat
    略。。。
  2. 启动 tomcat
  3. 浏览器访问: http://localhost:8080/cal.html
    在这里插入图片描述
  4. 完成测试

maven 中央仓库 : https://mvnrepository.com/

1.2 思考

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

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

1.3 Tomcat 整体架构分析

1.3.1 一图胜千言

● 说明: Tomcat 有三种运行模式(BIO, NIO, APR), 因为老师核心讲解的是 Tomcat 如何接收客户端请求,解析请求, 调用 Servlet , 并返回结果的机制流程, 采用 BIO 线程模型来模拟.[绘图]
在这里插入图片描述

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

2.1 实现任务阶段 1

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

基于 socket 开发服务端-流程:
在这里插入图片描述

2.1.1 需求分析/图解

  1. 需求分析如图, 浏览器请求 http://localhost:8080/??, 服务端返回 hi , hspedu
    在这里插入图片描述

2.1.2 分析+代码实现

● 分析示意图
在这里插入图片描述
● 代码实现

  1. 创 建 TomcatV1.java
/*** 这是第一个版本的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(字符流)//java基础 IO , 第19章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:判断字符串长度是否为0的作用是为了处理HTTP请求头和请求体之间的空行。if (mes.length() == 0) { break;//退出while}System.out.println(mes);}//我们的tomcat会送-http响应方式OutputStream outputStream = socket.getOutputStream();//构建一个http响应的头//\r\n 表示换行//http响应体,需要前面有两个换行 \r\n\r\nString respHeader = "HTTP/1.1 200 OK\r\n" +"Content-Type: text/html;charset=utf-8\r\n\r\n";String resp = respHeader + "hi, hspedu 韩顺平教育";System.out.println("========我们的tomcat 给浏览器会送的数据======");System.out.println(resp);outputStream.write(resp.getBytes());//将resp字符串以byte[] 方式返回outputStream.flush();outputStream.close();inputStream.close();socket.close();}}
}

细节说明:
1.判断字符串长度是否为0的作用是为了处理HTTP请求头和请求体之间的空行。
在HTTP请求中,请求头和请求体之间必须要有一个空行,表示请求头已经结束,请求体开始。因此,在读取HTTP请求时,需要先读取请求头并解析出请求信息,然后再读取请求体部分。而读取请求头时,每个请求头字段都是以冒号和空格分隔的键值对形式出现的,如果读取到的一行请求头字段的长度为0,就表明请求头已经结束了。此时就可以退出循环,继续处理请求体的内容了。
因此,为了在读取HTTP请求时正确地处理请求头和请求体之间的空行,需要添加判断字符串长度是否为0的逻辑。
 
2.在HTTP协议中,每行信息都以"\r\n"(回车+换行)作为结束符,表示该行信息结束,并让浏览器接收下一个信息。
所以在构造HTTP响应头部时,需要在每行信息后添加"\r\n"。而在Java中,“\n"表示换行,即使只写”\n"在大多数情况下也能正常工作,但是使用"\r\n"会更加规范和严谨,因为在Windows系统中,只用"\n"可能不会被认为是换行符,而使用"\r\n"则能保证适用于所有操作系统。
因此,这段代码中,在回复客户端的HTTP响应头中使用了"\r\n"作为结束符号,确保HTTP响应头的格式符合HTTP协议的规范。

  1. 测试 浏览器 : http://localhost:8080/
    在这里插入图片描述

2.1.3 问题分析

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

2.2 实现任务阶段 2

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

2.2.1 BIO 线程模型介绍

在这里插入图片描述

补充:
1.BIO是阻塞式I/O(Blocking I/O)的缩写,是Java网络编程中常用的一种I/O模型。它指的是在进行网络数据读写时,当用户线程发起了一个I/O操作(如读取数据),该线程会一直等待直到操作完成并返回结果,期间会一直阻塞在那里,无法做其他事情。与之相对的是非阻塞式I/O(NIO)和异步I/O(AIO)两种模型。
BIO模型是传统的线程池模型,即一个请求对应一个线程。在BIO模型中,一个客户端请求到来时,服务器会开启一个新的线程去处理这个请求,直到整个请求处理完毕才会退出线程。这种模型实现简单,易于理解,但同时也会带来一些问题,例如线程池大小的限制、线程上下文切换等问题。当线程池达到最大线程数时,新的请求就会被拒绝或者阻塞,降低系统的吞吐量和性能。
因此,在高并发、大规模的网络编程场景中,BIO不再适用,而需要使用NIO和AIO等更加高效的I/O模型。
 
2.NIO是Java NIO(New I/O)的缩写,指的是Java提供的一种高效的I/O处理方式。与传统的I/O操作(如BIO)不同,NIO是基于事件驱动的模型,可以在单线程中处理多个并发请求,提升了性能和可扩展性。
通过使用NIO库,Java应用程序可以实现非阻塞式I/O操作,包括文件操作、套接字操作等。它的主要优势在于使用少量线程就能处理大量并发连接,因此适用于高负载、高可靠性的网络应用程序。

2.2.2 需求分析/图解

  1. 需求分析如图, 浏览器请求 http://localhost:8080, 服务端返回 hi , hspedu, 后台hsptomcat 使用 BIO 线程模型,支持多线程=> 对前面的开发模式进行改造
    在这里插入图片描述

2.2.3 分析+代码实现

● 分析示意图
在这里插入图片描述
● 代码实现

HspTomcatV2.java

public class HspTomcatV2 {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 hspRequestHandler =new HspRequestHandler(socket);new Thread(hspRequestHandler).start();}}
}

HspRequestHandler.java

/*** 解读* 1. HspRequestHandler 对象是一个线程对象* 2. 处理一个http请求的*/
public class HspRequestHandler implements Runnable {//定义Socketprivate Socket socket = null;public HspRequestHandler(Socket socket) {this.socket = socket;}@Overridepublic 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("=========hsptomcatv2 接收到的数据如下=========");String mes = null;while ((mes = bufferedReader.readLine()) != null) {//     如果长度为0 ""if (mes.length() == 0) {break; //退出}System.out.println(mes);}// 构建一下http响应头// 返回的http的响应体和响应头之间有两个换行 \r\n\r\nString respHeader = "HTTP/1.1 200 OK\r\n" +"Content-Type: text/html;charset=utf-8\r\n\r\n";String resp = respHeader + "<h1>hello world!</h1>";System.out.println("========hsptomcatv2返回的数据是=========");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();}}}}
}
  • 测试:
    浏览器 :http://localhost:8080/
    在这里插入图片描述

2.2.4 问题分析

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

2.3 实现任务阶段 3

处理 Servlet

2.3.1 Servlet 生命周期-回顾

在这里插入图片描述

2.3.2 需求分析/图解

● 需求分析如图, 浏览器请求 http://localhost:8080/hspCalServlet, 提交数据,完成计算任务,如果 servlet 不存在,返回 404
在这里插入图片描述
在这里插入图片描述

2.3.3 分析+代码实现

● 分析示意图
在这里插入图片描述
在这里插入图片描述
架构图:
在这里插入图片描述

2.3.3.1 优化1阶段

1.搭建结构
2.封装HttpServletRequest

1.模拟servlet
HspServlet.java

/*** 先搭建结构,后面在写内容*/
public interface HspServlet {}

HspHttpServlet.java

public abstract class HspHttpServlet implements HspServlet {}

HspCalServlet.java

public class HspCalServlet extends HspHttpServlet {}

2.模拟HttpServletRequest和HttpServletResponse
HspRequest.java

/*** 解读* 1. HspRequest 作用是封装http请求的数据* get /hspCalServlet?num1=10&num2=30* 2. 比如 method(get) 、 uri(/hspCalServlet) 、 还有参数列表 (num1=10&num2=30)* 3. HspRequest 作用就等价原生的servlet 中的HttpServletRequest* 4. 这里考虑的是GET请求*/
public class HspRequest {private String method;private String uri;//存放参数列表 参数名-参数值 => HashMapprivate HashMap<String, String> parametersMapping =new HashMap<>();private InputStream inputStream = null;//构造器=> 对http请求进行封装 => 可以将老师写的代码封装成方法//inputStream 是和 对应http请求的socket关联public HspRequest(InputStream inputStream) {this.inputStream = inputStream;//完成对http请求数据的封装..encapHttpRequest();}/*** 将http请求的相关数据,进行封装,然后提供相关的方法,进行获取*/private void encapHttpRequest() {System.out.println("HspRequest init()");try {//inputstream -> BufferedReaderBufferedReader 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 - /hspCalServlet?num1=10&num2=30 - HTTP/1.1String[] requestLineArr = requestLine.split(" ");//得到methodmethod = requestLineArr[0];//解析得到 /hspCalServlet//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=30String 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=10for (String parameterPair : parametersPair) {//parameterVal ["num1", "10"]String[] parameterVal = parameterPair.split("=");if (parameterVal.length == 2) {//放入到 parametersMappingparametersMapping.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;}@Overridepublic String toString() {return "HspRequest{" +"method='" + method + '\'' +", uri='" + uri + '\'' +", parametersMapping=" + parametersMapping +'}';}
}

细节补充:
在Java中,Socket和流(Stream)通常是一起使用的,用于进行网络数据传输。如果关闭与Socket相关联的流会导致Socket也关闭的问题,取决于关闭流的方式。
如果使用Socket对象的close()方法关闭与其关联的输入流或输出流,则会同时关闭该Socket,因为这些流是通过Socket来创建的。例如:

Socket socket = new Socket("example.com", 80);
OutputStream os = socket.getOutputStream();
os.close(); // 关闭输出流,同时也会关闭socket

HspResponse

/*** 老师解读* 1. HspResponse对象可以封装OutputStream(是socket关联)* 2. 即可以通过 HspResponse对象 返回Http响应给浏览器/客户端* 3. HspResponse对象 的作用等价于原生的servlet的 HttpServletResponse*/
public class HspResponse {}

3.改进HspRequestHandler

/*** 解读* 1. HspRequestHandler 对象是一个线程对象* 2. 处理一个http请求的*/
public class HspRequestHandler implements Runnable {//定义Socketprivate Socket socket = null;public HspRequestHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {//这里我们可以对客户端/浏览器进行IO编程/交互try {//这里我们先死后活HspRequest hspRequest = new HspRequest(socket.getInputStream());String num1 = hspRequest.getParameter("num1");String num2 = hspRequest.getParameter("num2");System.out.println("请求的参数num1= " + num1);System.out.println("请求的参数num2= " + num2);System.out.println("hspRequest= " + hspRequest);// 构建一下http响应头// 返回的http的响应体和响应头之间有两个换行 \r\n\r\nString respHeader = "HTTP/1.1 200 OK\r\n" +"Content-Type: text/html;charset=utf-8\r\n\r\n";String resp = respHeader + "<h1>hello world!</h1>";System.out.println("========hsptomcatv2返回的数据是=========");System.out.println(resp);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();}}}}
}

2.3.3.2 优化2阶段

1.完善HspResponse
2.构建Servlet模型
3.创建Util工具类
4.优化HspRequestHandler处理器

补充:HspResponse.java

/*** 老师解读* 1. HspResponse对象可以封装OutputStream(是socket关联)* 2. 即可以通过 HspResponse对象 返回Http响应给浏览器/客户端* 3. HspResponse对象 的作用等价于原生的servlet的 HttpServletResponse*/
public class HspResponse {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";//说明同学们如果有兴趣, 在编写更多的方法//比如 setContentType//在创建 HspResponse 对象时,传入的outputStream是和Socket关联的public HspResponse(OutputStream outputStream) {this.outputStream = outputStream;}//当我们需要给浏览器返回数据时,可以通过HspResponse 的输出流完成//public OutputStream getOutputStream() {return outputStream;}
}

完善模拟的Servlet
1.补充Servlet的生命周期

/*** 先搭建结构,后面在写内容* 老韩只保留了 三个核心方法声明*/
public interface HspServlet {void init() throws Exception;void service(HspRequest request, HspResponse response) throws IOException;void destroy();
}

2.完善HttpServlet

public abstract class HspHttpServlet implements HspServlet {@Overridepublic void service(HspRequest request, HspResponse 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 子类 HspCalServlet 实现public abstract void doGet(HspRequest request, HspResponse response);public abstract void doPost(HspRequest request, HspResponse response);
}

细节补充:
在这段代码中,this关键字和省略this关键字是没有区别的。因为在Java中,当局部变量和实例变量同名时,编译器会优先使用局部变量,如果需要访问实例变量,可以使用this关键字来代表当前对象。
 
因此,使用this.doGet()和直接调用doGet()方法实际上是等价的,不会影响程序的执行结果。只要doGet()方法已经定义在了当前类中,都可以直接调用。
 
不过,在实际开发中,推荐使用this关键字来调用当前对象的方法或访问成员变量,可以提高代码的可读性和可维护性。另外,使用this关键字还有助于避免和局部变量或参数命名冲突的情况。
 
因此,虽然this.doGet()和doGet()在语义上是一致的,但是在实际开发中,建议使用this.doGet()的方式来调用对象的方法。

3.实现HspCalServlet

public class HspCalServlet extends HspHttpServlet {@Overridepublic void doGet(HspRequest request, HspResponse 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 = HspResponse.respHeader+ "<h1>" + num1 + " + " + num2 + " = " + sum + " HspTomcatV3 - 反射+xml创建</h1>";try {outputStream.write(respMes.getBytes());outputStream.flush();outputStream.close();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void doPost(HspRequest request, HspResponse response) {this.doGet(request, response);}@Overridepublic void init() throws Exception {}@Overridepublic void destroy() {}
}

WebUtils.java

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;}
}

改进优化HspRequestHandler处理器

/*** 老师解读* 1. HspRequestHandler 对象是一个线程对象* 2. 处理一个http请求的*/
public class HspRequestHandler implements Runnable {//定义Socketprivate Socket socket = null;public HspRequestHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {//这里我们可以对客户端/浏览器进行IO编程/交互try {//这里我们先死后活HspRequest hspRequest = new HspRequest(socket.getInputStream());//这里我们可以同HspResponse对象,返回数据给浏览器/客户端HspResponse hspResponse = new HspResponse(socket.getOutputStream());//创建HspCalServlet对象-> 一会我们再用反射来构建对象HspCalServlet hspCalServlet = new HspCalServlet();hspCalServlet.doGet(hspRequest, hspResponse);socket.close();} catch (IOException e) {e.printStackTrace();} finally {//最后一定确保socket要关闭if (socket != null) {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}}
}

2.3.3.3 优化3阶段

1.构造容器
2.配置web.xml
3.创建容器(存放servlet和servlet-mapping)
4.利用dom4j技术获取xml文件,反射创建对象

思路分析
在这里插入图片描述
拷贝web.xml
在这里插入图片描述
引入dom4j的依赖

<!--导入dom4j-->
<dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.1</version>
</dependency>

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 , 所以不识别, 没有关系, 直接忽略爆红..--><servlet><servlet-name>HspCalServlet</servlet-name><servlet-class>com.hspedu.tomcat.servlet.HspCalServlet</servlet-class></servlet><servlet-mapping><servlet-name>HspCalServlet</servlet-name><url-pattern>/hspCalServlet</url-pattern></servlet-mapping>
</web-app>

HspTomcatV3.java

/*** 第3版的Tomcat, 实现通过xml+反射来初始化容器*/
public class HspTomcatV3 {//1. 存放容器 servletMapping// -ConcurrentHashMap// -HashMap// key            - value// ServletName    对应的实例public static final ConcurrentHashMap<String, HspHttpServlet>servletMapping = new ConcurrentHashMap<>();//2容器 servletUrlMapping// -ConcurrentHashMap// -HashMap// key                    - value// url-pattern       ServletNamepublic static final ConcurrentHashMap<String, String>servletUrlMapping = new ConcurrentHashMap<>();//变强..public static void main(String[] args) {init();}//直接对两个容器进行初始化public static 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<Element> elements = rootElement.elements();//遍历并过滤到 servlet servlet-mappingfor (Element element : elements) {if ("servlet".equalsIgnoreCase(element.getName())) {//这是一个servlet配置//System.out.println("发现 servlet");//使用反射将该servlet实例放入到servletMappingElement 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);}
}

2.3.3.4 优化4阶段

1.完善自定义Tomcat
2.修改优化代码
3.完善测试web.xml

  • 改进HspTomcatV3
/*** 第3版的Tomcat, 实现通过xml+反射来初始化容器*/
public class HspTomcatV3 {//1. 存放容器 servletMapping// -ConcurrentHashMap// -HashMap// key            - value// ServletName    对应的实例public static final ConcurrentHashMap<String, HspHttpServlet>servletMapping = new ConcurrentHashMap<>();//2容器 servletUrlMapping// -ConcurrentHashMap// -HashMap// key                    - value// url-pattern       ServletNamepublic static final ConcurrentHashMap<String, String>servletUrlMapping = 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("=====hsptomcatv3在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<Element> elements = rootElement.elements();//遍历并过滤到 servlet servlet-mappingfor (Element element : elements) {if ("servlet".equalsIgnoreCase(element.getName())) {//这是一个servlet配置//System.out.println("发现 servlet");//使用反射将该servlet实例放入到servletMappingElement 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);}
}
  • 改进HspRequestHandler
/*** 1. HspRequestHandler 对象是一个线程对象* 2. 处理一个http请求的*/
public class HspRequestHandler implements Runnable {//定义Socketprivate Socket socket = null;public HspRequestHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {//这里我们可以对客户端/浏览器进行IO编程/交互try {//这里我们先死后活HspRequest hspRequest = new HspRequest(socket.getInputStream());//这里我们可以同HspResponse对象,返回数据给浏览器/客户端HspResponse hspResponse = new HspResponse(socket.getOutputStream());//1. 得到 uri => 就是 servletUrlMapping 的 url-patternString uri = hspRequest.getUri();String servletName = HspTomcatV3.servletUrlMapping.get(uri);if (servletName == null) {servletName = "";}//2. 通过uri->servletName->servlet的实例 , 真正的运行类型是其子类 HspCalServletHspHttpServlet hspHttpServlet =HspTomcatV3.servletMapping.get(servletName);//3. 调用service , 通过OOP的动态绑定机制,调用运行类型的 doGet/doPostif (hspHttpServlet != null) {//得到hspHttpServlet.service(hspRequest, hspResponse);} else {//没有这个servlet , 返回404的提示信息String resp = HspResponse.respHeader + "<h1>404 Not Found</h1>";OutputStream outputStream = hspResponse.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();}}}}
}
  • 改进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 , 所以不识别, 没有关系, 直接忽略爆红..--><servlet><servlet-name>HspCalServlet</servlet-name><servlet-class>com.hspedu.tomcat.servlet.HspCalServlet</servlet-class></servlet><servlet-mapping><servlet-name>HspCalServlet</servlet-name><url-pattern>/hspCalServlet</url-pattern></servlet-mapping><!--多配置一个servlet--><servlet><servlet-name>Hsp6CalServlet</servlet-name><servlet-class>com.hspedu.tomcat.servlet.Hsp6CalServlet</servlet-class></servlet><servlet-mapping><servlet-name>Hsp6CalServlet</servlet-name><url-pattern>/hsp6CalServlet</url-pattern></servlet-mapping>
</web-app>
  • 增添Hsp6CalServlet
public class Hsp6CalServlet extends HspHttpServlet {@Overridepublic void doGet(HspRequest request, HspResponse 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 = HspResponse.respHeader+ "<h1>" + num1 + " * " + num2 + " = " + sum + " HspTomcatV3 - 反射+xml创建</h1>";try {outputStream.write(respMes.getBytes());outputStream.flush();outputStream.close();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void doPost(HspRequest request, HspResponse response) {}@Overridepublic void init() throws Exception {}@Overridepublic void destroy() {}
}

2.4 小结

回顾 Tomcat 工作架构图
在这里插入图片描述
BIO线程模型

BIO(Blocking I/O)即同步阻塞 I/O,是 Java 中最早使用的一种 I/O 模型。它的特点是使用传统的套接字(Socket)进行通信,当服务端接收到客户端请求后,为每个客户端连接创建一个新的线程进行处理,因此这种模型也被称为“一请求一应答”模式。

BIO 线程模型的流程如下:

  1. 服务端启动后,通过 ServerSocket 等待客户端连接请求。
  2. 客户端发起连接请求后,服务端接收到请求并创建一个新的线程(或使用线程池中的一个线程)处理客户端的请求。
  3. 服务端将接收到的数据存放到缓冲区中,并处理客户端请求。
  4. 服务端将处理结果返回给客户端,并关闭连接。

需要注意的是,BIO 模型中的线程数随着客户端连接数量的增加而增加,如果客户端连接数量过多,服务端可能会出现线程资源不足的情况。


NIO线程模型

NIO(Non-blocking I/O)即非阻塞 I/O,是 Java 中另一种 I/O 模型。相比于 BIO 模型,它使用单个线程或少量线程来处理多个客户端的请求,因此也被称为“多路复用”模式。

NIO 线程模型的流程如下:

  1. 服务端启动后,通过 ServerSocketChannel 等待客户端连接请求。
  2. 客户端发起连接请求后,服务端接收到请求,并将其注册到 Selector 中,由 Selector 监听客户端的事件。
  3. Selector 在监听到事件后,将其分发给对应的线程进行处理,例如读取客户端发送的数据。
  4. 完成对客户端请求的处理后,服务端将处理结果返回给客户端,并关闭连接。

需要注意的是,NIO 模型中使用单个线程或少量线程进行处理,因此可以避免频繁创建线程造成的开销,同时也能够提高系统的性能和吞吐量。但是,在处理客户端请求时需要使用较为复杂的事件轮询机制。

3. 课后作业

在这里插入图片描述
在这里插入图片描述
cal.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>计算器</title>
</head>
<body>
<h1>计算器</h1>
<form action="/hspCalServlet" method="get">num1:<input type="text" name="num1"><br/>num2:<input type="text" name="num2"><br/><input type="submit" value="提交">
</form>
</body>
</html>

web.xml

<servlet><servlet-name>HspCalServlet</servlet-name><servlet-class>com.hspedu.tomcat.servlet.HspCalServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>HspCalServlet</servlet-name><url-pattern>/hspCalServlet</url-pattern>
</servlet-mapping>

WebUtils.java

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");}//根据文件名来读取该文件->Stringpublic static String readHtml(String filename) {String path = com.hspedu.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();}
}

HspRequestHandler.java

/*** 老师解读* 1. HspRequestHandler 对象是一个线程对象* 2. 处理一个http请求的*/
public class HspRequestHandler implements Runnable {//定义Socketprivate Socket socket = null;public HspRequestHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {//这里我们可以对客户端/浏览器进行IO编程/交互try {//这里我们先死后活HspRequest hspRequest = new HspRequest(socket.getInputStream());//这里我们可以同HspResponse对象,返回数据给浏览器/客户端HspResponse hspResponse = new HspResponse(socket.getOutputStream());//1. 得到 uri => 就是 servletUrlMapping 的 url-patternString uri = hspRequest.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 = HspResponse.respHeader + content;//得到outputstream , 返回信息(静态页面)给浏览器OutputStream outputStream = hspResponse.getOutputStream();outputStream.write(content.getBytes());outputStream.flush();outputStream.close();socket.close();return;}String servletName = HspTomcatV3.servletUrlMapping.get(uri);if (servletName == null) {servletName = "";}//2. 通过uri->servletName->servlet的实例 , 真正的运行类型是其子类 HspCalServletHspHttpServlet hspHttpServlet =HspTomcatV3.servletMapping.get(servletName);//3. 调用service , 通过OOP的动态绑定机制,调用运行类型的 doGet/doPostif (hspHttpServlet != null) {//得到hspHttpServlet.service(hspRequest, hspResponse);} else {//没有这个servlet , 返回404的提示信息String resp = HspResponse.respHeader + "<h1>404 Not Found</h1>";OutputStream outputStream = hspResponse.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();}}}}
}
/*** @author 韩顺平* @version 1.0* 第3版的Tomcat, 实现通过xml+反射来初始化容器*/
public class HspTomcatV3 {//1. 存放容器 servletMapping// -ConcurrentHashMap// -HashMap// key            - value// ServletName    对应的实例public static final ConcurrentHashMap<String, HspHttpServlet>servletMapping = new ConcurrentHashMap<>();//2容器 servletUrlMapping// -ConcurrentHashMap// -HashMap// key                    - value// url-pattern       ServletNamepublic 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) {HspTomcatV3 hspTomcatV3 = new HspTomcatV3();hspTomcatV3.init();//启动hsptomcat容器hspTomcatV3.run();}//启动HspTomcatV3容器public void run() {try {ServerSocket serverSocket = new ServerSocket(8080);System.out.println("=====hsptomcatv3在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<Element> elements = rootElement.elements();//遍历并过滤到 servlet servlet-mappingfor (Element element : elements) {if ("servlet".equalsIgnoreCase(element.getName())) {//这是一个servlet配置//System.out.println("发现 servlet");//使用反射将该servlet实例放入到servletMappingElement 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);}
}

相关文章:

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

1. 前言 先看一个小案例&#xff0c; 引出对 Tomcat 底层实现思考 1.1 完成小案例 ● 快速给小伙伴完成这个小案例 0. 我们准备使用 Maven 来创建一个 WEB 项目, 老师先简单给小伙伴介绍一下 Maven 是什么, 更加详细的使用&#xff0c;我们还会细讲, 现在先使用一把 先创建…...

非监督学习简单介绍

文章目录 非监督学习简单介绍聚类K-meansHierarchical聚类DBSCAN 降维PCAt-SNE 其他非监督学习技术结论 非监督学习简单介绍 非监督学习是机器学习中的一种方法&#xff0c;其目标是基于数据的内在结构和关系&#xff0c;从而在无标签数据中识别样本的潜在结构和模式。非监督学…...

香港科技大学有什么好的专业?

香港科技大学创办于1991年10月&#xff0c;是一所坐落于香港清水湾半岛的公立研究型大学。大学设有4个学院&#xff1a;工学院、理学院、人文社会科学学院和工商管理学院&#xff0c;还设有2个研究院&#xff1a;香港科技大学公共政策和行政研究生院和香港科技大学霍英东研究院…...

【两个月算法速成】day04

本文以收录专题刷题记录 目录 24. 两两交换链表中的节点 题目链接 思路 代码 19. 删除链表的倒数第 N 个结点 题目链接 思路-双指针 代码 面试题 02.07. 链表相交 题目链接 思路 代码 24. 两两交换链表中的节点 题目链接 力扣 思路 建议使用虚拟节点&#xff0…...

【Python】实战:生成无关联单选问卷 csv《压疮风险评估表》

目录 一、适用场景 二、业务需求 三、Python 文件 &#xff08;1&#xff09;创建文件 &#xff08;2&#xff09;代码示例 四、csv 文件 一、适用场景 实战场景&#xff1a; 问卷全部为单选题问卷问题全部为必填问题之间无关联关系每个问题的答案分数不同根据问卷全部问…...

rsync 远程删除文件

rsync 远程删除文件 rsync是一个强大的远程数据同步工具,它不仅可以实现远程文件复制,也可以实现远程文件删除。 要使用rsync实现远程删除文件,可以使用如下命令: bash rsync -avz --delete usernameremotehost:/path/to/files /path/to/local/dir这个命令的主要参数: -a:归…...

LinkedBlockingQueue原理

1. 基本的入队出队 public class LinkedBlockingQueue<E> extends AbstractQueue<E>implements BlockingQueue<E>, java.io.Serializable {static class Node<E> {E item;/*** 下列三种情况之一* - 真正的后继节点* - 自己, 发生在出队时* - null, 表…...

哈希表题目:网格照明

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;网格照明 出处&#xff1a;1001. 网格照明 难度 6 级 题目描述 要求 在 n n \texttt{n} \times \texttt{n} nn 的二维网格 grid \texttt{grid}…...

Python多线程爬虫为何效率低下?解析原因并提高爬虫速度的方法

目录 一、知识点二、多线程语法GIL单线程多线程单线程多线程 最后的惊喜 一、知识点 线程&#xff08;Thread&#xff09;也叫轻量级进程&#xff0c;是操作系统能够进行运算调度的最小单位&#xff0c;它被包含在进程之中&#xff0c;是进程中的实际运作单位。线程自己不拥有…...

Python 标准方形信号定义(完美实现)

之前我们介绍了如何定义一个标准的正弦信号,这里我们做一下延申,简单说明一下如何定义一个方形函数。 方形信号表达式 square signal = g ( t ) = sign [ sin ⁡ ( 2 π f t +...

[Daimayuan] 走不出的迷宫(C++,图论,DP)

有一个 H H H 行 W W W 列的迷宫&#xff08;行号从上到下是 1 − H 1−H 1−H&#xff0c;列号从左到右是 1 − W 1−W 1−W&#xff09;&#xff0c;现在有一个由 . 和 # 组成的 H 行 W 列的矩阵表示这个迷宫的构造&#xff0c;. 代表可以通过的空地&#xff0c;# 代表不…...

【LeetCode: 1416. 恢复数组 | 暴力递归=>记忆化搜索=>动态规划 】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…...

centos7查看磁盘io

1.查看所使用到的命令为iostat&#xff0c;centos7没有自带iostat&#xff0c;需要安装一下 2.安装iostat命令 yum -y install sysstat 3.使用iostat命令 iostat %user&#xff1a;表示用户空间进程使用 CPU 时间的百分比 %nice&#xff1a;表示用户空间进程以降低优先级的…...

浅析低代码开发的典型应用构建场景v

在数字经济蓬勃发展的大势之下&#xff0c;企业软件开发人员供给不足、开发速度慢、开发成本高、数字化和智能化成效不明显等问题日益凸出&#xff0c;阻碍了企业的数字化转型。 而近年来&#xff0c;低代码的出现推动了经济社会的全面提效&#xff0c;也成为人才供求矛盾的润…...

3 连续模块(二)

3.5 零极点增益模块 在控制系统设计和分析中&#xff0c;常用的函数包括 传递函数&#xff08;tf&#xff09;、零极点&#xff08;zpk&#xff09;和状态空间&#xff08;ss&#xff09;函数 传递函数&#xff08;tf&#xff09;&#xff1a;用于表示线性时不变系统的输入输出…...

ElasticSearch 部署及安装ik分词器

ansiable playbook链接&#xff1a; https://download.csdn.net/download/weixin_43798031/87719490 需要注意的点&#xff1a;公司es集群现以三个角色部署分别为 Gateway、Master、Data 简单的理解可以理解为在每台机器上部署了三个es&#xff0c;以端口和配置文件来区分这三…...

汽车充电桩检测设备TK4860C交流充电桩检定装置

TK4860C是一款在交流充电桩充电过程中实时检测充电电量的标准仪器&#xff0c;仪器以新能源车为负载&#xff0c;结合宽动态范围测量技术、电能ms级高速刷新等技术&#xff0c;TK4860C实现充电全过程的累积电能精准计量&#xff0c;相比于传统的预设检定点的稳态计量&#xff0…...

备份和恢复:确保数据安全

备份和恢复&#xff1a;确保数据安全 在计算机领域中&#xff0c;备份和恢复数据对于确保数据安全至关重要。本文将介绍备份策略概述、使用mysqldump进行备份、使用MySQL Enterprise Backup进行备份、恢复数据以及备份和恢复的最佳实践。 备份策略概述 在制定备份策略时&…...

8 DWA(一)

8 DWA DMA简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取&#xff08;可以直接访问32内部存储器&#xff0c;包括内存SRAM&#xff0c;Flash&#xff09; DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&#x…...

mysql慢查询日志

概念 MySQL的慢查询日志是MySQL提供的一种日志记录&#xff0c;它用来记录在MySQL中响应时间超过阀值的语句&#xff0c;具体指运行时间超过long_query_time值的SQL&#xff0c;则会被记录到慢查询日志中。long_query_time的默认值为10&#xff0c;意思是运行10秒以上的语句。…...

Sentinel介绍及搭建

分布式流量防护 服务雪崩 服务提供者不可用导致服务调用者也跟着不可用&#xff0c;以此类推引起整个链路中的所有微服务都不可用 分布式流量防护 在分布式系统中&#xff0c;服务之间的相互调用会生成分布式流量。如何通过组件进行流量防护&#xff0c;并有效控制流量&…...

最受信任的低代码平台排行榜

近年来&#xff0c;随着数字化转型的兴起&#xff0c;低代码平台获得了大量关注。它允许用户在几乎没有编码知识的情况下创建应用程序&#xff0c;从而使企业能够简化其流程并提高效率。随着低代码平台的日益流行&#xff0c;要确定哪些平台最可靠、最值得信赖并非易事。在本文…...

Django框架之创建项目、应用并配置数据库

django3.0框架创建项目、应用并配置数据库 创建项目 进入命令行 新建一个全英文的目录 进入目录 输入命令 django-admin startproject project 项目目录层级 查看当前目录层级 tree /f 目录文件说明 创建数据库 做一个学生管理系统做演示&#xff0c;使用navicat创建数据…...

软件测试之基础概念学习篇(需求 + 测试用例 + 开发模型 + 测试模型 + BUG)

文章目录 1. 什么是软件测试2. 软件测试和软件开发的区别3. 软件测试和软件调试的区别4. 什么是需求1&#xff09;以需求为依据设计测试用例 5. 测试用例是什么6. 什么是 BUG&#xff08;软件错误&#xff09;7. 五个开发模型1&#xff09;瀑布模型2&#xff09;螺旋模型3&…...

Windows下版本控制器(SVN) - 1、开发中的实际问题+2、版本控制简介

文章目录 基础知识-Windows下版本控制器(SVN)1、开发中的实际问题2、版本控制简介2.1 版本控制[Revision control]2.2 Subversion2.3 Subversion 的优良特性2.4 SVN 的工作原理&#xff1a;2.5 SVN 基本操作 本人其他相关文章链接 基础知识-Windows下版本控制器(SVN) 1、开发中…...

Learning Dynamic Facial Radiance Fields for Few-Shot Talking Head Synthesis 笔记

Learning Dynamic Facial Radiance Fields for Few-Shot Talking Head Synthesis 笔记 摘要 Talking head synthesis is an emerging technology with wide applications in film dubbing, virtual avatars and online education. Recent NeRF-based methods generate more n…...

SpringBoot 项目整合 Redis 教程详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

3ASC25H214 DATX130以力控制为基础的装配应用方面已经形成了一个解决方案

​ 3ASC25H214 DATX130以力控制为基础的装配应用方面已经形成了一个解决方案 ABB的机器人解决方案最终选择了IRB6400机器人 ABB的解决方案 ABB一直都在不断地研究和开发机器人应用的新技术&#xff0c;有一部分研究活动是与大学进行合作的&#xff0c;其中一项是ABB的科学家和…...

Java的位运算

目录 1 Java中支持的位运算 2 位运算规则 3 逻辑运算 3.1 与运算&#xff08;&&#xff09; 3.2 或运算&#xff08;|&#xff09; 3.3 异或运算&#xff08;^&#xff09; 3.3 取反运算&#xff08;~&#xff09; 4 位移操作 4.1 左移&#xff08;<<&#…...

FastDFS分布式文件存储

FastDFS文件上传 简介&#xff1a; 主要解决&#xff1a;大容量的文件存储和高并发访问的问题 论坛&#xff1a;https://bbs.chinaunix.net 下载网站&#xff1a;https://sourceforge.net/projects/fastdfs/files/ 安装参考&#xff1a;https://www.cnblogs.com/cxygg/p/1…...