手写tomcat+手写线程池进行线程管理

目录

手写tomcat主要分为3个步骤

Maven依赖

MyTomcat

bean

MyRequest

MyResponse

MyUrlMapping

servlet

AbcServlet

 MyServlet

config

MyUrlMappingConfig

手写线程池

ExecutorPool

 


手写tomcat主要分为3个步骤

  1. 提供socket服务
  2. 把请求和响应封装为request/response
  3. 根据请求进行转发

Maven依赖

request解析参数时使用到了json工具类帮助解析json字符串

<!--JSON依赖-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.49</version>
    </dependency>
</dependencies>

 

MyTomcat

主要作用是完成上面所说的手写tomcat的3个步骤,然后使用线程池进行多线程的管理。

package com.siyang.tomcat;

import com.siyang.tomcat.bean.MyRequest;
import com.siyang.tomcat.bean.MyResponse;
import com.siyang.tomcat.bean.MyUrlMapping;
import com.siyang.tomcat.config.MyUrlMappingConfig;
import com.siyang.tomcat.servlet.MyServlet;
import com.siyang.tomcat.thread.ExecutorPool;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * @author siyang
 * @create 2020-09-11 15:16
 */
public class MyTomcat {
    private int port = 8088;
    private Map<String,String> urlClazzMap = new HashMap();

    public MyTomcat(int port) {
        this.port = port;

    }

