基于Netty框架手写一个Tomcat容器
Netty作为底层通信框架,用来实现Web容器自然也不难,我们先介绍一下整体实现思路。我们知道,Tomcat是基于J2EE规范的Web容器,主要入口是web。xml文件。web。xml文件中主要配置Servlet、Filter、Listener等,而Servlet、Filter、Listener在J2EE中只是抽象的实现,具体业务逻辑由开发者来实现。本章内容,就以最常用的Servlet为例来详细展开。1环境准备1。1定义GPServlet抽象类
首先,我们创建GPServlet类。我们都知道GPServlet生命周期中最常用的方法是doGet()方法和doPost()方法,而doGet()方法和doPost()方法是service()方法的分支实现,看下面的简易版Servlet源码实现。
从上面的代码中,我们看到,doGet()方法和doPost()方法中有两个参数GPRequest和GPResponse对象,这两个对象是由Web容器创建的,主要是对底层Socket的输入输出的封装。其中GPRequest是对Input的封装,GPResponse是对Output的封装。1。2创建用户业务代码
下面基于GPServlet来实现两个业务逻辑FirstServlet和SecondServlet。FirstServlet类的实现代码如下。
SecondServlet类的实现代码如下。
1。3完成web。properties配置
为了简化操作,我们用web。properties文件代替web。xml文件,具体内容如下。
上述代码分别给两个Servlet配置了firstServlet。do和secondServlet。do的URL映射。4。2基于传统IO手写Tomcat
下面我们来看GPRequest和GPResponse的基本实现。4。2。1创建GPRequest对象
GPRequest主要就是对HTTP的请求头信息进行解析。我们从浏览器发送一个HTTP请求,如在浏览器地址栏中输入http:localhost:8080,后台服务器获取的请求其实就是一串字符串,具体格式如下。
在GPRequest获得输入内容之后,对这一串满足HTTP的字符信息进行解析。我们来看GPRequest简单直接的代码实现。
在上面的代码中,GPRequest主要提供了getUrl()方法和getMethod()方法。输入流InputStream作为GPRequest的构造参数传入,在构造函数中,用字符串切割的方法提取请求方式和URL。4。2。2创建GPResponse对象
接下来看GPResponse的实现,与GPRequest的实现思路类似,就是按照HTTP规范从Output输出格式化的字符串,来看代码。
上面的代码中,输出流OutputStream作为GPResponse的构造参数传入,主要提供了一个write()方法。通过write()方法按照HTTP规范输出字符串。4。2。3创建GPTomcat启动类
前面4。2。1和4。2。2两节只是对J2EE规范的再现,接下来就是真正Web容器的实现逻辑,分为三个阶段:初始化阶段、服务就绪阶段、接受请求阶段。
第一阶段:初始化阶段,主要是完成对web。xml文件的解析。
上面代码中,首先从WEBINF读取web。properties文件并对其进行解析,然后将URL规则和GPServlet的对应关系保存到servletMapping中。
第二阶段:服务就绪阶段,完成ServerSocket的准备工作。在GPTomcat类中增加start()方法。
第三阶段:接受请求阶段,完成每一次请求的处理。在GPTomcat中增加process()方法的实现。
每次客户端请求过来以后,从servletMapping中获取其对应的Servlet对象,同时实例化GPRequest和GPResponse对象,将GPRequest和GPResponse对象作为参数传入service()方法,最终执行业务逻辑。最后,增加main()方法。
服务启动后,运行效果如下图所示。
4。3基于Netty重构Tomcat实现
了解了传统的IO实现方式之后,我们发现Netty版本的实现就比较简单了,来看具体的代码实现。4。3。1重构GPTomcat逻辑
话不多说,直接看代码。
代码的基本思路和基于传统IO手写的版本一致,不再赘述。4。3。2重构GPRequest逻辑
我们先来看代码。
和基于传统的IO手写的版本一样,提供getUrl()方法和getMethod()方法。在Netty的版本中,我们增加了getParameter()的实现,供大家参考。4。3。3重构GPResponse逻辑
还是继续看代码。
相对于基于传统的IO手写的版本而言,主要变化就是利用Netty对HTTP的默认支持,可以使用现成的API。4。3。4运行效果演示
启动容器,我们在浏览器地址栏中输入http:localhost:8080firstServlet。do,可以得到如下图所示的结果。
在浏览器地址栏中输入http:localhost:8080secondServlet。do,可以得到如下图所示的结果。