Tomcat簡單實現-- 5.1 多埠支援
阿新 • • 發佈:2020-08-16
Connector多埠支援
- 截至現在我們的伺服器只支援一個10086埠,下面改造server.xml檔案來實現對於多埠的支援。
Connector類
-
首先建立Connector類,這個類的於server和service之間的關係是:
- 一個Server指的是一個伺服器,一個伺服器可以開啟多個服務,也就是service
- 一個服務可以支援多個埠訪問,也就是Connector
-
讓Connector類繼承Runnable介面,這樣當解析server.xml檔案之後,就可以建立多個執行緒用來實現多埠支援。
-
public class Connector implements Runnable
-
port
屬性,指定埠號,從埠號獲取這個Connector是對應哪個埠的。 -
int port; public void setPort(int port){ this.port = port; }
-
Service屬性,經過上面的分析可知,一個Connector對應著一個Service,一個Service對應著多個Connector。
-
構造方法:
-
private Service service; publicv Connector(Service service){ this.service = service; }
-
將Service中啟動的服務移動到Connector中來。
-
package jerrymice.catalina; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.log.LogFactory; import com.sun.org.apache.bcel.internal.ExceptionConst; import jerrymice.http.Request; import jerrymice.http.Response; import jerrymice.util.Constant; import jerrymice.util.ThreadUtil; import jerrymice.util.WebXmlUtil; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; /** * @author xsl20 */ public class Connector implements Runnable{ int port; private Service service; public Service getService(){ return service; } public void setPort(int port){ this.port = port; } public Connector(Service service){ this.service = service; } public void init(){ LogFactory.get().info("Initializing Protocol [http-bio-{}], port"); } public void start(){ LogFactory.get().info("Starting ProtocolHandler [http-bio-{}],port"); new Thread(this).start(); } @Override public void run() { try{ ServerSocket serverSocket = new ServerSocket(port); while(true) { Socket socket = serverSocket.accept(); Runnable r = () -> { try { Request request = new Request(socket, service); Response response = new Response(); String uri = request.getUri(); Context context = request.getContext(); if ("/500.html".equals(uri)) { throw new Exception("this is a deliberately create exception"); } if ("/".equals(uri)) { uri = WebXmlUtil.getWelcomeFile(request.getContext()); } String filename = StrUtil.removePrefix(uri, "/"); File file = FileUtil.file(context.getDocBase(), filename); if (file.exists()){ // 如果檔案存在 byte[] body = FileUtil.readBytes(file); response.setBody(body); // 通過解析檔案的副檔名來獲取瀏覽器的mimeType String extName = FileUtil.extName(file); String mimeType = WebXmlUtil.getMimeType(extName); response.setContentType(mimeType); if ("timeConsume.html".equals(filename)){ Thread.sleep(1000); } }else { // 訪問檔案不存在的情況下 handle404(socket, uri); return; } handle200(socket, response); }catch (Exception e){ handle500(socket, e); LogFactory.get().info(e); }finally { // 將socket的關閉提取到最後 try{ socket.close(); }catch(IOException e){ LogFactory.get().info(e); } } }; ThreadUtil.run(r); } }catch (IOException e) { LogFactory.get().info(e); } } private static void handle200(Socket socket, Response response) throws IOException { // 獲取型別 String contentType = response.getContentType(); String headText = Constant.responseHead200; headText = StrUtil.format(headText, contentType); byte[] head = headText.getBytes(); // 獲取response中的html文字,這個html文字是通過writer寫到stringWriter字元流上的 byte[] body = response.getBody(); byte[] responseBytes = new byte[head.length + body.length]; ArrayUtil.copy(head, 0, responseBytes, 0, head.length); ArrayUtil.copy(body, 0, responseBytes, head.length, body.length); OutputStream outputStream = socket.getOutputStream(); outputStream.write(responseBytes); } private static void handle404(Socket socket, String uri) throws IOException{ OutputStream outputStream = socket.getOutputStream(); String responseText = StrUtil.format(Constant.textFormat404, uri, uri); responseText = Constant.responseHead404 + responseText; byte[] responseBytes = responseText.getBytes(StandardCharsets.UTF_8); outputStream.write(responseBytes); } protected void handle500(Socket socket, Exception exception){ try { OutputStream outputStream = socket.getOutputStream(); //當發生異常時,首先Exception的異常堆疊,比如做一些平常的任務時,當異常發生時,會出錯的位置和依次呼叫的資訊,這些資訊就是放在 //異常堆疊中的,異常堆疊並不包含這個異常本身 StackTraceElement[] stackTraceElements = exception.getStackTrace(); // 準備一個StringBuilder來將這些資訊裝起來 StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(exception.toString()); stringBuilder.append("\r\n"); for (StackTraceElement element : stackTraceElements) { stringBuilder.append("\t"); stringBuilder.append(element.toString()); stringBuilder.append("\r\n"); } String msg = exception.getMessage(); if (null != msg && msg.length() > 20) { msg = msg.substring(0, 19); } String text = StrUtil.format(Constant.textFormat500, msg, exception.toString(), stringBuilder.toString()); text = Constant.responseHead500 + text; byte[] responseBytes = text.getBytes(); outputStream.write(responseBytes); }catch(IOException e) { LogFactory.get().info(e); } } }
Service
-
Service
要做出對應的改動,現在一個Service
對應著多個Connector
類,所以新增屬性Connector列表 -
private List<Connector> connectors; public Service(Server server){ this.server = server; this.name = ServerXmlUtil.getServiceName(); this.engine = new Engine(this); this.connectors = SweverXmlUtil.getConnectors(this); }
-
init()
初始化方法很簡單。 -
public void init(){ System.out.println("length of connector" + connectors.size()); TimeInterval timeInterval = DateUtil.timer(); for (Connector connector : connectors){ connector.init(); } LogFactory.get().info("Initialization processed in {} ms",timeInterval.intervalMs()); for (Connector connector : connectors){ connector.start(); } }
Server
-
將原本的開啟服務和初始化等程式碼全部放到
Connector
類中,現在Server
方法變得非常輕盈 -
package jerrymice.catalina; import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.TimeInterval; import cn.hutool.core.io.FileUtil; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.log.LogFactory; import cn.hutool.system.SystemUtil; import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils; import jerrymice.http.Request; import jerrymice.http.Response; import jerrymice.util.Constant; import jerrymice.util.WebXmlUtil; import sun.awt.windows.WPrinterJob; import java.awt.*; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; /** * @author :xiaosong * @description:TODO * @date :2020/8/5 14:06 */ public class Server { private Service service; public Server(){ this.service = new Service(this); } public void start(){ TimeInterval timeInterval = DateUtil.timer(); logJvm(); init(); LogFactory.get().info("Server startup in {} ms", timeInterval.intervalMs()); } private void logJvm() { // 建立一個Map用於儲存各種資訊 Map<String, String> infoMap = new LinkedHashMap<>(); infoMap.put("Server version", "JerryMice 1.0.0"); infoMap.put("Server build", "2020-08-03"); infoMap.put("OS:\t", SystemUtil.get("os.name")); infoMap.put("OS version", SystemUtil.get("os.version")); infoMap.put("Architecture", SystemUtil.get("os.arch")); infoMap.put("Java Home", SystemUtil.get("java.home")); infoMap.put("JSM Version", SystemUtil.get("java.runtime.version")); infoMap.put("JVM Vendor", SystemUtil.get("java.vm.specification.vendor")); Set<String> keys = infoMap.keySet(); for (String key : keys) { // 呼叫hutool的LogFactory工廠函式獲取logger,logger會自動根據log4j.properties來對Log4j的Logger進行配置 LogFactory.get().info(key + ":\t\t" + infoMap.get(key)); } } public void init(){ service.start(); } }
ServerXmlUtil
-
Connector
類中的埠號port
是通過這個靜態工具類中的方法來解析檔案中已經設定好的埠號的。 -
public static List<Connector> getConnectors(Service service){ //一個service對應著多個connector List<Connector> connectors = new ArrayList<>(); String xml = FileUtil.readUtf8String(Constant.serverXmlFile); Document document = Jsoup.parse(xml); Elements elements = document.select("Connector"); for (Element element : elements){ int port = Convert.toInt(element.attr("port")); Connector connector = new Connector(service); connector.setPort(port); connectors.add(connector); } return connectors; }
Bootstrap
-
修改啟動類,以前是從
Service
類進行啟動,現在從Service
的Connector
類進行啟動。 -
public class Bootstrap { public static void main(String[] args) { Server server = new Server(); server.start(); } }
-
啟動結果
-
D:\Java\jdk1.8.0_251\bin\java.exe "-javaagent:D:\JetBrains\IntelliJ IDEA 2020.2\lib\idea_rt.jar=54153:D:\JetBrains\IntelliJ IDEA 2020.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Java\jdk1.8.0_251\jre\lib\charsets.jar;D:\Java\jdk1.8.0_251\jre\lib\deploy.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\access-bridge-64.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\cldrdata.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\dnsns.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\jaccess.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\jfxrt.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\localedata.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\nashorn.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\sunec.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\sunmscapi.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk1.8.0_251\jre\lib\ext\zipfs.jar;D:\Java\jdk1.8.0_251\jre\lib\javaws.jar;D:\Java\jdk1.8.0_251\jre\lib\jce.jar;D:\Java\jdk1.8.0_251\jre\lib\jfr.jar;D:\Java\jdk1.8.0_251\jre\lib\jfxswt.jar;D:\Java\jdk1.8.0_251\jre\lib\jsse.jar;D:\Java\jdk1.8.0_251\jre\lib\management-agent.jar;D:\Java\jdk1.8.0_251\jre\lib\plugin.jar;D:\Java\jdk1.8.0_251\jre\lib\resources.jar;D:\Java\jdk1.8.0_251\jre\lib\rt.jar;D:\JavaProjects\Jerrymice\out\production\Jerrymice;D:\JavaProjects\Jerrymice\lib\jspc_all.jar;D:\JavaProjects\Jerrymice\lib\junit-4.9.jar;D:\JavaProjects\Jerrymice\lib\servlet-api.jar;D:\JavaProjects\Jerrymice\lib\jsoup-1.12.1.jar;D:\JavaProjects\Jerrymice\lib\log4j-1.2.17.jar;D:\JavaProjects\Jerrymice\lib\hutool-all-4.3.1.jar jerrymice.Bootstrap 08 16, 2020 18:15:56 下午 jerrymice.catalina.Host scanContextOnWebAppsFolder INFO : Scanning webapps in webapps... 08 16, 2020 18:15:56 下午 jerrymice.catalina.Context <init> INFO : Deploying web application directory D:\JavaProjects\Jerrymice\webapps\b 08 16, 2020 18:15:56 下午 jerrymice.catalina.Context <init> INFO : Deployment of web application directory D:\JavaProjects\Jerrymice\webapps\b has finished in 2 ms 08 16, 2020 18:15:56 下午 jerrymice.catalina.Context <init> INFO : Deploying web application directory D:\JavaProjects\Jerrymice\webapps\dir1 08 16, 2020 18:15:56 下午 jerrymice.catalina.Context <init> INFO : Deployment of web application directory D:\JavaProjects\Jerrymice\webapps\dir1 has finished in 0 ms 08 16, 2020 18:15:56 下午 jerrymice.catalina.Context <init> INFO : Deploying web application directory D:\JavaProjects\Jerrymice\webapps\ROOT 08 16, 2020 18:15:56 下午 jerrymice.catalina.Context <init> INFO : Deployment of web application directory D:\JavaProjects\Jerrymice\webapps\ROOT has finished in 0 ms 08 16, 2020 18:15:56 下午 jerrymice.catalina.Host scanContextsByServerXml INFO : Scanning webapps from server.xml... 08 16, 2020 18:15:56 下午 jerrymice.catalina.Context <init> INFO : Deploying web application directory d:/Java/JavaProjects/Jerrymice/webapps/b 08 16, 2020 18:15:56 下午 jerrymice.catalina.Context <init> INFO : Deployment of web application directory d:/Java/JavaProjects/Jerrymice/webapps/b has finished in 0 ms 08 16, 2020 18:15:56 下午 jerrymice.catalina.Server logJvm INFO : Server version: JerryMice 1.0.0 08 16, 2020 18:15:56 下午 jerrymice.catalina.Server logJvm INFO : Server build: 2020-08-03 08 16, 2020 18:15:56 下午 jerrymice.catalina.Server logJvm INFO : OS: : Windows 10 08 16, 2020 18:15:56 下午 jerrymice.catalina.Server logJvm INFO : OS version: 10.0 08 16, 2020 18:15:56 下午 jerrymice.catalina.Server logJvm INFO : Architecture: amd64 08 16, 2020 18:15:56 下午 jerrymice.catalina.Server logJvm INFO : Java Home: D:\Java\jdk1.8.0_251\jre 08 16, 2020 18:15:56 下午 jerrymice.catalina.Server logJvm INFO : JSM Version: 1.8.0_251-b08 08 16, 2020 18:15:56 下午 jerrymice.catalina.Server logJvm INFO : JVM Vendor: Oracle Corporation length of connector3 08 16, 2020 18:15:56 下午 jerrymice.catalina.Connector init INFO : Initializing Protocol [http-bio-{}], port 08 16, 2020 18:15:56 下午 jerrymice.catalina.Connector init INFO : Initializing Protocol [http-bio-{}], port 08 16, 2020 18:15:56 下午 jerrymice.catalina.Connector init INFO : Initializing Protocol [http-bio-{}], port 08 16, 2020 18:15:56 下午 jerrymice.catalina.Service init INFO : Initialization processed in 1 ms 08 16, 2020 18:15:56 下午 jerrymice.catalina.Connector start INFO : Starting ProtocolHandler [http-bio-{}],port 08 16, 2020 18:15:56 下午 jerrymice.catalina.Connector start INFO : Starting ProtocolHandler [http-bio-{}],port 08 16, 2020 18:15:56 下午 jerrymice.catalina.Connector start INFO : Starting ProtocolHandler [http-bio-{}],port 08 16, 2020 18:15:56 下午 jerrymice.catalina.Server start INFO : Server startup in 3 ms