tomcat原理模拟和tomcat优化

1、tomcat实现原理

servlet 没有主方法main,依赖tomcat才能运行,因为tomcat 有主方法main,由java编写

servlet中doGet和doPost方法属于非静态方法,只能依托new对象存在,tomcat无法new出来对象,因此tomcat无法事先知道他们的存在。而任何语言都可以通过类的所在的路径或目录获取类信息,去某个目录下遍历所有子文件,能够获取所有文件的路径信息。tomcat可以通过servlet注解,找到对应的类,servlet注解相当于给类加了标记。

Tomcat和Servlet的关系是,Tomcat是Servlet容器,负责处理客户端的请求并将请求传递给Servlet,然后将Servlet的响应返回给客户。Servlet是一种运行在支持Java语言的服务器上的组件。

静态页面请求资源时,tomcat拷贝Html页面发送给浏览器,浏览器对html进行解析,浏览器看到src/href自动发请求。Tomcat将Http请求文本接收并解析,然后封装成HttpServletRequest类型的request对象,所有的Http头数据可以通过request对象调用对应的方法查询到。Servlet要响应的信息封装为HttpServletResponse类型的response对象,通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给Tomcat,Tomcat就会将其变成响应文本的格式发送给浏览器。

浏览器对请求的资源会在content-type中标记类型,然后以相同类型返回,如请求的是html页面,返回的就是html页面;请求的是css,返回的就是css。

Tomcat的工作方式是,当它启动时,会把所有的Servlet都加载到内存中。具体而言,就是前端页面需要写请求路径和请求方法,一般是通过js的ajax给前端传输请求,这些请求借助socket接收,然后socket将这些信息根据http协议对这些字符串进行解析,识别出请求路径和请求参数,根据每个servlet中的注解找出请求路径,再根据请求路径判断调用哪个servlet。找到对应的Servlet后,Tomcat会调用该Servlet的相应方法(doGet或doPost)来处理这个请求。Servlet会解析请求参数,可能会做一些处理,然后返回一个响应。Servlet返回的响应会通过Tomcat传回给前端。这可能是一个HTML页面,也可能是一些JSON数据,取决于你的Servlet是如何处理的。前端接收到响应后,浏览器会根据响应的内容进行渲染。如果响应是一个HTML页面,浏览器就会展示这个页面。如果响应是一些JSON数据,那么前端可能会根据这些数据进行一些更新或者其他的操作。

具体流程如下图所示:

2、tomcat优化 

2.1 tomcat存在的问题

①挑选servlet很慢,因为跟硬盘交互;

②反射也很慢,就是遍历内存,CPU运算一次是纳秒级,这些都是毫秒级

2.2 tomcat优化一

挑选servlet后反射获取注解信息这一步可以进行优化,将servlet及对应的注解类信息放入一个map中,之后每次请求就直接通过map中的信息对servlet及注解信息进行筛选,只需要O(1)的时间复杂度,时间缩短为微秒级,tomcat启动时就会进行这一步操作。map中的value值是一个集合体,包含servlet对象,doget方法对象,dopost方法对象。

2.3 tomcat优化二

方法的调用是拷贝这一份方法压入栈中去执行,没有必要每次都创建对象执行方法,很占用内存,因为在操作系统中最小的内存分配单位是4KB,不管对象实际大小多大,每次最小分配4KB,大于4KB则是分配4KB的倍数,内存占的多影响运行速度,因此可以在tomcat启动阶段只创建一个对象,没有必要每次发送请求都创建一个对象获取方法执行,然后tomcat每次请求通过这一个对象进行方法的调用。因此,也可以只保留一个doGet和doPost方法,一个对象被多线程共用,类的对象保留一个,方法的对象保留一个,静态方法的调用不需要依托对象,非静态方法的调用需要依托对象

3、tomcat实现代码

①Client


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;public class Client extends Thread {//定义一个Socket对象Socket socket = null;public Client(String host, int port) {try {//需要服务器的IP地址和端口号,才能获得正确的Socket对象socket = new Socket(host, port);} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {//客户端一连接就可以写数据给服务器了new sendMessThread().start();super.run();try {// 读Sock里面的数据InputStream s = socket.getInputStream();byte[] buf = new byte[4096];int len = 0;while ((len = s.read(buf)) != -1) {System.out.println(getdate() + "  服务器说:  "+new String(buf, 0, len,"UTF-8"));}} catch (IOException e) {e.printStackTrace();}}//往Socket里面写数据,需要新开一个线程class sendMessThread extends Thread{@Overridepublic void run() {super.run();//写操作Scanner scanner=null;OutputStream os= null;try {scanner=new Scanner(System.in);os= socket.getOutputStream();String in="";do {in=scanner.next();os.write((""+in).getBytes("UTF-8"));os.write(("哈哈哈哈").getBytes("UTF-8"));os.flush();} while (!in.equals("bye"));} catch (IOException e) {e.printStackTrace();}scanner.close();try {os.close();} catch (IOException e) {e.printStackTrace();}}}public static String getdate() {Date date = new Date();SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");String result = format.format(date);return result;}//函数入口public static void main(String[] args) {//需要服务器的正确的IP地址和端口号Client clientTest=new Client("62.234.175.16", 80);clientTest.start();}
}

