Tomcat

1.访问执行流程

2.tomcat的执行流程

Tomcat的两个重要身份
1)http服务器
2)Tomcat是⼀个Servlet容器

3.tomcat的容器执行流程

当⽤户请求某个URL资源时

1)HTTP服务器会把请求信息使⽤ServletRequest对象封装起来

2)进⼀步去调⽤Servlet容器中某个具体的Servlet

3)在 2)中,Servlet容器拿到请求后,根据URL和Servlet的映射关系,找到相应的Servlet

4)如果Servlet还没有被加载,就⽤反射机制创建这个Servlet,并调⽤Servlet的init⽅法来完成初始化

5)接着调⽤这个具体Servlet的service⽅法来处理请求,请求处理结果使⽤ServletResponse对象封装

6)把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端

4.tomcat的结构图

5.tomcat两大功能

1.连接器(Coyote)connector

1.运行流程

负责对外交流: 处理Socket连接,负责⽹络字节流与Request和Response对象的转化

(1)Coyote 封装了底层的⽹络通信(Socket 请求及响应处理)

(2)Coyote 使Catalina 容器(容器组件)与具体的请求协议及IO操作⽅式完全解耦

(3)Coyote 将Socket 输⼊转换封装为 Request 对象,进⼀步封装后交由Catalina 容器进⾏处理,处

理请求完成后, Catalina 通过Coyote 提供的Response 对象将结果写⼊输出流

(4)Coyote 负责的是具体协议(应⽤层)和IO(传输层)相关内容

2.组件

EndPoint EndPoint 是 Coyote 通信端点,即通信监听的接⼝,是具体Socket接收和发送处理器,是对传输层的抽象,因此EndPoint⽤来实现TCP/IP协议的

Processor Processor⽤来实现HTTP协议,Processor接收来⾃EndPoint的Socket,读取字节流解析成Tomcat Request和Response对象,并通过Adapter将其提交到容器处理,Processor是对应⽤层协议的抽象

ProtocolHandler Coyote 协议接⼝, 通过Endpoint 和 Processor , 实现针对具体协议的处理能⼒

Adapter CoyoteAdapter负责将Tomcat Request转成ServletRequest,再调⽤容器

2.容器组件container

负责内部处理:加载和管理Servlet,以及具体处理Request请求;

6.server.xml

1.每⼀个Service实例下可以有多个Connector实例和⼀个Container实例

