手写一个民用Tomcat (03)

我们完成了这个 小型Tomcat 基本功能,但是他处理请求还是 一个一个的执行,并不能做到并行处理。 我们仿照Tomcat的思路来进行 一比一精准优化。

首先看一下我们的JxdHttpConnector 有什么改进,他可以理解成一个快递站领导,统一指挥JxdHttpProcessor 进行 工作。

JxdHttpConnector引入一个队列池,每接到一个请求,就冲队列池中取出来,执行这个请求,执行完之后再放回队列池中,继续等待下一个处理。

复制代码
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayDeque;
import java.util.Deque;

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

    @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);
    }
}
复制代码
processorDeque这就是队列池,刚开始的时候 会创建三个,随着业务访问了增加 最多增加10个 ,

你们发现了吗 JxdHttpProcessor 为啥要.start()多线程进行处理,因为 你 虽然引入队列池但是还是不能 解决并行处理问题,所以要让每一个真正的执行者(员工)。进行并行处理。

复制代码
JxdHttpProcessor 代码:
复制代码
public class JxdHttpProcessor implements Runnable {
    boolean available = false;
    Socket socket;

    JxdHttpConnector connector;

    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;
        try {
            input = socket.getInputStream();
            output = socket.getOutputStream();
            // create Request object and parse
            JxdRequest request = new JxdRequest(input);
            request.parse();
            // create Response object
            JxdResponse response = new JxdResponse(output);
            if (request.getUri().startsWith("/servlet/")) {
                //加载动态资源
                JxdServletProcessor jxdServletProcessor = new JxdServletProcessor();
                jxdServletProcessor.process(request, response);
            } else {
                //加载静态资源
                StaticResourceProcessor staticResourceProcessor = new StaticResourceProcessor();
                staticResourceProcessor.process(request, response);
            }
            //因为是多线程所以只能交给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();
    }
}
复制代码
JxdHttpProcessor 真正的执行者 实现了 Runnable 接口 同时引入了 await()和assign()方法和available 标识。

先说await()方法 ,available 默认为false 也就是线程启动的时候进入 等待状态,一直等,等谁呢 他在等待一个指令,就类似于 员工快递站工作,等来了快递 才工作 没有快递 就休息。

在说一下 assign()方法,他的入参是Socket ,表示 快递来了 ,因为available 默认为false所以他会继续走 ,把标志available 默认为true 然后 通知await()中等待的 员工要开始工作了 。

这就是 形成了一套 运行 机制,我通知你干活,你干完活之后 告诉我 ,我们在02那篇文章的时候是这样的,JxdHttpConnector 领导一直紧跟着JxdHttpProcessor 员工,直到员工干完活,一个领导盯着一个员工很累的,现在是 这个领导 统一指挥一个 团队,员工自己干完活之后反馈给领导。

复制代码
socket.close(); 还记得这个吗。之前是 有JxdHttpConnector 来关闭的,现在由 员工JxdHttpProcessor来关闭了,为啥 因为领导 一致盯着 员工直到 结束,但是现在 盯不住了 ,只能员工自己 才知道什么时候结束 。

备注:其他类不展示 基本每改动。

看一下运行结果吧。

复制代码
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();
        connector.start();
    }
}
相关推荐
小鑫记得努力7 分钟前
Java类和对象(下篇)
java
binishuaio11 分钟前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE13 分钟前
【Java SE】StringBuffer
java·开发语言
老友@13 分钟前
aspose如何获取PPT放映页“切换”的“持续时间”值
java·powerpoint·aspose
wrx繁星点点28 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
Upaaui31 分钟前
Aop+自定义注解实现数据字典映射
java
zzzgd81631 分钟前
easyexcel实现自定义的策略类, 最后追加错误提示列, 自适应列宽,自动合并重复单元格, 美化表头
java·excel·表格·easyexcel·导入导出
友善的鸡蛋32 分钟前
解决:使用EasyExcel导入Excel模板时出现数据导入不进去的问题
java·easyexcel·excel导入
星沁城32 分钟前
240. 搜索二维矩阵 II
java·线性代数·算法·leetcode·矩阵
NoneCoder1 小时前
Java企业级开发系列(1)
java·开发语言·spring·团队开发·开发