1. 项目背景与核心价值
十年前我刚入行Java开发时,第一次接触Tomcat就被它的精巧设计所震撼。作为轻量级Web容器,Tomcat用不到10MB的体积实现了Servlet规范的核心功能,这种"小身材大能量"的特性让我萌生了自己实现一个简化版的想法。经过三个月的业余开发,myTomcat项目终于成型——这是一个完全手写的、支持基础Servlet规范的Web容器实现。
与直接使用现成Tomcat不同,手写实现能让你真正理解:
- HTTP协议如何转换为Java对象
- 线程池如何高效处理并发请求
- Servlet生命周期管理的底层逻辑
- 类加载机制在Web容器中的特殊实现
2. 核心架构设计
2.1 整体架构图
[浏览器] -> [Connector] -> [Engine] -> [Host] -> [Context] -> [Wrapper] -> [Servlet]2.2 关键组件实现
2.2.1 连接器(Connector)
public class HttpConnector implements Runnable { private ServerSocket serverSocket; private ExecutorService threadPool; public void start() { threadPool = Executors.newFixedThreadPool(100); new Thread(this).start(); } @Override public void run() { while(!shutdown) { Socket socket = serverSocket.accept(); threadPool.execute(new HttpProcessor(socket)); } } }2.2.2 请求处理器(HttpProcessor)
class HttpProcessor { public void process(Socket socket) { // 1. 解析HTTP请求行 String requestLine = readLine(socket.getInputStream()); String[] parts = requestLine.split(" "); String method = parts[0]; String uri = parts[1]; // 2. 构建Request/Response对象 Request request = new Request(method, uri); Response response = new Response(socket.getOutputStream()); // 3. 调用Servlet Servlet servlet = mapper.mapToServlet(uri); servlet.service(request, response); } }3. 关键技术实现细节
3.1 类加载机制
public class WebappClassLoader extends URLClassLoader { public Class<?> findClass(String name) { // 1. 检查本地缓存 // 2. 从WEB-INF/classes加载 // 3. 从WEB-INF/lib加载 // 4. 委托父类加载器 } }3.2 会话管理
public class StandardSession implements Session { private String id; private Map<String,Object> attributes; private long lastAccessedTime; public Object getAttribute(String name) { updateAccessTime(); return attributes.get(name); } }4. 性能优化实践
4.1 线程池调优
// 根据CPU核心数动态设置线程数 int corePoolSize = Runtime.getRuntime().availableProcessors() * 2; ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, corePoolSize * 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000) );4.2 连接复用
// 在HttpProcessor中添加Keep-Alive支持 if ("keep-alive".equalsIgnoreCase(request.getHeader("Connection"))) { response.setHeader("Connection", "keep-alive"); socket.setKeepAlive(true); }5. 测试验证方案
5.1 单元测试示例
@Test public void testRequestParsing() { String httpRequest = "GET /test HTTP/1.1\r\nHost: localhost\r\n\r\n"; InputStream input = new ByteArrayInputStream(httpRequest.getBytes()); Request request = new Request(input); assertEquals("GET", request.getMethod()); assertEquals("/test", request.getRequestURI()); }5.2 性能测试结果
并发用户数 | 平均响应时间 | 吞吐量 100 | 23ms | 4200 req/s 500 | 67ms | 7400 req/s 1000 | 142ms | 6800 req/s6. 常见问题排查
6.1 内存泄漏排查
# 使用jmap生成堆转储 jmap -dump:live,format=b,file=heap.bin <pid> # 使用MAT分析内存泄漏6.2 线程阻塞分析
// 在ThreadPoolExecutor中添加监控 executor.setRejectedExecutionHandler((r, executor) -> { log.warn("Task rejected, queue size: " + executor.getQueue().size()); });7. 扩展功能实现
7.1 JSP支持
public class JspServlet extends HttpServlet { public void service(HttpServletRequest req, HttpServletResponse res) { // 1. 将JSP编译为Servlet // 2. 加载生成的Servlet类 // 3. 调用_service方法 } }7.2 热部署实现
public class HotDeployWatcher extends Thread { public void run() { while(true) { checkWebappModification(); Thread.sleep(5000); } } }8. 项目总结与展望
经过完整实现myTomcat,我获得了以下深刻认知:
- 网络通信层是性能关键,NIO模型比BIO提升3倍吞吐量
- 合理的类加载设计能有效隔离Web应用
- 线程池配置需要根据实际场景动态调整
后续计划加入:
- 异步Servlet支持
- WebSocket协议实现
- 更精细化的内存管理
重要提示:生产环境建议使用成熟Tomcat,本项目主要用于学习原理。实际部署时需要注意线程安全、资源释放等问题。