<?xml version="1.0" encoding="UTF-8"?>
<!--Licensed to the Apache Software Foundation (ASF) under one or morecontributor license agreements.  See the NOTICE file distributed withthis work for additional information regarding copyright ownership.The ASF licenses this file to You under the Apache License, Version 2.0(the "License"); you may not use this file except in compliance withthe License.  You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.
-->
<!-- Note:  A "Server" is not itself a "Container", so you may notdefine subcomponents such as "Valves" at this level.Documentation at /docs/config/server.html--><!--Server 根元素,创建⼀个Server实例,⼦标签有 Listener、GlobalNamingResources、
Service
-->
<!--port:关闭服务器的监听端⼝shutdown:关闭服务器的指令字符串
-->
<Server port="8005" shutdown="SHUTDOWN"><!-- 以⽇志形式输出服务器 、操作系统、JVM的版本信息 --><Listener className="org.apache.catalina.startup.VersionLoggerListener" /><!-- Security listener. Documentation at /docs/config/listeners.html<Listener className="org.apache.catalina.security.SecurityListener" />--><!--APR library loader. Documentation at /docs/apr.html --><!-- 加载(服务器启动) 和 销毁 (服务器停⽌) APR。 如果找不到APR库, 则会输出⽇志, 并
不影响 Tomcat启动 --><Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /><!-- Prevent memory leaks due to use of particular java/javax APIs--><!-- 避免JRE内存泄漏问题 --><Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /><!-- 加载(服务器启动) 和 销毁(服务器停⽌) 全局命名服务 --><Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /><!-- 在Context停⽌时重建 Executor 池中的线程, 以避免ThreadLocal 相关的内存泄漏 --><Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /><!-- Global JNDI resourcesDocumentation at /docs/jndi-resources-howto.html--><!--定义服务器的全局JNDI资源 --><GlobalNamingResources><!-- Editable user database that can also be used byUserDatabaseRealm to authenticate users--><Resource name="UserDatabase" auth="Container"type="org.apache.catalina.UserDatabase"description="User database that can be updated and saved"factory="org.apache.catalina.users.MemoryUserDatabaseFactory"pathname="conf/tomcat-users.xml" /></GlobalNamingResources><!-- A "Service" is a collection of one or more "Connectors" that sharea single "Container" Note:  A "Service" is not itself a "Container",so you may not define subcomponents such as "Valves" at this level.Documentation at /docs/config/service.html--><!--该标签⽤于创建 Service 实例,默认使⽤ org.apache.catalina.core.StandardService。默认情况下,Tomcat 仅指定了Service 的名称, 值为 "Catalina"。Service ⼦标签为 : Listener、Executor、Connector、Engine,其中:Listener ⽤于为Service添加⽣命周期监听器,Executor ⽤于配置Service 共享线程池,Connector ⽤于配置Service 包含的链接器,Engine ⽤于配置Service中链接器对应的Servlet 容器引擎
--><Service name="Catalina"><!--The connectors can use a shared executor, you can define one or more named thread pools--><!--<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"maxThreads="150" minSpareThreads="4"/>--><!--  默认情况下,Service 并未添加共享线程池配置。 如果我们想添加⼀个线程池, 可以在<Service> 下添加如下配置:name:线程池名称,⽤于 Connector中指定namePrefix:所创建的每个线程的名称前缀,⼀个单独的线程名称为
namePrefix+threadNumbermaxThreads:池中最⼤线程数minSpareThreads:活跃线程数,也就是核⼼池线程数,这些线程不会被销毁,会⼀直存在maxIdleTime:线程空闲时间,超过该时间后,空闲线程会被销毁,默认值为6000(1分钟),单位毫秒maxQueueSize:在被执⾏前最⼤线程排队数⽬,默认为Int的最⼤值,也就是⼴义的⽆限。除⾮特
殊情况,这个值 不需要更改,否则会有请求不会被处理的情况发⽣prestartminSpareThreads:启动线程池时是否启动 minSpareThreads部分线程。默认值为false,即不启动threadPriority:线程池中线程优先级,默认值为5,值从1到10className:线程池实现类,未指定情况下,默认实现类为
org.apache.catalina.core.StandardThreadExecutor。如果想使⽤⾃定义线程池⾸先需要实现org.apache.catalina.Executor接⼝
--><Executor name="commonThreadPool"namePrefix="thread-exec-"maxThreads="200"minSpareThreads="100"maxIdleTime="60000"maxQueueSize="Integer.MAX_VALUE"prestartminSpareThreads="false"threadPriority="5"className="org.apache.catalina.core.StandardThreadExecutor"/><!--
port:端⼝号,Connector ⽤于创建服务端Socket 并进⾏监听, 以等待客户端请求链接。如果该属性设置
为0, Tomcat将会随机选择⼀个可⽤的端⼝号给当前Connector 使⽤
protocol:当前Connector ⽀持的访问协议。 默认为 HTTP/1.1 , 并采⽤⾃动切换机制选择⼀个基于 JAVA
NIO 的链接器或者基于本地APR的链接器(根据本地是否含有Tomcat的本地库判定)
connectionTimeOut:
Connector 接收链接后的等待超时时间, 单位为 毫秒。 -1 表示不超时。
redirectPort:当前Connector 不⽀持SSL请求, 接收到了⼀个请求, 并且也符合security-constraint 约束,
需要SSL传输,Catalina⾃动将请求重定向到指定的端⼝。
executor:指定共享线程池的名称, 也可以通过maxThreads、minSpareThreads 等属性配置内部线程池。URIEncoding:⽤于指定编码URI的字符编码, Tomcat8.x版本默认的编码为 UTF-8 , Tomcat7.x版本默认为ISO-
8859-1
-->
<!--org.apache.coyote.http11.Http11NioProtocol , ⾮阻塞式 Java NIO 链接器--><!-- A "Connector" represents an endpoint by which requests are receivedand responses are returned. Documentation at :Java HTTP Connector: /docs/config/http.htmlJava AJP  Connector: /docs/config/ajp.htmlAPR (HTTP/AJP) Connector: /docs/apr.htmlDefine a non-SSL/TLS HTTP/1.1 Connector on port 8080--><Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" /><!-- A "Connector" using the shared thread pool--><!--<Connector executor="tomcatThreadPool"port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />--><!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443This connector uses the NIO implementation. The defaultSSLImplementation will depend on the presence of the APR/nativelibrary and the useOpenSSL attribute of theAprLifecycleListener.Either JSSE or OpenSSL style configuration may be used regardless ofthe SSLImplementation selected. JSSE style configuration is used below.--><!--<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"maxThreads="150" SSLEnabled="true"><SSLHostConfig><Certificate certificateKeystoreFile="conf/localhost-rsa.jks"type="RSA" /></SSLHostConfig></Connector>--><!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2This connector uses the APR/native implementation which always usesOpenSSL for TLS.Either JSSE or OpenSSL style configuration may be used. OpenSSL styleconfiguration is used below.--><!--<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"maxThreads="150" SSLEnabled="true" ><UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" /><SSLHostConfig><Certificate certificateKeyFile="conf/localhost-rsa-key.pem"certificateFile="conf/localhost-rsa-cert.pem"certificateChainFile="conf/localhost-rsa-chain.pem"type="RSA" /></SSLHostConfig></Connector>--><!-- Define an AJP 1.3 Connector on port 8009 --><Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /><!-- An Engine represents the entry point (within Catalina) that processesevery request.  The Engine implementation for Tomcat stand aloneanalyzes the HTTP headers included with the request, and passes themon to the appropriate Host (virtual host).Documentation at /docs/config/engine.html --><!-- You should set jvmRoute to support load-balancing via AJP ie :<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">--><!--name: ⽤于指定Engine 的名称, 默认为CatalinadefaultHost:默认使⽤的虚拟主机名称, 当客户端请求指向的主机⽆效时, 将交由默认的虚拟主机处理, 默认为localhost--><Engine name="Catalina" defaultHost="localhost"><!--For clustering, please take a look at documentation at:/docs/cluster-howto.html  (simple how to)/docs/config/cluster.html (reference documentation) --><!--<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>--><!-- Use the LockOutRealm to prevent attempts to guess user passwordsvia a brute-force attack --><Realm className="org.apache.catalina.realm.LockOutRealm"><!-- This Realm uses the UserDatabase configured in the global JNDIresources under the key "UserDatabase".  Any editsthat are performed against this UserDatabase are immediatelyavailable for use by the Realm.  --><Realm className="org.apache.catalina.realm.UserDatabaseRealm"resourceName="UserDatabase"/></Realm><!--Host 标签⽤于配置⼀个虚拟主机 --><Host name="www.2.com"  appBase="webapps"unpackWARs="true" autoDeploy="true"><!-- SingleSignOn valve, share authentication between web applicationsDocumentation at: /docs/config/valve.html --><!--<Valve className="org.apache.catalina.authenticator.SingleSignOn" />--><!--docBase:Web应⽤⽬录或者War包的部署路径。可以是绝对路径,也可以是相对于 Host appBase的相对路径。path:Web应⽤的Context 路径。如果我们Host名为localhost, 则该web应⽤访问的根路径为: http://localhost:8080/web_demo。--><Context docBase="/Users/yingdian/web_demo" path="/web3"></Context><!-- Access log processes all example.Documentation at: /docs/config/valve.htmlNote: The pattern used is equivalent to using pattern="common" --><Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"prefix="localhost_access_log" suffix=".txt"pattern="%h %l %u %t &quot;%r&quot; %s %b" /></Host><Host name="www.1.com"  appBase="webapps"unpackWARs="true" autoDeploy="true"><!-- SingleSignOn valve, share authentication between web applicationsDocumentation at: /docs/config/valve.html --><!--<Valve className="org.apache.catalina.authenticator.SingleSignOn" />--><!--docBase:Web应⽤⽬录或者War包的部署路径。可以是绝对路径,也可以是相对于 Host appBase的相对路径。path:Web应⽤的Context 路径。如果我们Host名为localhost, 则该web应⽤访问的根路径为: http://localhost:8080/web_demo。--><!-- Access log processes all example.Documentation at: /docs/config/valve.htmlNote: The pattern used is equivalent to using pattern="common" --><Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"prefix="localhost_access_log" suffix=".txt"pattern="%h %l %u %t &quot;%r&quot; %s %b" /></Host></Engine></Service>
</Server>

