MiniCat
基本介绍
我们要手写实现一个 Mini 版的 Tomcat,我们要实现的是,作为一个服务器软件,可以通过我们的浏览器客户端发送HTTP请求,Minicat处理之后,处理结果可以返回给浏览器的客户端。
● 提供服务,接受请求(Socket通信) ● 请求信息封装成 Request 对象(Response对象) ● 客户端请求资源,资源分为静态资源(HTML)和动态资源(Servlet) ● 资源返回客户端浏览器
Tomcat 的核心作用
Tomcat 是一个 Servlet 容器,核心职责包括:
- 接收浏览器的 HTTP 请求
- 分发请求给对应的 Servlet
- 管理 Servlet 的生命周期
- 返回响应给浏览器
最小功能模块
为了实现最基本的功能,MiniCat 至少需要实现以下模块:
Socket 服务端监听器
- 使用 ServerSocket 接收浏览器请求
- 多线程处理并发请求
Request 解析器
- 解析 HTTP 请求(请求行、请求头、请求体)
- 构建 MiniRequest 对象,封装请求信息
Response 构建器
- 构建 HTTP 响应
- 包含响应状态、响应头、响应体
Servlet 接口与管理
- 自定义 MiniServlet 接口(模拟 javax.servlet.Servlet)
- 加载与初始化 Servlet
- 调用 service() 方法处理请求
URL 映射 Dispatcher
- 维护 URL 与 Servlet 的映射关系
- 根据请求路径匹配对应的 Servlet 实例
WebApp 配置加载器
- 读取 web.xml(简化版)或自定义配置文件
- 动态加载类(使用 Class.forName)
准备依赖
<dependencies>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>
Bootstrap 代码
public class Bootstrap {
private int port = 8080;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void start() throws Exception {
loadServlet();
int corePoolSize = 10;
int maximumPoolSize = 20;
long keepAliveTime = 100L;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> deque = new ArrayBlockingQueue<>(50);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
deque,
threadFactory,
handler
);
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("ServerSocket: " + serverSocket);
while (true) {
Socket socket = serverSocket.accept();
RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
threadPoolExecutor.execute(requestProcessor);
}
}
private Map<String, HttpServlet> servletMap = new HashMap<>();
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> selectNodes = rootElement.selectNodes("//servlet");
for (int i = 0; i < selectNodes.size(); i ++) {
Element element = selectNodes.get(i);
Element servletElement = (Element) element.selectSingleNode("servlet-name");
String servletName = servletElement.getStringValue();
Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
String servletClass = servletClassElement.getStringValue();
Element servletMapping = (Element) rootElement
.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解析
主入口
创建 Bootstrap 实例,调用 start() 启动服务器,捕获异常输出。
启动逻辑
调用 loadServlet() 方法,加载自定义的 Servlet 配置(从 web.xml 解析映射关系)。
创建线程池
- 核心线程数:10,最大线程数:20
- 空闲线程存活时间:100秒
- 任务队列:50个
- 拒绝策略:默认抛出异常(AbortPolicy)
创建 ServerSocket 并监听请求
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("ServerSocket: " + serverSocket);
主循环
- accept() 阻塞直到有浏览器请求
- 创建一个新的 RequestProcessor 实例处理请求
- 使用线程池异步执行
加载配置
从 classpath 中读取 web.xml 文件,并用 SAXReader 解析 XML 文档。
暂时小结
Bootstrap 作为 MiniCat 的核心启动类,完成了:
- 加载配置文件 web.xml 中配置的 Servlet 类
- 初始化 Servlet 并建立 URL 映射
- 启动 Socket 服务监听 HTTP 请求
- 为每一个请求分发一个线程去执行
- 线程中实际使用了 RequestProcessor 进行业务处理