②Server


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;public class AppServer {public static void main(String[] args) throws Exception {FindFile.init();System.out.println("服务启动");try (ServerSocket serverSocket = new ServerSocket(80);// 监听自己的服务器的80端口Socket clientSocket = serverSocket.accept();//ֻ只要发来的数据就接收到这里,网络流--->基本类型数组PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));) {System.out.println("客户端连接");String inputLine;String httpstr = "";while ((inputLine = in.readLine()) != null) {httpstr += inputLine;if(inputLine.equals("") ){ break;}}System.out.println("HTTP协议开始:" + httpstr + "###HTTP协议结束");String[] arr = httpstr.split("\\?");String str = arr[0];String[] strarr = str.split("/");String  url = strarr[strarr.length-1]; System.out.println("提取到的url:" + url );String params = arr[1].split(" HTTP")[0];//key1=22&key2=value2&key3=value3&key4=value4&key5=88String[] pramarr = params.split("&");Learn1.chose2(url,pramarr);} catch (IOException e) {System.out.println("Exception caught when trying to listen on port " + 80 + " or listening for a connection");System.out.println(e.getMessage());}System.out.println("服务退出");}
}

 ③FindFile


import java.io.File;
import java.util.HashMap;public class FindFile {public static HashMap<String,Object[]> map = new HashMap<>();public static void main(String[] args) throws Exception {}public static void init()  {File folder = new File("D:\\eclipse project\\testDemo\\src");try {traverseFolder2(folder);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static void traverseFolder(File folder) {File[] files = folder.listFiles();if (files != null) {for (File file : files) {if (file.isDirectory()) {traverseFolder(file); // 递归遍历子文件夹} else {System.out.println(file.getAbsolutePath().split("src")[1]); }}}}public static void traverseFolder2(File folder) throws Exception{File[] files = folder.listFiles();if (files != null) {for (File file : files) {if (file.isDirectory()) {traverseFolder2(file); } else {String filepath = file.getAbsolutePath().split("src")[1];filepath = filepath.substring(1,filepath.length());filepath = filepath.replace("\\", ".");if(filepath.endsWith("java")) {filepath = filepath.replace(".java", "");Class<?> cl = Class.forName(filepath);WebServlet2 annotation =  cl.getAnnotation(WebServlet2.class);if(annotation!=null) {String urlname = annotation.url();//System.out.println(filepath); //System.out.println("提取到的路径:" + urlname); map.put(urlname, new Object[] {cl.newInstance(), cl.getMethod("doGet", new Class[] {HSRequest.class}), cl.getMethod("doPost", new Class[] {HSRequest.class})});}}}}}}
}

④learn

package tomcat_you;
import java.lang.reflect.Method;
import java.util.Arrays;public class Learn1 {public static void chose(String url,String[] pramarr)throws Exception {String[] arr = {"tomcat_you.TestDemo2", "tomcat_you.TestDemo3",  "tomcat_you.TestDemo4"};Class<?>[] cls = new Class[arr.length];for(int i =0;i < cls.length; i++) {cls[i] = Class.forName(arr[i]);WebServlet2 annotation =  cls[i].getAnnotation(WebServlet2.class);if(annotation!=null) {//模拟挑选出了servlet(带有注解的类)ࣩString urlname = annotation.url();if(url.equals(urlname)) {Object x = cls[i].newInstance();//   HttpServletDemo w = (HttpServletDemo)x;// w.doGet(22, "www");Method method = cls[i].getDeclaredMethod("doGet", new Class[] {HSRequest.class});HSRequest request = new HSRequest();for(String param : pramarr) {String[] arrw = param.split("=");request.put(arrw[0], arrw[1]);}method.invoke(x, request);}}else {continue;}}}//tomcat优化public static void chose2(String url,String[] pramarr)throws Exception {HSRequest request = new HSRequest();for(String param : pramarr) {String[] arrw = param.split("=");request.put(arrw[0], arrw[1]);}Object[] arr = FindFile.map.get(url);Method m = (Method) arr[1];m.invoke(arr[0], request);}
}

⑤HSRequest

package tomcat_you;import java.util.HashMap;public class HSRequest {private HashMap<String,String> map = new HashMap<>();public String getParameter(String key) {return map.get(key);}public void put(String key, String value) {map.put(key, value);}
}

⑥HttpServletDemo

package tomcat_you;public class HttpServletDemo {public void doGet(HSRequest request) {}public void doPost(HSRequest request){}
}

⑦WebServlet

package tomcat_you;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD,ElementType.TYPE})
public @interface WebServlet2 {public int age();// 为name指定初始值ֵpublic String url() default "小花";
}

 ⑧TestDemo

package tomcat_you;
@WebServlet2(url = "servlet1",age=12)
public class TestDemo2 extends HttpServletDemo{public void doGet(HSRequest request){String name = request.getParameter("name");String age = request.getParameter("age");System.out.println("姓名" + name + ",年龄:" + age);}public void doPost(HSRequest request){}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

fiddler捕获HTTPS

打开软件查看请求会出现&#xff1a;HTTPS decryption is disabled. Click to configure&#xff0c;表示已经禁用https捕获&#xff0c;点击黄色警告可以进行https配置&#xff0c;也可以选择菜单栏tools>options进行配置 勾选Decrypt HTTPS traffic → 点开 Actions → 重…

【接上篇】二、Flask学习之CSS(下篇)

上篇&#xff1a;二、Flask学习之CSS 3.8hover hover是用来美化鼠标悬停的效果的&#xff0c;当鼠标停放在某个区域&#xff0c;就会执行对应的hover操作。可以操作本标签的内容&#xff0c;也可以操作本标签下某一个标签的内容 3.9after <!DOCTYPE html> <html l…

【极问系列】springBoot集成elasticsearch出现Unable to parse response body for Response

【极问系列】 springBoot集成elasticsearch出现Unable to parse response body for Response 如何解决&#xff1f; 一.问题 #springboot集成elasticsearch组件,进行增删改操作的时候报异常Unable to parse response body for Response{requestLineDELETE /aurora-20240120/…

【精选】中间件 tomcat漏洞复现

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

一个简单的ETCD GUI工具

使用ETCD没有好用的GUI工具&#xff0c;随手用c#写了一个&#xff0c; 做得好玩的一个ETCD GUI工具&#xff0c;后面加上CLI 工具&#xff0c;类似于 redis Cli工具一样&#xff0c;简化在 Linux下面的操作&#xff0c;不知道有没有必要&#xff0c; git 地址如下&#xff0c;…

对Git更深入了解与学习

对Git更深入了解与学习 0. 前言0.1 工作区与暂存区 1. git remote update origin2. git push origin --delete 分支名 删除远端分支3. git remote4. git fetch5. git status5.1 git status 直观理解5.2 暂存与暂存取消 &#xff08;git restore&#xff09;5.3 push之后 6. git…

Linux内存管理:(九)内存规整

文章说明&#xff1a; Linux内核版本&#xff1a;5.0 架构&#xff1a;ARM64 参考资料及图片来源&#xff1a;《奔跑吧Linux内核》 Linux 5.0内核源码注释仓库地址&#xff1a; zhangzihengya/LinuxSourceCode_v5.0_study (github.com) 1. 引言 伙伴系统以页面为单位来管…

蓝桥杯真题(Python)每日练Day2

题目 题目分析 对于本题首先确定其数据结构为优先队列&#xff0c;即邮费最小的衣服优先寄&#xff0c;算法符合贪心算法。可以直接使用queue库的PriorityQueue方法实现优先队列。关于PriorityQueue的使用方法主要有&#xff1a; import queue q queue.Queue()# 队列 pq qu…

自然语言推断:注意力之注意(Attending)

注意&#xff08;Attending&#xff09; 第一步是将一个文本序列中的词元与另一个序列中的每个词元对齐。假设前提是“我确实需要睡眠”&#xff0c;假设是“我累了”。由于语义上的相似性&#xff0c;我们不妨将假设中的“我”与前提中的“我”对齐&#xff0c;将假设中的“累…

电脑pdf如何转换成word格式?用它实现pdf文件一键转换

pdf转word格式可以用于提取和重用pdf文档中的内容&#xff0c;有时候&#xff0c;我们可能需要引用或引用pdf文档中的一些段落、表格或数据&#xff0c;通过将pdf转换为可编辑的Word文档&#xff0c;可以轻松地复制和粘贴所需内容&#xff0c;节省我们的时间&#xff0c;那么如…

接口的返回值中所需信息作为其他接口入参使用(postman与jmeter的使用)

一、背景&#xff1a; 偶尔会用到一个场景&#xff0c;两个接口之前的调用有依赖关系&#xff0c;将其中一个的返回参数中的部分信息取出来作为入参在第二个接口中使用&#xff0c;代码内是比较好实现&#xff0c;只要定义一个变量&#xff0c;用于参数传递。 如果是测试过程中…

VMware 安装 CentOS7

目录 镜像下载VMware创建创建新的虚拟机直接自定义了选择镜像所在位置更改虚拟机的名称和存储位置&#xff08;尽量不要使用默认位置&#xff09;设置虚拟机的配置&#xff08;根据自己的情况而定&#xff09;设置虚拟机的内存&#xff08;根据自己情况而定&#xff09;设置网络…