1. 程式人生 > 實用技巧 >Tomcat簡單實現-- 5.1 多埠支援

Tomcat簡單實現-- 5.1 多埠支援

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類進行啟動,現在從ServiceConnector類進行啟動。

  • 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