手把手带你写一个 MiniTomcat
郭屹
前 Sun Microsystems Java 研发工程师
1792 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 22 讲
开篇词 (1讲)
手把手带你写一个 MiniTomcat
15
15
1.0x
00:00/00:00
登录|注册

04|各司其职的Server:拆分响应模块与处理模块

你好,我是郭屹。今天我们继续手写 MiniTomcat。
在上一节课,我们基于最早的最小可用 HttpServer 服务器进行了改造。主要包括对 HTTP 协议返回内容中的状态行与返回头进行封装,以及引入动态资源和 Servlet 的概念,对 Web 端返回内容进行了扩充,已经有点 Servlet 容器的雏形了。
但我也提到,当前我们自定义的 Servlet 接口是不满足 Java Servlet 规范的。因此这节课我们首先会讨论如何符合 Servlet 规范,在 Java 的规则下实现 MiniTomcat。
其次,在当前的 HttpServer 中,HttpServer 类承担了接收客户端请求、调用 Servlet、响应客户端等多种功能,功能太多了,因此我们要将其进行功能拆分,使各个部分各司其职。
好,就让我们一起来动手实现。

项目结构

这节课我们计划采用 Maven 结构对项目的包依赖进行管理,省去了导入 jar 包的环节。但有一点我们始终坚持,就是引入最少的依赖包,一切功能尽可能用最原生的 JDK 来实现,以便于我们从头做起更深地理解原理。在这节课中,项目结构变化如下:
MiniTomcat
├─ src
│ ├─ main
│ │ ├─ java
│ │ │ ├─ server
│ │ │ │ ├─ HttpConnector.java
│ │ │ │ ├─ HttpProcessor.java
│ │ │ │ ├─ HttpServer.java
│ │ │ │ ├─ Request.java
│ │ │ │ ├─ Response.java
│ │ │ │ ├─ ServletProcessor.java
│ │ │ │ ├─ StatisResourceProcessor.java
│ │ ├─ resources
│ ├─ test
│ │ ├─ java
│ │ │ ├─ test
│ │ │ │ ├─ HelloServlet.java
│ │ ├─ resources
├─ webroot
│ ├─ test
│ │ ├─ HelloServlet.class
│ ├─ hello.txt
├─ pom.xml
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了如何在手写MiniTomcat的过程中,拆分响应模块与处理模块,使各个部分各司其职。作者首先讨论了符合Servlet规范的重要性,并介绍了在Java的规则下实现MiniTomcat的方法。文章还提到了项目结构的变化,引入了Maven结构对项目的包依赖进行管理,并尽可能使用最原生的JDK来实现功能。新增的HttpConnector.java和HttpProcessor.java用来拆分HttpServer两个类,同时引入了javax.servlet.Servlet类。通过这些改进,读者可以更深地理解原理,并且能够快速了解如何在Java的规则下实现MiniTomcat。文章还详细介绍了对Request和Response类的改造,以及对ServletProcessor和HelloServlet的调整,最终实现了正确输出中文。整体而言,本文通过实际代码示例和详细解释,帮助读者更好地理解了如何在Java环境下实现MiniTomcat。 文章通过拆分HttpServer的功能,将其分为Connector和Processor两部分,实现了各司其职的目标。HttpProcessor负责处理接收、响应客户端请求以及调用Servlet,而HttpConnector负责接收连接并支持并发处理,提高整个服务器的吞吐量。通过对HelloServlet的改造和测试,读者可以清晰地了解改进后的功能。最后,文章提出了一个思考题,引导读者思考如何提高服务器的并发度。 总的来说,本文通过实际代码示例和详细解释,帮助读者更好地理解了如何在Java环境下实现MiniTomcat,并且引发了读者对服务器并发度提升的思考。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《手把手带你写一个 MiniTomcat》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(4)

  • 最新
  • 精选
  • ?新!
    老师我有个关于tomcat连接层的问题求教? 场景:外部nginx日志记录调用了A服务并且超时,但是A服务本地日志localhost_access_log,没有记录,了解后怀疑是连接层,会等待队列就超时了,所以没有到容器层,没有被localhost_access_log记录? 问题: 1 我的怀疑是否可能? 2 有办法验证吗?比如tomcat等待队列超时或者accept超时,能记录日志

    作者回复: 有道理。你看一下catalina开头的日志文件。 再启用一下AccessLogValve看看。

    2024-01-02归属地:四川
    2
    1
  • peter
    请教老师几个问题: Q1:streamHandler不需要赋值吗? ServletProcessor.java的process方法中: URLStreamHandler streamHandler = null; urls[0] = new URL(null, repository, streamHandler); 代码声明了变量“streamHandler”, 但是没有赋值,然后直接用来创建URL的对象。 请问:为什么没有给“streamHandler”赋值? Q2:HttpConnector的run方法为什么没有注解? 类定义:HttpConnector implements Runnable, 其run方法上面没有注解,Idea2019提示: Missing '@Override' annotation on 'run()' 。 但程序能运行。请问老师的代码中,run方法为什么没有注解? Q3:第03课代码,一次请求,socket = serverSocket.accept();为什么运行两次? HttpServer.java文件中,while(true)代码块,在serverSocket.accept();这里阻塞。浏览器中输入请求,创建Request,成功地走完了整个流程。走完整个流程后按道理应该还在serverSocket.accept();这里阻塞。但竟然再次创建Request,不过在Request类的parse函数中,在i = input.read(buffer);这个地方不再往下面执行。我在input.read前后都加了打印语句,前面的打印语句执行了,后面的没有执行,神奇啊,为什么啊? 简单地说,就是:浏览器发送一个请求,HttpServer收到了两个request,第一个正常处理,第二个不能正常执行。(我用的是Chrome浏览器,也许和浏览器有关?)

    作者回复: Q1, 你自己看URL api帮助文档 Q2, @Override是一个编译时注解,加上是一个好习惯,没有也不会出运行时错误 Q3,这个问题好。有些浏览器将请求分成简单请求和非简单请求,对于非简单请求,浏览器会先发出一个OPTIONS预检请求,这是为了安全性的原因。MiniTomcat没有考虑这些实际工作中的复杂性,直接忽略了。

    2023-12-16归属地:北京
    3
    1
  • stars
    只是在主线程启动了一个子线程,这样就提高吞吐率了吗?请教老师。

    作者回复: 没有简单的答案。对Tomcat这种场景,前面的线程用于接收客户端网络连接,后面的线程用于业务处理(合理的假定会消耗数据库),这种场景就是会提高吞吐量的。

    2023-12-25归属地:陕西
  • HH🐷🐠
    😄池子+队列, 学艺不精具体细节答不上来, 请老师指点。

    作者回复: 提高并发度有一系列技术。能第一反应出来的就是多线程,将processor设计成多个线程,放到一个池子里面,服务器接受前端多个请求后交给后面线程池子里面的多个processor线程来并发处理。这解决了一部分问题,但是对一个processor来说,它还是串行工作的,当它涉及到数据库访问网络访问文件操作的时候,可以进一步再分线程。不过程序模式需要调整成使用Future或者CompletableFuture,完全的响应式编程结构复杂。JDK21提出的virtual thread很好地解决了这个问题。实际工作中,要根据场景要求进行选择。

    2023-12-15归属地:广东
    2
收起评论
显示
设置
留言
4
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部