TomcatNginx源码笔记分析
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 "%r" %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 "%r" %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()
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加載類
引導啟動類加載器(BootstrapClassLoader) c++編寫,加載java核?庫 java.*,?如rt.jar中的類,構造ExtClassLoader和AppClassLoader
擴展類加載器(ExtClassLoader) java編寫,加載擴展庫 JAVA_HOME/lib/ext?錄下的jar中的類,如classpath中的jre ,javax.*或者java.ext.dir指定位置中的類
系統類加載器 (SystemClassLoader/AppClassLoader)默認的類加載器,搜索環境變量 classpath 中指明的路徑
3.2 雙親委派機制
3.2.1 什么是雙親委派
當某個類加載器需要加載某個.class?件時,它?先把這個任務委托給他的上級類加載器,遞歸這個操
作,如果上級的類加載器沒有加載,??才會去加載這個類。
3.2.2 作用
2.保證核?.class不能被篡改。通過委托?式,不會去篡改核?.class,即使篡改也不會去加載,即使
加載也不會是同?個.class對象了。不同的加載器加載同?個.class也不是同?個.class對象。這樣
保證了class執?安全(如果?類加載器先加載,那么我們可以寫?些與java.lang包中基礎類同名
的類, 然后再定義?個?類加載器,這樣整個應?使?的基礎類就都變成我們??定義的類了。
)
Object類 -----> ?定義類加載器(會出現問題的,那么真正的Object類就可能被篡改了)
3.2.3 Tomcat 的類加載機制
tomcat 8.5 默認改變了嚴格的雙親委派機制
?先從 Bootstrap Classloader加載指定的類
如果未加載到,則從 /WEB-INF/classes加載
如果未加載到,則從 /WEB-INF/lib/*.jar 加載
如果未加載到,則依次從 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時間)占總時間的百分?, ?作時間并不僅是程序運?的時間,還包
含內存分配時間。
暫停時間:由垃圾回收導致的應?程序停?響應次數/時間。
垃圾收集器
單線程執?所有的垃圾回收?作, 適?于單核CPU服務器
?作進程(單線程)垃圾回收線程進?垃圾收集**|—**?作進程繼續
并?收集器(Parallel Collector)
?作進程 (多線程)垃圾回收線程進?垃圾收集**|—**?作進程繼續
?稱為吞吐量收集器(關注吞吐量), 以并?的?式執?年輕代的垃圾回收, 該?式可以顯著降
低垃圾回收的開銷(指多條垃圾收集線程并??作,但此時?戶線程仍然處于等待狀態)。適?于多
處理器或多線程硬件上運?的數據量較?的應?
并發收集器(Concurrent Collector)
以并發的?式執??部分垃圾回收?作,以縮短垃圾回收的暫停時間。適?于那些響應時間優先于
吞吐量的應?, 因為該收集器雖然最?化了暫停時間(指?戶線程與垃圾收集線程同時執?,但不?
定是并?的,可能會交替進?), 但是會降低應?程序的性能
CMS收集器(Concurrent Mark Sweep Collector)
并發標記清除收集器, 適?于那些更愿意縮短垃圾回收暫停時間并且負擔的起與垃圾回收共享處
理器資源的應?
G1收集器(Garbage-First Garbage Collector)
適?于?容量內存的多核服務器, 可以在滿?垃圾回收暫停時間?標的同時, 以最?可能性實現
?吞吐量(JDK1.7之后)
6.nginx
6.1特點
跨平臺:Nginx可以在?多數類unix操作系統上編譯運?,?且也有windows版本
Nginx的上??常容易,配置也?較簡單
?并發,性能好
穩定性也特別好,宕機概率很低
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源码笔记分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分布式理论、架构设计(自定义RPC)
- 下一篇: Nginx的反向代理 和 负载均衡