手写Tomcat,我们起名叫Minicat,完成以下功能,作为一个服务器软件提供服务,可以通过浏览器发送http请求,Minicat可以接收到请求进行处理,处理之后结果返回给客户端 |
---|
- 提供服务,接收请求(Socket通信)
- 请求信息封装成Request对象(Respnse对象)
- 客户端请求资源,资源分为静态资源(html)和动态资源(Servlet)
- 资源返回给客户端浏览器
由简单到复杂,我们将其分为4个版本1.0,2.0,3.0,multithreading |
---|
- 1.0版本:浏览器请求http://localhost:8080,返回一个固定的字符串到页面"Hello Minicat!!!"
- 2.0版本:封装Request和Response对象,返回html静态资源文件
- 3.0版本:处理返回动态资源Servlet
- multithreading: 多线程版
源码(Gitee码云):https://gitee.com/yin_zhipeng/implement-_tomcat_of_myself.git |
---|
准备工作 |
---|
1. 建立maven项目引入依赖
<build>
<plugins>
<!--指定编译级别-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId><!--加个插件-->
<version>3.1</version>
<configuration>
<source>8</source><!--JDK-->
<target>8</target><!--编译-->
<encoding>utf-8</encoding><!--编码-->
</configuration>
</plugin>
</plugins>
</build>
文章目录
一、Minicat1.0版本
实现浏览器请求http://localhost:8080,返回一个固定的字符串到页面"Hello Minicat!!!" |
---|
1. 提供服务,接收请求(Socket通信)
1. 首先玩Socket需要定义一个端口号,Tomcat是配在web.xml文件中,为了代码简洁我们直接定义主类里,后面自己实现Servlet的时候,会用web.xml解析Servlet |
---|
2. 有了端口号,想要程序启动,需要做一些初始化的程序,比如启动socket,我们将其封装到方法中 |
---|
3. main函数测试 |
---|
- 网页提示响应无效ERR_INVALID_HTTP_RESPONSE错误的HTTP响应
- http协议规定了,我们请求和响应的请求/响应头、请求/响应体
- 格式不正确的话,浏览器没法正常解析
2. 响应信息封装成Respnse对象(http)
1. 我们将响应头信息,封装成一个工具类(只简单提供响应码为200和404的情况) |
---|
- 我们先看下百度的响应头
- 封装自己的,我们只封装必须的一些参数
2. 修改响应内容,带上响应头 |
---|
3. main测试 |
---|
二、Minicat2.0版本
封装Request和Response对象,返回html静态资源文件 |
---|
- 为了加以区分,我将各版本分开存放
1. 获取请求信息
- 修改start()方法,根据InputStream获取信息
- 测试
2. 封装Request和Response
1. 封装Request获取请求方式和url,封装Response,根据url获取静态html文件,输出 |
---|
- Request
- 封装Response对象之前,我们要响应静态资源,可以单独将处理静态资源的逻辑封装成工具类
- 封装Response对象
2. 编写index.html,用户请求后,将html输出 |
---|
- 修改start()方法,调用我们封装好的工具类
- 编写html,运行Minicat,保证html页面被编译
三、Minicat3.0版本
处理返回动态资源Servlet |
---|
- 为了加以区分,我将各版本分开存放
1. 实现自己的Servlet
- Servlet接口,制定servlet基本规范
- HttpServlet抽象类,对servlet做一定抽象封装
- 自己写Servlet,处理业务
2. 加载解析Servlet配置(web.xml)
- web.xml
- 添加依赖
- 重写Minicat启动类,添加一个map存储url和Servlet类的映射,添加解析处理xml文件配置的方法
3. 接收处理请求,测试
- 改造start()方法,判断请求url,如果是Servlet映射,就是动态请求,否则就是请求静态资源
- 启动Minicat测试
四、解析问题,多线程改造
对于多线程不了解的同学可以粗略浏览下我们另外两篇文章 |
---|
- 为了加以区分,我将各版本分开存放
1. Minicat3.0的问题
我们希望,每个用户访问Minicat,是独立的,互不影响的,不能出现用户2等待用户1的情况 |
---|
- 下面我们模拟两个用户同时访问服务器
- 用户A先请求/myServlet,但是处理时间有些长
- 用户B又来请求index.html,但是会因为用户A的请求没处理完,而等待
- 我们为Servlet添加一段代码Thread.sleep(100000);//休眠100000ms,模拟一个Servlet处理时间较长的情景
- 我们请求/myServlet,因为线程休眠时间很长,所以我们在等待它运行完成
- 我们再请求/index.html,此时访问静态资源,也需要等待前面的Servlet处理完成
因为现在的Minicat,逻辑都是跑在主线程(main)中的,就是一个单线程,只能处理完一个,下一个才能继续处理 |
---|
- 那么我们就让逻辑跑在多个线程中
- 也就是将start() 多线程化
2. 线程改造
1. 写一个线程类 |
---|
2. 改造Minicat 的 start()方法 |
---|
3. 测试 |
---|
3. 使用线程池优化
每次来一个请求都创建一个线程出来,有点太过土豪,所以我们通过线程池管理线程,请求来了,从线程池拿线程,用完放回去,这样提高线程复用性 |
---|
- 就像我们平常吃饭,每次用一次性筷子,太浪费木头了
- 一般家里的筷子都是用完洗,洗完放回收纳盒,下次接着用
1. 写一个方法,提供线程池 |
---|
- 我用的线程池是ThreadPoolExecutor,我从以前写到文章截取这个线程池的解析
- 提供一个方法,创建线程池ThreadPoolExecutor
2. 使用线程池,改造start()方法 |
---|
3. 测试 |
---|