2.自定义tomcat

2.1 执行流程图


2.2 执行流程

以ServerSocket为核心,导向

ServerSocket serverSocket = new ServerSocket(port);//8080
Socket socket = serverSocket.accept();
socket.close();

1.加载配置文件

读取web.xml文件

key 为 /lagou (url-pattern路径)

value是 实例化后的servlet(LagouServlet)实例 存储到Map集合中

<?xml version="1.0" encoding="UTF-8" ?>
<web-app><servlet><servlet-name>lagou</servlet-name><servlet-class>server.LagouServlet</servlet-class></servlet><servlet-mapping><servlet-name>lagou</servlet-name><url-pattern>/lagou</url-pattern></servlet-mapping>
</web-app>
  private void loadServlet() {InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");SAXReader saxReader = new SAXReader();try {Document document = saxReader.read(resourceAsStream);Element rootElement = document.getRootElement();//获取节点是servlet的标签List<Element> selectNodes = rootElement.selectNodes("//servlet");for (int i = 0; i < selectNodes.size(); i++) {Element element =  selectNodes.get(i);// <servlet-name>lagou</servlet-name>Element servletnameElement = (Element) element.selectSingleNode("servlet-name");String servletName = servletnameElement.getStringValue();// <servlet-class>server.LagouServlet</servlet-class>Element servletclassElement = (Element) element.selectSingleNode("servlet-class");String servletClass = servletclassElement.getStringValue();// 根据servlet-name的值找到url-patternElement servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");//   /lagouString urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());}} catch (DocumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}

2. 定义线程池

(RequestProcessor)

// 定义一个线程池int corePoolSize = 10;int maximumPoolSize =50;long keepAliveTime = 100L;TimeUnit unit = TimeUnit.SECONDS;BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);ThreadFactory threadFactory = Executors.defaultThreadFactory();RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);

3. 请求进,调用线程池的线程

      */ while(true) {Socket socket = serverSocket.accept();RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);//requestProcessor.start();threadPoolExecutor.execute(requestProcessor);}
//========================================@Overridepublic void run() {try{InputStream inputStream = socket.getInputStream();// 封装Request对象和Response对象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();}}

1.Request请求

将请求头封装 获取Get请求方式以及路径

  // 构造器,输入流传入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);/**  /GET /lagou HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9**/String inputStr = new String(bytes);// 获取第一行请求头信息String firstLineStr = inputStr.split("\\n")[0];  // GET / HTTP/1.1String[] strings = firstLineStr.split(" ");this.method = strings[0];this.url = strings[1];System.out.println("=====>>method:" + method);System.out.println("=====>>url:" + url);}

2.Response请求

获取到绝对路径,判断File.isFile()然后输出静态文件

public class Response {private OutputStream outputStream;public Response() {}public Response(OutputStream outputStream) {this.outputStream = outputStream;}// 使用输出流输出指定字符串public void output(String content) throws IOException {outputStream.write(content.getBytes());}/**** @param path  url,随后要根据url来获取到静态资源的绝对路径,进一步根据绝对路径读取该静态资源文件,最终通过*                  输出流输出*              /-----> classes*/public void outputHtml(String path) throws IOException {// 获取静态资源文件的绝对路径String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);// 输入静态资源文件File file = new File(absoluteResourcePath);if(file.exists() && file.isFile()) {// 读取静态资源文件,输出静态资源StaticResourceUtil.outputStaticResource(new FileInputStream(file),outputStream);}else{// 输出404output(HttpProtocolUtil.getHttpHeader404());}}}

4.调用LagouServlet

package server;import java.io.IOException;public class LagouServlet extends HttpServlet {@Overridepublic void doGet(Request request, Response response) {//        try {//            Thread.sleep(100000);
//        } catch (InterruptedException e) {//            e.printStackTrace();
//        }String content = "<h1>LagouServlet get</h1>";try {response.output((HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content));} catch (IOException e) {e.printStackTrace();}}@Overridepublic void doPost(Request request, Response response) {String content = "<h1>LagouServlet post</h1>";try {response.output((HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content));} catch (IOException e) {e.printStackTrace();}}@Overridepublic void init() throws Exception {}@Overridepublic void destory() throws Exception {}
}

####工具类

package server;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;public class StaticResourceUtil {/*** 获取静态资源文件的绝对路径* @param path* @return*/public static String getAbsolutePath(String path) {String absolutePath = StaticResourceUtil.class.getResource("/").getPath();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;// 输出http请求头,然后再输出具体内容outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());// 读取内容输出long written = 0 ;// 已经读取的内容长度int byteSize = 1024; // 计划每次缓冲的长度byte[] bytes = new byte[byteSize];while(written < resourceSize) {if(written  + byteSize > resourceSize) {  // 说明剩余未读取大小不足一个1024长度,那就按真实长度处理byteSize = (int) (resourceSize - written);  // 剩余的文件内容长度bytes = new byte[byteSize];}inputStream.read(bytes);outputStream.write(bytes);outputStream.flush();written+=byteSize;}}
}
package server;/*** http协议工具类,主要是提供响应头信息,这里我们只提供200和404的情况*/
public class HttpProtocolUtil {/*** 为响应码200提供请求头信息* @return*/public static String getHttpHeader200(long contentLength) {return "HTTP/1.1 200 OK \n" +"Content-Type: text/html \n" +"Content-Length: " + contentLength + " \n" +"\r\n";}/*** 为响应码404提供请求头信息(此处也包含了数据内容)* @return*/public static String getHttpHeader404() {String str404 = "<h1>404 not found</h1>";return "HTTP/1.1 404 NOT Found \n" +"Content-Type: text/html \n" +"Content-Length: " + str404.getBytes().length + " \n" +"\r\n" + str404;}
}

================

迭代升级

1.口述流程

项目启动bootstrap.start()

  1. 加载server.xml,读取端口port 8080以及HostNameappBase的绝对路径
  2. 遍历文件夹,读取项目的web.xml文件,获取setvlet的class的类路径,以及url-pattern,实例化存储到对象中
  3. 请求进来时,截取Url判断相应的servlet,执行相应的请求

2.读取server.xml,web.xml

/*** 加载解析web.xml,初始化Servlet*/private void loadConfig() {InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");SAXReader saxReader = new SAXReader();try {Document document = saxReader.read(resourceAsStream);Element rootElement = document.getRootElement();List<Element> selectNodes = rootElement.selectNodes("//Host");for (int i = 0; i < selectNodes.size(); i++) {Host host = new Host();Element element = selectNodes.get(i);String hostName = element.attributeValue("name");String appBase = element.attributeValue("appBase");// 扫描appBase下的文件夹,每一个文件夹认为是一个项目(Context)File appBaseFolder = new File(appBase);File[] files = appBaseFolder.listFiles();for(File file: files) {if(file.isDirectory()) {Context context = new Context();String contextPath = file.getName();context.setPath(contextPath);// 构建Wrappers,一个Wrapper对应一个ServletFile webFile = new File(file,"web.xml");List<Wrapper> list = loadWebXml(webFile.getAbsolutePath());context.setWrappers(list);host.getContexts().add(context);}}host.setName(hostName);mapper.getHosts().add(host);}} catch (DocumentException | FileNotFoundException e) {e.printStackTrace();}}/*** 解析web.xml,构建Wrappers*/public List<Wrapper> loadWebXml(String webXmlPath) throws FileNotFoundException {List<Wrapper> list = new ArrayList<>();InputStream resourceAsStream = new FileInputStream(webXmlPath);SAXReader saxReader = new SAXReader();try {Document document = saxReader.read(resourceAsStream);Element rootElement = document.getRootElement();MyClassLoader myClassLoader = new MyClassLoader();List<Element> selectNodes = rootElement.selectNodes("//servlet");for (int i = 0; i < selectNodes.size(); i++) {Element element = selectNodes.get(i);// <servlet-name>lagou</servlet-name>Element servletnameElement = (Element) element.selectSingleNode("servlet-name");String servletName = servletnameElement.getStringValue();// <servlet-class>server.LagouServlet</servlet-class>Element servletclassElement = (Element) element.selectSingleNode("servlet-class");String servletClass = servletclassElement.getStringValue();// 根据servlet-name的值找到url-patternElement servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");// /lagouString urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();Class<?> aClass = myClassLoader.findClass(webXmlPath.replace("web.xml", "")  + "/",servletClass);HttpServlet servlet = (HttpServlet) aClass.newInstance();Wrapper wrapper = new Wrapper();wrapper.setUrlPattern(urlPattern);wrapper.setServlet(servlet);list.add(wrapper);}}catch (Exception e) {e.printStackTrace();}return list;}

3.类的实例化加载,动态加载.class

   Class<?> aClass = myClassLoader.findClass(webXmlPath.replace("web.xml", "")  + "/",servletClass);HttpServlet servlet = (HttpServlet) aClass.newInstance();
//=======================================================
public class MyClassLoader extends ClassLoader {/** * name class 类的绝对路径*/  @Override  protected Class<?> findClass(String basePath,String name)  {String myPath = "file://" + basePath +  name.replaceAll("\\.","/") + ".class";System.out.println(myPath);byte[] cLassBytes = null;Path path = null;try {path = Paths.get(new URI(myPath));cLassBytes = Files.readAllBytes(path);} catch (IOException | URISyntaxException e) {e.printStackTrace();}Class clazz = defineClass(name, cLassBytes, 0, cLassBytes.length);return clazz;}  }

4.判断URL的路径

 Servlet servlet = resolveServlet(request.getUrl());
//=========================/*** 从mapper中取出需要的servlet* @return*/private Servlet resolveServlet(String url) {String[] split = url.split("/");String contextFlag = split[1];String servletFlag = url.substring(("/" + contextFlag).length());// 此处认为只有这一个hostList<Context> contexts = mapper.getHosts().get(0).getContexts();for(Context context: contexts) {if(contextFlag.equalsIgnoreCase(context.getPath())) {List<Wrapper> wrappers = context.getWrappers();for(Wrapper wrapper:wrappers) {if(wrapper.getUrlPattern().equalsIgnoreCase(servletFlag)) {return wrapper.getServlet();}}}}return null;}

5.封装的类

5.1Host类

public class Host {private String name;private List<Context> contexts = new ArrayList<>();public String getName() {return name;}public void setName(String name) {this.name = name;}public List<Context> getContexts() {return contexts;}public void setContexts(List<Context> contexts) {this.contexts = contexts;}
}

5.2Mapper类

public class Mapper {private List<Host> hosts = new ArrayList<>();public List<Host> getHosts() {return hosts;}public void setHosts(List<Host> hosts) {this.hosts = hosts;}
}

5.3wapper类

public class Wrapper {private String urlPattern;private Servlet servlet;public String getUrlPattern() {return urlPattern;}public void setUrlPattern(String urlPattern) {this.urlPattern = urlPattern;}public Servlet getServlet() {return servlet;}public void setServlet(Servlet servlet) {this.servlet = servlet;}
}

3.jvm的类加载机制

3.1加载类

  1. 引导启动类加载器(BootstrapClassLoader) c++编写,加载java核⼼库 java.*,⽐如rt.jar中的类,构造ExtClassLoader和AppClassLoader

  2. 扩展类加载器(ExtClassLoader) java编写,加载扩展库 JAVA_HOME/lib/ext⽬录下的jar中的类,如classpath中的jre ,javax.*或者java.ext.dir指定位置中的类

  3. 系统类加载器 (SystemClassLoader/AppClassLoader)默认的类加载器,搜索环境变量 classpath 中指明的路径

3.2 双亲委派机制

3.2.1 什么是双亲委派

当某个类加载器需要加载某个.class⽂件时,它⾸先把这个任务委托给他的上级类加载器,递归这个操

作,如果上级的类加载器没有加载,⾃⼰才会去加载这个类。

3.2.2 作用

  1. 防⽌重复加载同⼀个.class。通过委托去向上⾯问⼀问,加载过了,就不⽤再加载⼀遍。保证数据安全。

2.保证核⼼.class不能被篡改。通过委托⽅式,不会去篡改核⼼.class,即使篡改也不会去加载,即使

加载也不会是同⼀个.class对象了。不同的加载器加载同⼀个.class也不是同⼀个.class对象。这样

保证了class执⾏安全(如果⼦类加载器先加载,那么我们可以写⼀些与java.lang包中基础类同名

的类, 然后再定义⼀个⼦类加载器,这样整个应⽤使⽤的基础类就都变成我们⾃⼰定义的类了。

Object类 -----> ⾃定义类加载器(会出现问题的,那么真正的Object类就可能被篡改了)

3.2.3 Tomcat 的类加载机制

tomcat 8.5 默认改变了严格的双亲委派机制

  1. ⾸先从 Bootstrap Classloader加载指定的类

    如果未加载到,则从 /WEB-INF/classes加载

    如果未加载到,则从 /WEB-INF/lib/*.jar 加载

  2. 如果未加载到,则依次从 System、Common、Shared 加载(在这最后⼀步,遵从双亲委派

机制)

Common 通⽤类加载器加载Tomcat使⽤以及应⽤通⽤的⼀些类,位于CATALINA_HOME/lib下,⽐如servlet-api.jar

Catalina ClassLoader ⽤于加载服务器内部可⻅类,这些类应⽤程序不能访问

Shared ClassLoader ⽤于加载应⽤程序共享类,这些类服务器不会依赖

Webapp ClassLoader,每个应⽤程序都会有⼀个独⼀⽆⼆的Webapp ClassLoader,他⽤来加载本应⽤程序 /WEB-INF/classes 和 /WEB-INF/lib 下的类。

4.tomcat对Https支持

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"maxThreads="150" schema="https" secure="true" SSLEnabled="true"><SSLHostConfig><Certificate certificateKeystoreFile="/Users/yingdian/workspace/servers/apache-tomcat-8.5.50/conf/lagou.keystore" certificateKeystorePassword="lagou123" type="RSA"/></SSLHostConfig>
</Connector>

5.tocmat的优化策略

5.1Tomcat⾃身配置的优化

(⽐如是否使⽤了共享线程池?IO模型?)

5.1.1调整tomcat线程池

  <Executor name="commonThreadPool"namePrefix="thread-exec-"maxThreads="200"minSpareThreads="100"maxIdleTime="60000"maxQueueSize="Integer.MAX_VALUE"prestartminSpareThreads="false"threadPriority="5"className="org.apache.catalina.core.StandardThreadExecutor"/><Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443"executor="commonThreadPool"/>

5.1.2禁⽤ A JP 连接器

禁用下面这段
<!-- Define an AJP 1.3 Connector on port 8009 --><Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

5.1.3调整 IO 模式

Tomcat8之前的版本默认使⽤BIO(阻塞式IO),对于每⼀个请求都要创建⼀个线程来处理,不适

合⾼并发;Tomcat8以后的版本默认使⽤NIO模式(⾮阻塞式IO)

protocol就是Io模型

<Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443"executor="commonThreadPool"/>

5.1.4动静分离

可以使⽤Nginx+Tomcat相结合的部署⽅案,Nginx负责静态资源访问,Tomcat负责Jsp等动态资源访问处理(因为Tomcat不擅⻓处理静态资源)。

5.2 jvm优化

jvm内存模型

5.2.1设置虚拟机参数

JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

5.2.2垃圾回收策略

JDK1.7之后使用G1收集器

JAVA_OPTS="-XX:+UseConcMarkSweepGC"

垃圾回收性能指标

吞吐量:⼯作时间(排除GC时间)占总时间的百分⽐, ⼯作时间并不仅是程序运⾏的时间,还包

含内存分配时间。

暂停时间:由垃圾回收导致的应⽤程序停⽌响应次数/时间。

垃圾收集器

  1. 串⾏收集器(Serial Collector)

单线程执⾏所有的垃圾回收⼯作, 适⽤于单核CPU服务器

⼯作进程(单线程)垃圾回收线程进⾏垃圾收集**|—**⼯作进程继续

  1. 并⾏收集器(Parallel Collector)

    ⼯作进程 (多线程)垃圾回收线程进⾏垃圾收集**|—**⼯作进程继续

    ⼜称为吞吐量收集器(关注吞吐量), 以并⾏的⽅式执⾏年轻代的垃圾回收, 该⽅式可以显著降

    低垃圾回收的开销(指多条垃圾收集线程并⾏⼯作,但此时⽤户线程仍然处于等待状态)。适⽤于多

    处理器或多线程硬件上运⾏的数据量较⼤的应⽤

  2. 并发收集器(Concurrent Collector)

    以并发的⽅式执⾏⼤部分垃圾回收⼯作,以缩短垃圾回收的暂停时间。适⽤于那些响应时间优先于

    吞吐量的应⽤, 因为该收集器虽然最⼩化了暂停时间(指⽤户线程与垃圾收集线程同时执⾏,但不⼀

    定是并⾏的,可能会交替进⾏), 但是会降低应⽤程序的性能

  3. CMS收集器(Concurrent Mark Sweep Collector)

    并发标记清除收集器, 适⽤于那些更愿意缩短垃圾回收暂停时间并且负担的起与垃圾回收共享处

    理器资源的应⽤

  4. G1收集器(Garbage-First Garbage Collector)

    适⽤于⼤容量内存的多核服务器, 可以在满⾜垃圾回收暂停时间⽬标的同时, 以最⼤可能性实现

    ⾼吞吐量(JDK1.7之后)

6.nginx

6.1特点

  1. 跨平台:Nginx可以在⼤多数类unix操作系统上编译运⾏,⽽且也有windows版本

  2. Nginx的上⼿⾮常容易,配置也⽐较简单

  3. ⾼并发,性能好

  4. 稳定性也特别好,宕机概率很低

6.3 nginx.conf

#===================start=全局快
#user  nobody;
#运行用户
#worker进程数量 通常设置和Cpu数量相等
worker_processes  1;#全局错误日志以及Pid文件位置
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;#pid        logs/nginx.pid;
#===================end=全局快
#events块主要影响nginx服务器与⽤户的⽹络连接,⽐如worker_connections 1024,标识每个workderprocess⽀持的最⼤连接数为1024
events {worker_connections  1024;
}#负载均衡策略
upstream lagouServer{
#每个请求按照ip的hash结果分配,每⼀个客户端的请求会固定分配到同⼀个⽬标服务器处理,可以解决session问题ip_hash;#weight代表权重,默认每⼀个负载的服务器都为1,权重越⾼那么被分配的请求越多(⽤于服务器性能不均衡的场景)server 62.234.115.217:8080 weight=1;server 62.234.115.217:8082 weight=2;
}#http块http块是配置最频繁的部分,虚拟主机的配置,监听端⼝的配置,请求转发、反向代理、负载均衡等http {#引入mime类型的文件include       mime.types;default_type  application/octet-stream;#log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '#                  '$status $body_bytes_sent "$http_referer" '#                  '"$http_user_agent" "$http_x_forwarded_for"';#access_log  logs/access.log  main;sendfile        on;#tcp_nopush     on;#连接超时时间#keepalive_timeout  0;keepalive_timeout  65;#开启gzip压缩#gzip  on;server {#建通端口listen       80;#使用localhost访问server_name  localhost;#charset koi8-r;#access_log  logs/host.access.log  main;#默认请求location / {root   html; #默认的网站根目录位置index  index.html index.htm;#索引页,欢迎页}#反向代理,请求转发到其他服务器,根据前缀location /abc {proxy_pass http://127.0.0.1:8080;}location /def {proxy_pass http://127.0.0.1:8080;}#使用负载浚航location /def {proxy_pass http://myServer/;}#error_page  404              /404.html;# redirect server error pages to the static page /50x.html#错误提示页面error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}#动静态分离 静态资源处理,直接去nginx服务器目录中加载location /static/ {root staticData}# proxy the PHP scripts to Apache listening on 127.0.0.1:80##location ~ \.php$ {#    proxy_pass   http://127.0.0.1;#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000##location ~ \.php$ {#    root           html;#    fastcgi_pass   127.0.0.1:9000;#    fastcgi_index  index.php;#    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;#    include        fastcgi_params;#}# deny access to .htaccess files, if Apache's document root# concurs with nginx's one##location ~ /\.ht {#    deny  all;#}}# another virtual host using mix of IP-, name-, and port-based configuration##server {#    listen       8000;#    listen       somename:8080;#    server_name  somename  alias  another.alias;#    location / {#        root   html;#        index  index.html index.htm;#    }#}# HTTPS server##server {#    listen       443 ssl;#    server_name  localhost;#    ssl_certificate      cert.pem;#    ssl_certificate_key  cert.key;#    ssl_session_cache    shared:SSL:1m;#    ssl_session_timeout  5m;#    ssl_ciphers  HIGH:!aNULL:!MD5;#    ssl_prefer_server_ciphers  on;#    location / {#        root   html;#        index  index.html index.htm;#    }#}}

6.4命令

./nginx -s reload 来说明nginx信号处理这部分

1)master进程对配置⽂件进⾏语法检查

2)尝试配置(⽐如修改了监听端⼝,那就尝试分配新的监听端⼝)

3)尝试成功则使⽤新的配置,新建worker进程

4)新建成功,给旧的worker进程发送关闭消息

5)旧的worker进程收到信号会继续服务,直到把当前进程接收到的请求处理完毕后关闭

所以reload之后worker进程pid是发⽣了变化的

6.5nginx底层刨析

Nginx启动后,以daemon多进程⽅式在后台运⾏,包括⼀个Master进程和多个Worker进程,Master进程是领导,是⽼⼤,Worker进程是⼲活的⼩弟。

master进程

​ 主要是管理worker进程,⽐如:

​ 接收外界信号向各worker进程发送信号(./nginx -s reload)

​ 监控worker进程的运⾏状态,当worker进程异常退出后Master进程会⾃动重新启动新的worker进程等

worker进程

​ worker进程具体处理⽹络请求。多个worker进程之间是对等的,他们同等竞争来⾃客户端的请

​ 求,各进程互相之间是独⽴的。⼀个请求,只可能在⼀个worker进程中处理,⼀个worker进程,

​ 不可能处理其它进程的请求。worker进程的个数是可以设置的,⼀般设置与机器cpu核数⼀致。

拓展

1.AJP和HTTP连接器区别

tomcat的server.xml中的AJP和HTTP连接器区别

HTTP协议:连接器监听8080端口,负责建立HTTP连接。在通过浏览器访问Tomcat服务器的Web应用时,使用的就是这个连接器。  
AJP协议:连接器监听8009端口,负责和其他的HTTP服务器建立连接。在把Tomcat与其他HTTP服务器集成时,就需要用到这个连接器。

AJP(Apache JServ Protocol)是定向包协议。因为性能原因,使用二进制格式来传输可读性文本。WEB服务器通过TCP连接和SERVLET容器连接。

WEB服务器一般维持和Web容器的多个TCP Connecions,即TCP连接池,多个request/respons循环重用同一个Connection。

但是当Connection被分配(Assigned)到某个请求时,该请求完成之前,其他请求不得使用该连接。

单词

Protocol 协议 ==>ProtocolHandler

Rejected 拒绝

Block 块,阻塞
Policy 政策,方针
trigger触发

protocols 协议

extension 延伸

Executor 执行者

available 可用
digest 消化,摘要

internal 内部

standard 标准

Upgrade升级,往上

artifact 成品

TomcatNginx源码笔记分析相关推荐

  1. SpringBoot源码笔记分析

    SpringBoot源码笔记分析 1.基础 1.配置SpringBoot热部署 1.引入依赖 <dependency><groupId>org.springframework. ...

  2. NJ4X源码阅读分析笔记系列(一)——项目整体分析

    NJ4X源码阅读分析笔记系列(一)--项目整体分析 NJ4X是什么 参见NJ4X的官网:http://www.nj4x.com/ Java and .Net interfaces to support ...

  3. NJ4X源码阅读分析笔记系列(三)—— nj4x-ts深入分析

    NJ4X源码阅读分析笔记系列(三)-- nj4x-ts深入分析 一.系统的工作流程图(模块级) 其工作流程如下(以行情获取为例): 应用端向Application Server发起连接 应用服务器调用 ...

  4. Kafka 3.0 源码笔记(3)-Kafka 消费者的核心流程源码分析

    文章目录 前言 1. 消费者负载均衡的实现 2. 源码分析 2.1 KafkaConsumer 的初始化 2.2 KafkaConsumer 的消息拉取 2.2.1 消息拉取的准备及入口 2.2.2 ...

  5. angularjs源码笔记(3)--injector

    2019独角兽企业重金招聘Python工程师标准>>> 简介 injector是用来做参数自动注入的,例如 function fn ($http, $scope, aService) ...

  6. spring aop原理_Spring知识点总结!已整理成142页离线文档(源码笔记+思维导图)...

    写在前面 由于Spring家族的东西很多,一次性写完也不太现实.所以这一次先更新Spring[最核心]的知识点:AOP和IOC 无论是入门还是面试,理解AOP和IOC都是非常重要的.在面试的时候,我没 ...

  7. 推荐 7 个 Vue2、Vue3 源码解密分析的开源项目

    大家好,我是你们的 猫哥,那个不喜欢吃鱼.又不喜欢喵 的超级猫 ~ 1. 为什么要学习源码 ? 阅读优秀的代码的目的是让我们能够写出优秀的代码. 不给自己设限,不要让你周围人的技术上限成为你的上限.其 ...

  8. Toast源码深度分析

    目录介绍 1.最简单的创建方法 1.1 Toast构造方法 1.2 最简单的创建 1.3 简单改造避免重复创建 1.4 为何会出现内存泄漏 1.5 吐司是系统级别的 2.源码分析 2.1 Toast( ...

  9. DPDK 跟踪库tracepoint源码实例分析

    DPDK笔记 DPDK 跟踪库tracepoint源码实例分析 RToax 2021年4月 注意: 跟踪库 基于DPDK 20.05 DPDK跟踪库:trace library 1. trace流程源 ...

最新文章

  1. Instagram个性化推荐工程中三个关键技术是什么?
  2. Java基于Swing和Netty仿QQ界面聊天小项目
  3. Ubuntu查看端口占用情况
  4. Tiny210编译和烧写u-boot步骤
  5. hdu1465 不容易系列之一(错排问题)
  6. 文本打开方式和二进制打开方式的区别是什么?
  7. [待]-optee的native_intr_handler中断处理流程
  8. 虚拟磁盘类型_一起来了解一下Window10系统中虚拟硬盘的相关操作
  9. 【OS学习笔记】三 计算机的启动过程
  10. word横向网格线设置在哪里_word排版技巧:如何防止行距随字号而改变?
  11. VMware vSphere 性能优化设计经验+优化方法 | 周末送资料
  12. 华为 MindSpore 喊你来开发 10 大 AI 模型,260 万奖金怎能没你一份?
  13. 今天,我们考大学是为了什么?
  14. excel : 如何快速跳到某一行
  15. nodeJS之TCP模块net
  16. 100 余个超实用网站
  17. Xpose安装 xposed下载出错 http://dl.xposed.info/repo
  18. 存储珍贵的数据和资源的好选择,铁威马NAS F2-221折腾体验
  19. eclipse安装GWT插件
  20. Unity:动态加载游戏等级.

热门文章

  1. python列透视_python – 在pandas数据帧中透视列和列值
  2. 按钮的android程序闪退,Android实例化控件有什么特殊要求吗??怎么程序加了这一段之后就闪退。。...
  3. Java黑皮书课后题第5章:*5.31(金融应用:计算CD价值)假设你用10 000美元投资一张CD,年利率为5.75%。编写程序,提示由用户输入一个金额数、年获利率、月份数,然后显示一个表格
  4. Java黑皮书课后题第5章:*5.18(使用循环语句打印4个图案)使用嵌套的循环语句,编写4个独立的程序打印下面的图案
  5. 解题:POI 2013 Triumphal arch
  6. 1小时钟回顾MySQL语法(中)
  7. 你们肯定都不相信每天能准时下班了
  8. nosql-redis学习 数据类型
  9. 修改GitHub记录中的invalid-email-address
  10. Thread的join方法使用解析