    public void start() throws IOException {
        // 初始化
        init();
        // socket 连接
        ServerSocket socket = new ServerSocket(port);
        ExecutorPool executorPool = new ExecutorPool(5,10,10);
        while(true){
            Socket accept = socket.accept();
            // 使用多线程池避免堵塞
            executorPool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 获取输入输出流
                        InputStream inputStream  = accept.getInputStream();
                        OutputStream outputStream = accept.getOutputStream();
                        // 将请求和响应封装为request/response
                        MyRequest myRequest = new MyRequest(inputStream);
                        MyResponse myResponse = new MyResponse(outputStream);
                        System.out.println(myRequest.toString());
                        // 进行分发
                        if(myRequest.getUrl() != null){
                            // 分发
                            dispatch(myRequest,myResponse);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        // 关闭
                        try {
                            accept.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        }
    }

    private void init(){
        /**
         * 处理请求路径与文件的映射
         */
        for (MyUrlMapping myUrlMapping : MyUrlMappingConfig.mappingList) {
            urlClazzMap.put(myUrlMapping.getUrl(),myUrlMapping.getClazzPath());
        }

    }

    /**
     * 请求分发
     * @param request
     * @param response
     */
    private void dispatch(MyRequest request,MyResponse response) {

        try {
            if(urlClazzMap.containsKey(request.getUrl())){
                // 根据映射执行servlet
                Class<?> servletClazz = Class.forName(urlClazzMap.get(request.getUrl()));
                MyServlet o = (MyServlet)servletClazz.newInstance();
                o.service(request,response);
            }else{
                // 判断有没有父路径匹配的情况 /xx/*的情况
                String subUrl = request.getUrl();
                while(subUrl.indexOf("/")!= -1){ // 保证url中有 /
                    subUrl = subUrl.substring(0,subUrl.lastIndexOf("/"));
                    String starUrl = subUrl + "/*";
                    if(urlClazzMap.containsKey(starUrl)){
                        // 根据映射执行servlet
                        Class<?> servletClazz = Class.forName(urlClazzMap.get(starUrl));
                        MyServlet o = (MyServlet)servletClazz.newInstance();
                        o.service(request,response);
                        break;
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            System.out.println("映射文件类路径错误");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NullPointerException e) {
            System.out.println("没有路径映射");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        MyTomcat myTomcat = new MyTomcat(8888);
        myTomcat.start();
    }
}

 

bean

MyRequest

用于解析socket传来的inputStream,从流中解析请求头的信息,从中得到请求路径、请求方式、请求参数等参数。

package com.siyang.tomcat.bean;

import com.alibaba.fastjson.JSON;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * @author siyang
 * @create 2020-09-11 15:16
 */
public class MyRequest {
    private String url;
    private String method;
    private Map<String,Object> param = new HashMap<>();

    public MyRequest(InputStream inputStream) throws IOException {
        String inputInfo  = null;
        byte[] bytes = new byte[1024];
        int len = 0;
        if((len = inputStream.read(bytes)) != -1){
            inputInfo = new String(bytes);
        }
        /**
         *  GET /abc HTTP/1.1
         * Host: localhost:8888
         * Connection: keep-alive
         * Upgrade-Insecure-Requests: 1
         * User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3775.400 QQBrowser/10.6.4209.400
         * Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,;q = 0.8
         * Accept - Encoding:gzip, deflate, br
         * Accept - Language:zh - CN, zh; q = 0.9
         */
        // 只需要解析第一句话
        if(inputInfo != null ){
            String header = inputInfo.split("\n")[0];
            String[] temp = header.split(" ");

            // 获取请求方式
            this.method = temp[0];
            // 获取路径与参数
            if(this.method.equals("GET")){
                // 判断GET方法是否带有参数
                if(temp[1].contains("?")){
                    // 参数
                    String p = temp[1].substring(temp[1].indexOf("?")+1);
                    String[] ps = p.split("&");
                    for (String s : ps) {
                        String[] kv = s.split("=");
                        this.param.put(kv[0],kv[1]);
                    }
                    // 请求地址
                    String u = temp[1].substring(0,temp[1].indexOf("?"));
                    this. url = u.endsWith("/") ? u.substring(0,u.lastIndexOf("/")): u ;
                }else{
                    // 获取 url
                    this.url = temp[1].endsWith("/") ? temp[1].substring(0,temp[1].lastIndexOf("/")): temp[1] ;
                }
            }else if (this.method.equals("POST")){
                // 获取 url
                this.url = temp[1].endsWith("/") ? temp[1].substring(0,temp[1].lastIndexOf("/")): temp[1] ;
                // 获取参数
                String json = inputInfo.substring(inputInfo.indexOf("\r\n\r\n") + 4).replace("\u0000","");
                try {
                    if(!json.equals("")){ // 参数不为空
                        this.param = (Map<String,Object>)JSON.parse(json);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("post请求参数不为json格式");
                }
            }

        }

    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public Map<String, Object> getParam() {
        return param;
    }

    public void setParam(Map<String, Object> param) {
        this.param = param;
    }

    @Override
    public String toString() {
        return "MyRequest{" +
                "url='" + url + '\'' +
                ", method='" + method + '\'' +
                ", param=" + param +
                '}';
    }
}

MyResponse

用来响应结果

package com.siyang.tomcat.bean;

import java.io.IOException;
import java.io.OutputStream;

/**
 * @author siyang
 * @create 2020-09-11 15:41
 */
public class MyResponse {
    private OutputStream outputStream;
    private String contentType = "text/html";
    private String charactor = "utf-8";

    public MyResponse(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public void write(String content) throws IOException {

        /**
         * HTTP/1.1 200 OK
         * Content-Type: text/html;charset=utf-8
         * <html><body></body></html>
         *
         */
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("HTTP/1.1 200 OK\n")
                .append("Content-Type: "+contentType+";charset="+charactor+"\n")
                .append("\r\n")
                .append("<html><body>")
                .append(content)
                .append("</body></html>");

        outputStream.write(stringBuffer.toString().getBytes());
        outputStream.close();
    }

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public String getCharactor() {
        return charactor;
    }

    public void setCharactor(String charactor) {
        this.charactor = charactor;
    }
}

MyUrlMapping

封装请求与映射servlet的实体类

package com.siyang.tomcat.bean;

/**
 * 封装请求与映射servlet的实体类
 * @author siyang
 * @create 2020-09-11 15:54
 */
public class MyUrlMapping {
    private String url;
    private String name;
    private String clazzPath;

    public MyUrlMapping(String url, String name, String clazzPath) {
        this.url = url;
        this.name = name;
        this.clazzPath = clazzPath;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getClazzPath() {
        return clazzPath;
    }

    public void setClazzPath(String clazzPath) {
        this.clazzPath = clazzPath;
    }
}

servlet

AbcServlet

自定义servlet

package com.siyang.tomcat.servlet;

import com.siyang.tomcat.bean.MyRequest;
import com.siyang.tomcat.bean.MyResponse;

import java.io.IOException;

/**
 * @author siyang
 * @create 2020-09-11 15:57
 */
public class AbcServlet implements MyServlet {
    @Override
    public void doGet(MyRequest myRequest, MyResponse myResponse) {
        this.doPost(myRequest,myResponse);
    }

    @Override
    public void doPost(MyRequest myRequest, MyResponse myResponse) {
        try {
            myResponse.write("牛逼");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 MyServlet

自定义Servlet接口

package com.siyang.tomcat.servlet;

import com.siyang.tomcat.bean.MyRequest;
import com.siyang.tomcat.bean.MyResponse;

/**
 * @author siyang
 * @create 2020-09-11 15:47
 */
public interface MyServlet {

    public void doGet(MyRequest myRequest, MyResponse myResponse);
    public void doPost(MyRequest myRequest, MyResponse myResponse);

    default void service(MyRequest myRequest, MyResponse myResponse){
        if(myRequest.getMethod().equals("GET")){
            doGet(myRequest,myResponse);
        }else if(myRequest.getMethod().equals("POST")){
            doPost(myRequest,myResponse);
        }
    }

}

config

MyUrlMappingConfig

配置请求路径与响应实体类的映射类

package com.siyang.tomcat.config;

import com.siyang.tomcat.bean.MyUrlMapping;

import java.util.ArrayList;
import java.util.List;

/**
 * 配置请求路径与响应实体类的映射类
 * @author siyang
 * @create 2020-09-11 15:55
 */
public class MyUrlMappingConfig {
    public static List<MyUrlMapping> mappingList = new ArrayList<MyUrlMapping>();

    static {
        mappingList.add(new MyUrlMapping("/*","请求abc","com.siyang.tomcat.servlet.AbcServlet"));
//        mappingList.add(new MyUrlMapping("/abc","请求abc",""));
    }
}

手写线程池

ExecutorPool

自定义线程池,进行线程任务的管理。初始化时指定最大线程数、核心线程数、任务队列数、非核心线程存活时间等参数。

当任务队列满了时,会创建非核心线程进行执行任务。非核心线程如果长时间没有执行任务(存活时间指定),会自动销毁。

这里的非核心线程和核心线程没有本质上的区别,当长时间没有执行任务,存活时间过了后,会按照空闲时间逐渐销毁,只留下核心线程数指定数目 的线程作为核心线程。

package com.siyang.tomcat.thread;

import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程池
 * @author siyang
 * @create 2020-09-11 17:24
 */
public class ExecutorPool {
    private  int MAX_THREAD_NUMBER = 10; // 默认最大线程数 ,最大线程数 - 核心线程数 = 可以回收的线程数
    private  int MAIN_THREAD_NUMBER = 3; // 核心线程数
    private  int ALIVE_TIME = 6*1000; // 无任务状态下非核心线程线程存活时间 (ms),默认1分钟
    private  List<Thread> threads  = new CopyOnWriteArrayList<>(); // 线程列表
    private  int MAX_TASK_QUEUE_NUMBER = 10; // 默认最大任务队列数
    private  ArrayBlockingQueue<Runnable> taskQueue ; // 任务队列
    private volatile  int WORK_THREAD_NUMBER = 0; // 工作线程数
    private final ReentrantLock lock  = new ReentrantLock();

    public ExecutorPool(){
        taskQueue = new ArrayBlockingQueue(MAX_TASK_QUEUE_NUMBER);
    }

    /**
     * 
     * @param mainThreadNumber 核心线程数
     * @param maxThreadNumber 最大线程数
     * @param taskQueueNumber 任务队列数
     */
    public ExecutorPool(int mainThreadNumber,int maxThreadNumber,int taskQueueNumber){
        if(mainThreadNumber>maxThreadNumber){
            throw new RuntimeException("核心线程数不能大于最大线程数");
        }
        this.MAIN_THREAD_NUMBER = mainThreadNumber;
        this.MAX_THREAD_NUMBER = maxThreadNumber;
        this.MAX_TASK_QUEUE_NUMBER = taskQueueNumber;
        taskQueue = new ArrayBlockingQueue(MAX_TASK_QUEUE_NUMBER);
    }
    /**
     * 
     * @param mainThreadNumber 核心线程数
     * @param maxThreadNumber 最大线程数
     * @param taskQueueNumber 任务队列数
     * @param aliveTime 非核心线程存活时间
     */
    public ExecutorPool(int mainThreadNumber,int maxThreadNumber,int taskQueueNumber,int aliveTime){
        this.MAIN_THREAD_NUMBER = mainThreadNumber;
        this.MAX_THREAD_NUMBER = maxThreadNumber;
        this.MAX_TASK_QUEUE_NUMBER = taskQueueNumber;
        this.ALIVE_TIME = aliveTime;
        taskQueue = new ArrayBlockingQueue(MAX_TASK_QUEUE_NUMBER);
    }

    /**
     * 提交任务
     * @param runnable
     */
    public void submit(Runnable runnable){
        try {
            // 加锁
            lock.lock();
            // 存入任务队列
            if(!taskQueue.offer(runnable)){
                if(WORK_THREAD_NUMBER < MAX_THREAD_NUMBER){
                    MyThread myThread = new MyThread(runnable);
                    // 添加到线程列表中
                    threads.add(myThread);
                    // 执行任务
                    myThread.start();
                    WORK_THREAD_NUMBER ++;
//                    System.out.println("任务队列已满,创建非核心线程->名字:"+myThread.getName());
                }else{
                    System.out.println("任务队列已满,无法创建非核心线程,请扩充任务队列大小");
                }
            }else{
//                System.out.println("存入任务队列");
            }

            if(WORK_THREAD_NUMBER < MAIN_THREAD_NUMBER){
                MyThread myThread = new MyThread(taskQueue.poll());
                // 添加到线程列表中
                threads.add(myThread);
                // 执行任务
                myThread.start();
                WORK_THREAD_NUMBER ++;
//                System.out.println("创建核心线程->名字:"+myThread.getName());
            }



        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }


    }

    /**
     * 自定义线程类
     */
    class MyThread extends Thread{
        private Runnable task;
        private long aliveTime; // 存活时间
        public MyThread(Runnable runnable) {
            this.task = runnable;
            this.aliveTime = System.currentTimeMillis();
        }

        @Override
        public void run() {
            // 一直循环执行 从队列中获取任务
            while(true){
                if(this.task != null){
                    // 执行任务 不会新建线程
//                    System.out.println("线程名"+ super.getName());
                    this.task.run();
                    // 运行完后重置runnable;
                    this.task = null;
                    // 重置生存时间
                    this.aliveTime = System.currentTimeMillis();
                }else{
                    // 超时删除
                    long date = System.currentTimeMillis();
                    if(date-aliveTime >= ALIVE_TIME){
                        // 核心线程无法删除
                        synchronized (MyThread.class){
                            if(WORK_THREAD_NUMBER > MAIN_THREAD_NUMBER){
                                threads.remove(this);
//                                System.out.println("删除队列"+",删除线程名字"+super.getName());
                                WORK_THREAD_NUMBER --;
                                break;
                            }
                        }

                    }
                    Runnable poll = taskQueue.poll();
                    this.task = poll;
                }

            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务");
            }
        };
        ExecutorPool executorPool = new ExecutorPool(10,10,10);
        for(int i =0 ;i<10;i++){
            Thread.sleep(1000);
            executorPool.submit(r);
        }
        Thread.sleep(1000);
        for(int i =0 ;i<100;i++){
            executorPool.submit(r);
        }
    }
}

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值