tomcat学习笔记手写tomcat

Posted 拐柒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tomcat学习笔记手写tomcat相关的知识,希望对你有一定的参考价值。

手写实现迷你版tomcat

名称:Minicat
Minicat:作为服务器软件提供服务,我们通过浏览器客户端发送HTTP请求,Minicat可以接受请求进行处理,处理之后的结果可以返回浏览器客户端。
1、提供服务,接受请求(socket通信)
2、请求信息封装成request/response对象
3、客户端请求资源,资源分为静态资源(html)和动态资源(servlet)
4、资源返回给客户端浏览器

Bootstrap

迷你tomcta启动类。

public class Bootstrap {
    /**
     * 定义socket监听端口号
     */
    private int port=8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
    /**
     *Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {
        //加载解析配置文件web.xml
        loadServlet();
        //定义线程池
        //初始大小
        int corePoolSize=10;
        //最大大小
        int maximumPoolSize=50;
        //存活时长
        long keepAliveTime=100L;
        //时长单位
        TimeUnit unit= TimeUnit.SECONDS;
        //请求队列
        BlockingQueue<Runnable> workQueue=new ArrayBlockingQueue<>(50);
        //线程工厂
        ThreadFactory threadFactory=Executors.defaultThreadFactory();
        //拒绝策略
        RejectedExecutionHandler handler=new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);



        /**
         * 1.0
         * 完成minicat1.0版本(浏览器请求localhost:8080,返回固定的字符串到页面"hellow Minicat!")
         */
        ServerSocket serverSocket=new ServerSocket(port);
        System.out.println("Minicat start on port:"+port);
//        while (true) {
//            Socket socket = serverSocket.accept();
//            //有了socket以后,接收到请求,获取输出流
//            OutputStream outputStream = socket.getOutputStream();
//            String data="Hello Minicat!";
//            String responseText=HttpProtocolUtil.getHttpHeader200(data.getBytes().length)+data;
//            outputStream.write(responseText.getBytes());
//            socket.close();
//        }
        /**
         * 2.0
         * 完成minicat2.0版本
         * 封装Request和Response对象,返回html静态资源文件
         */
//        while (true) {
//            Socket socket = serverSocket.accept();
//            InputStream inputStream = socket.getInputStream();
//            //封装request/response对象
//            Request request=new Request(inputStream);
//            Response response=new Response(socket.getOutputStream());
//            response.outPutHtml(request.getUrl());
//            socket.close();
//        }

        /**
         * 3.0
         * 请求动态资源servlet
         */
//        while (true) {
//            Socket socket = serverSocket.accept();
//            InputStream inputStream = socket.getInputStream();
//            //封装request/response对象
//            Request request=new Request(inputStream);
//            Response response=new Response(socket.getOutputStream());
//            if(servletMap.get(request.getUrl())==null){
//                response.outPutHtml(request.getUrl());
//            }else {
//                HttpServlet httpServlet = servletMap.get(request.getUrl());
//                httpServlet.service(request,response);
//            }
//            socket.close();
//        }


        /**
         * 不适用线程池
         * 多线程改造
         */
//        while (true) {
//            Socket socket = serverSocket.accept();
//            RequestProcessor requestProcessor=new RequestProcessor(socket,servletMap);
//            requestProcessor.start();
//        }

