手写一个民用Tomcat (08)

发布于:2024-04-26 ⋅ 阅读:(19) ⋅ 点赞:(0)

这次我们Tomcat 的改动是  指责分离,同时引入一个Wrapper封装

我们的Processor 是负责处理处理请求的任务

我们的Connector 是负责通信的 

详细代码如下

public class JxdHttpConnector implements Runnable {
    int minProcessors = 3;
    int maxProcessors = 10;
    int curProcessors = 0;
    //存放多个processor的池子
    Deque<JxdHttpProcessor> processorDeque = new ArrayDeque<>();
    private  ServletContainer container;

    //sessions map存放session
    public static Map<String, HttpSession> sessions = new ConcurrentHashMap<>();

    //创建新的session
    public static JxdSession createSession() {
        JxdSession session = new JxdSession();
        session.setValid(true);
        session.setCreationTime(System.currentTimeMillis());
        String sessionId = generateSessionId();
        session.setId(sessionId);
        sessions.put(sessionId, session);
        return (session);
    }

    /**
     * 生成随机数方法
     * @return
     */
    protected static synchronized String generateSessionId() {
        Random random = new Random();
        long seed = System.currentTimeMillis();
        random.setSeed(seed);
        byte bytes[] = new byte[16];
        random.nextBytes(bytes);
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            byte b1 = (byte) ((bytes[i] & 0xf0) >> 4);
            byte b2 = (byte) (bytes[i] & 0x0f);
            if (b1 < 10)
                result.append((char) ('0' + b1));
            else
                result.append((char) ('A' + (b1 - 10)));
            if (b2 < 10)
                result.append((char) ('0' + b2));
            else
                result.append((char) ('A' + (b2 - 10)));
        }
        return (result.toString());
    }


    @Override
    public void run() {
        ServerSocket serverSocket = null;
        int port = 8080;
        try {
            serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

        //初始化池子 最开始三个
        initProcessorDeque();

        while (true) {
            Socket socket = null;
            try {
                //这是单线程 一个请求一个请求获取socket
                socket = serverSocket.accept();

                //得到一个新的processor,这个processor从池中获取(池中有可能新建)
                JxdHttpProcessor processor = createProcessor();
                if (processor == null) {
                    socket.close();
                    continue;
                }
                processor.assign(socket);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void start() {
        Thread thread = new Thread(this);
        thread.start();
    }

    //从池子中获取一个processor,如果池子为空且小于最大限制,则新建一个
    private JxdHttpProcessor createProcessor() {
        synchronized (processorDeque) {
            if (processorDeque.size() > 0) {
                return processorDeque.pop();
            }
            if (curProcessors < maxProcessors) {
                return newProcessor();
            } else {
                return null;
            }
        }
    }

    private void initProcessorDeque() {
        for (int i = 0; i < minProcessors; i++) {
            JxdHttpProcessor processor = new JxdHttpProcessor(this);
            processor.start();
            processorDeque.push(processor);
        }
        curProcessors = minProcessors;
    }


    private JxdHttpProcessor newProcessor() {
        JxdHttpProcessor jxdHttpProcessor = new JxdHttpProcessor(this);
        jxdHttpProcessor.start();
        processorDeque.push(jxdHttpProcessor);
        curProcessors++;
        return processorDeque.pop();
    }

    public void recycle(JxdHttpProcessor processor) {
        processorDeque.push(processor);
    }


    public ServletContainer getContainer() {
        return container;
    }

    public void setContainer(ServletContainer container) {
        this.container = container;
    }
}

public class JxdHttpProcessor implements Runnable {
    boolean available = false;
    Socket socket;

    JxdHttpConnector connector;
    private int serverPort = 0;
    private boolean keepAlive = false;
    private boolean http11 = true;
    public JxdHttpProcessor(JxdHttpConnector connector) {
        this.connector = connector;
    }

    private void process(Socket socket) { //服务器循环等待请求并处理
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

        InputStream input = null;
        OutputStream output = null;

        // create Request object and parse
        JxdRequest request = new JxdRequest();
        try {
            output = socket.getOutputStream();
            keepAlive = true;
            //先关掉keepAlive 目前 为了方便测试
//            while (keepAlive) {
                request.parse(socket);
                // create Response object
                JxdResponse response = new JxdResponse(output);
                response.setRequest(request);
                request.setResponse(response);
                //handle session
                if (request.getSessionId() == null || request.getSessionId().equals("")) {
                    request.getSession(true);
                }
                response.setCharacterEncoding("UTF-8");
                response.addHeader(DefaultHeaders.CONTENT_TYPE_NAME, "text/html;charset=UTF-8");
                response.sendHeaders();//发送响应头
                //这段代码是测试用,可以获取的 请求参数 支持get 和post
                Map<String, String[]> map = request.getParameterMap();

                if (request.getUri().startsWith("/servlet/")) {
                    //加载动态资源
                    JxdServletProcessor jxdServletProcessor = new JxdServletProcessor(connector);
                    jxdServletProcessor.process(request, response);
                } else {
                    //加载静态资源
                    StaticResourceProcessor staticResourceProcessor = new StaticResourceProcessor();
                    staticResourceProcessor.process(request, response);
                }
                response.finishResponse();
                System.out.println("response header connection------"+response.getHeader("Connection"));
                if ("close".equals(response.getHeader("Connection"))) {
                    keepAlive = false;
                }
//            }
            //因为是多线程所以只能交给httpProcessor 来关闭
            socket.close();
        } catch (Exception ea) {
            ea.printStackTrace();
        }
    }


    @Override
    public void run() {
        while (true) {
            // 等待socket分配过来
            Socket socket = await();
            if (socket == null) continue;
            // 处理请求
            process(socket);
            // 回收processor
            connector.recycle(this);
        }
    }

    public void start() {
        Thread thread = new Thread(this);
        thread.start();
    }

    private synchronized Socket await() {
        // 等待connector提供一个新的socket
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 获得这个新的Socket
        Socket socket = this.socket;
        //设置标志为false
        available = false;
        //通知另外的线程
        notifyAll();
        return (socket);
    }

    public synchronized void assign(Socket socket) {
        // 等待connector提供一个新的socket
        while (available) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        // 获取到这个新的Socket
        this.socket = socket;
        // 把标志设置回去
        available = true;
        //通知另外的线程
        notifyAll();
    }
}

同时引入一个Wapper

/**
 * ServletContainer 主要用servletClsMap,servletInstanceMap 两个
 * map 管理Servlet
 */
public class ServletWrapper {
    private Servlet instance = null;
    private String servletClass;
    private ClassLoader loader;
    private String name;
    protected ServletContainer container = null;
    public ServletWrapper(String servletClass, ServletContainer container) {
        this.container = container;
        this.servletClass = servletClass;
        try {
            loadServlet();
        } catch (ServletException e) {
            e.printStackTrace();
        }
    }
    public ClassLoader getLoader() {
        if (loader != null)
            return loader;
        return container.getLoader();
    }

    public Servlet getServlet(){
        return this.instance;
    }
    public Servlet loadServlet() throws ServletException {
        if (instance!=null) return instance;

        Servlet servlet = null;
        String actualClass = servletClass;
        if (actualClass == null) {
            throw new ServletException("servlet class has not been specified");
        }
        ClassLoader classLoader = getLoader();
        Class classClass = null;
        try {
            if (classLoader!=null) {
                classClass = classLoader.loadClass(actualClass);
            }
        }
        catch (ClassNotFoundException e) {
            throw new ServletException("Servlet class not found");
        }
        try {
            servlet = (Servlet) classClass.newInstance();
        }
        catch (Throwable e) {
            throw new ServletException("Failed to instantiate servlet");
        }
        try {
            servlet.init(null);
        }
        catch (Throwable f) {
            throw new ServletException("Failed initialize servlet.");
        }
        instance =servlet;
        return servlet;
    }
    public void invoke(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        if (instance != null) {
            instance.service(request, response);
        }
    }
}

引入一个Servlet 容器 ,里边是一个map 用来管理 Servlet 不必每次都要创建

/**
 * ServletContainer 主要用servletClsMap,servletInstanceMap 两个
 * map 管理Servlet
 */
public class ServletContainer {
    JxdHttpConnector connector = null;
    ClassLoader loader = null;
    //包含servlet类和实例的map
    Map<String, String> servletClsMap = new ConcurrentHashMap<>(); //servletName -
    Map<String, ServletWrapper> servletInstanceMap = new ConcurrentHashMap<>();//servletN


    public ServletContainer() {
        try {
// create a URLClassLoader
            URL[] urls = new URL[1];
            URLStreamHandler streamHandler = null;
            File classPath = new File(JxdHttpServer.WEB_ROOT);
            String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();
            urls[0] = new URL(null, repository, streamHandler);
            loader = new URLClassLoader(urls);
        } catch (IOException e) {
            System.out.println(e.toString());
        }
    }

    public String getInfo() {
        return null;
    }

    public ClassLoader getLoader() {
        return this.loader;
    }

    public void setLoader(ClassLoader loader) {
        this.loader = loader;
    }

    public JxdHttpConnector getConnector() {
        return connector;
    }

    public void setConnector(JxdHttpConnector connector) {
        this.connector = connector;
    }

    public String getName() {
        return null;
    }

    public void setName(String name) {
    }

    //invoke方法用于从map中找到相关的servlet,然后调用
    public void invoke(JxdRequest request, JxdResponse response) throws IOException, ServletException {
        ServletWrapper servletWrapper = null;
        String uri = request.getUri();
        //首先根据uri最后一个/号来定位,后面的字符串认为是servlet名字
        String servletName = uri.substring(uri.lastIndexOf("/") + 1);
        String servletClassName = servletName;
        servletWrapper = servletInstanceMap.get(servletName);
        //如果容器内没有这个servlet,先要load类,创建新实例
        if (servletWrapper == null) {
            servletWrapper = new ServletWrapper(servletClassName, this);
            this.servletClsMap.put(servletName, servletClassName);
            this.servletInstanceMap.put(servletName, servletWrapper);
        }
        try {
            HttpRequestFacade requestFacade = new HttpRequestFacade(request);
            HttpResponseFacade responseFacade = new HttpResponseFacade(response);
            System.out.println("Call service()");
            //让servletWrapper去执行servlet
            servletWrapper.invoke(requestFacade,responseFacade);
        } catch (Exception e) {
            System.out.println(e.toString());
        } catch (Throwable e) {
            System.out.println(e.toString());
        }
    }
}

JxdHttpServer 我们的启动类 也发发生了变化
public class JxdHttpServer {
    public static final String WEB_ROOT = System.getProperty("user.dir");

    public static final String FILE_ROOT = "D:\\";

    public static void main(String[] args) {
        JxdHttpConnector connector = new JxdHttpConnector();
        ServletContainer container = new ServletContainer();
        connector.setContainer(container);
        container.setConnector(connector);
        connector.start();
    }
}