        System.out.println("使用线程池进行多线程改造");
        /**
         * 使用线程池
         * 多线程改造
         */
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor=new RequestProcessor(socket,servletMap);
            threadPoolExecutor.execute(requestProcessor);
        }
    }
    private Map<String,HttpServlet> servletMap=new HashMap<>();
    //加载解析web.xml初始化servlet
    private void loadServlet() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader=new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> list = rootElement.selectNodes("//servlet");
            for (int i = 0; i < list.size(); i++) {
                Element element = list.get(i);
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                //servlet-name>lagouServlet</servlet-name>
                String servletName=servletNameElement.getStringValue();
                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                //<servlet-class>server.LagouServlet</servlet-class>
                String servletClass=servletClassElement.getStringValue();


                //根据servletName的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                //<url-pattern>/lagou</url-pattern>
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                try {
                    servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * Minicat启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap=new Bootstrap();
        try {
            //启动Minicat
            bootstrap.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

HttpProtocolUtil

http协议工具类,


/**
 * http协议工具类,主要是提供响应头信息,这里我们提供200和404的情况
 */
public class HttpProtocolUtil {
    /**
     * 为响应码200提供请求头信息
     * @return
     */
    public static String getHttpHeader200(long contentLength){
        return "HTTP/1.1 200 OK \\n" +
                "Content-Type: text/html;charset=utf-8 \\n" +
                "Content-Length: " +contentLength+" \\n" +
                "\\r\\n";
    }

    /**
     * 为响应码404提供请求头信息(此处也包含了数据内容)
     * @return
     */
    public static String getHttpHeader404(){
        String str="<h1>404 not found</h1>";
        return "HTTP/1.1 404 NOT Found \\n" +
                "Content-Type: text/html;charset=utf-8 \\n" +
                "Content-Length: " +str.getBytes().length+" \\n" +
                "\\r\\n" + str;
    }

}

Servlet

servlet接口,定义servlet规范

public interface Servlet {
    void init() throws Exception;
    void destroy() throws Exception;
    void service(Request request,Response response) throws Exception;
}

HttpServlet

处理请求的httpservlet

public abstract class HttpServlet implements Servlet{
    public abstract void doGet(Response response,Request request);
    public abstract void doPost(Request request,Response response);
    @Override
    public void service(Request request, Response response) throws Exception {
        if("GET".equals(request.getMethod())){
            doGet(response,request);
        }else {
            doPost(request,response);
        }
    }
}

LagouServlet

自定义servlet

public class LagouServlet extends HttpServlet{
    @Override
    public void doGet(Response response, Request request) {
        try {
            Thread.sleep(200000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String string="<h1>LagouServlet get</h1>";
        try {
            response.outPut(HttpProtocolUtil.getHttpHeader200(string.getBytes().length)+string);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(Request request, Response response) {
        String string="<h1>LagouServlet post</h1>";
        try {
            response.outPut(HttpProtocolUtil.getHttpHeader200(string.getBytes().length)+string);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void init() throws Exception {

    }

    @Override
    public void destroy() throws Exception {

    }
}

Request

请求信息封装类

/**
 * 把请求信息封装为Request对象(根据inputStream封装)
 */
public class Request {
    //请求方式get/post
    private String method;
    //url
    private String url;
    //输入流,其他属性从输入流解析出来
    private InputStream inputStream;

    public Request() {
    }
    //构造器,输入流传入,解析出url和method
    public Request(InputStream inputStream) throws IOException {
        this.inputStream = inputStream;
        int count=0;
        while (count==0){
            count=inputStream.available();
        }
        byte[] bytes=new byte[count];
        inputStream.read(bytes);
        System.out.println("请求信息:"+new String(bytes));
        String string=new String(bytes);
        //获取第一行请求头信息 GET / HTTP/1.1
        String firstLine=string.split("\\\\n")[0];
        String[] s = firstLine.split(" ");
        this.method=s[0];
        this.url=s[1];

        System.out.println("method:"+this.method);
        System.out.println("url:"+this.url);
    }

    public String getMethod() {
        return method;
    }

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

    public String getUrl() {
        return url;
    }

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

    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }
}

RequestProcessor

请求处理器

public class RequestProcessor extends Thread{
    private Socket socket;
    private Map<String,HttpServlet> servletMap;

    public RequestProcessor() {
    }

    public RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap) {
        this.socket = socket;
        this.servletMap = servletMap;
    }

    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();
            //封装request/response对象
            Request request=new Request(inputStream);
            Response response=new Response(socket.getOutputStream());
            if(servletMap.get(request.getUrl())==null){
                response.outPutHtml(request.getUrl());
            }else {
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request,response);
            }
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Response

返回信息封装类

/**
 * 把相应信息封装为Response对象(根据outputStream封装)
 * 该对象需要提供核心对象,输出静态资源(html)
 */
public class Response {
    private OutputStream outputStream;

    public Response() {
    }

    public Response(OutputStream outputStream) {

        this.outputStream = outputStream;
    }
    //使用输出流输出指定字符串
    public void outPut(String content) throws IOException {
        this.outputStream.write(content.getBytes());
    }
    /**
     *
     * @param path url ,根据url获取到静态资源的绝对路径,根据绝对路径,进一步读取该静态文件,最终通过输出流输出
     */
    public void outPutHtml(String path) throws IOException {
        //获取静态资源的绝对路径
        String absoluteResourcePath=StaticResourceUtil.getAbsolutePath(path);
        //输出静态资源
        File file=new File(absoluteResourcePath);
        if(file.exists()&& file.isFile()){
            //读取静态资源文件,输出静态资源
            StaticResourceUtil.outputStaticResource(new FileInputStream(file),outputStream);
        }else {
            //输出404
            outPut(HttpProtocolUtil.getHttpHeader404());
        }
    }
}

StaticResourceUtil

静态资源工具类

public class StaticResourceUtil {
    /**
     * 获取静态资源的绝对路径
     * @param path
     * @return
     */
    public static String getAbsolutePath(String path){
        String absolutePath=StaticResourceUtil.class.getResource("/").getPath();
        return absolutePath.replaceAll("\\\\\\\\","/")+path;
    }

    /**
     * 读取静态资源文件输入流,通过输出流输出
     * @param inputStream
     * @param outputStream
     */
    public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {
        int count=0;
        while (count==0){
            count = inputStream.available();
        }
        int resourceSize=count;
        //http请求头输出,然后输出具体内容
        outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());
        //读取输出内容
        long written=0;//已经读取的内容长度
        int byteSize=1024;//计划每次缓冲长度
        byte[] bytes=new byte[byteSize];
        while (written<resourceSize){
            if(written+byteSize>resourceSize){//剩余未读取大小不足1024,那就按照真实长度处理
                byteSize= (int) (resourceSize-written);
                bytes=new byte[byteSize];
            }
            inputStream.read(bytes);
            outputStream.write(bytes);
            outputStream.flush();
            written+=byteSize;
        }
    }
}

以上是关于tomcat学习笔记手写tomcat的主要内容,如果未能解决你的问题,请参考以下文章

阿里P8架构师手写笔记:Spring源码+JVM+MySQL+Kafka+Tomcat

阿里P8架构师手写笔记:Spring源码+JVM+MySQL+Kafka+Tomcat

springMVC学习笔记手写springmvc

手写Tomcat

独家干货!腾讯T3-3手写8W字Tomcat体系架构,从性能优化到源码底层

手写个Tomcat雏型