做网站servlet从零搭建避坑指南3步搞定
改个需求建站公司拖一周,这种痛苦谁懂?上周客户就为改个首页轮播图的切换速度,催了我三天。很多后端新手觉得 Servlet 是老古董,但实际做网站servlet从零搭建,才是解决这类“响应慢、逻辑乱”问题的根本。别被那些花里胡哨的框架带偏了,回到 HTTP 协议本质,你会发现很多所谓的“复杂架构”,底层都是 Request 和 Response 在打交道。今天不讲虚的,直接拿一个真实的内部工具站案例,拆解怎么从零搭建一个基于 Servlet 的轻量级系统。
项目背景与需求:为什么我们要退回 Servlet
这个项目叫“运维监控看板”,服务对象是公司内部的 20 名运维工程师。最初外包团队给搞了个 Spring Boot + Vue 的全家桶,看着高大上,但有两个致命伤:一是部署依赖太重,JDK 版本、Tomcat 版本、Redis 连接配置,稍微有点风吹草动就起不来;二是改个逻辑,外包小哥要先拉代码、起服务、等编译,一来一回半天没了。
客户的核心诉求其实很简单:
- 极速响应:页面加载必须在 1 秒内,数据实时刷新。
- 极简部署:打成一个 WAR 包,扔进任何标准 Tomcat 就能跑,不要依赖外部数据库(数据源是本地文件),不要复杂的中间件。
- 代码透明:运维大哥们得能看懂代码,最好别用太多注解魔法,逻辑要直观。
这时候,做网站servlet从零搭建的优势就出来了。Servlet 是 Java Web 开发的地基,它没有黑盒,每一个请求怎么进来、怎么处理、怎么出去,全是明文逻辑。对于这种内部小工具,过度设计就是最大的坑。我们决定抛弃 Spring,回归原生 Servlet + JSP(或者 Thymeleaf 轻量模板),配合 Jackson 处理 JSON,构建一个极简但健壮的系统。
技术选型:拒绝过度设计,只选最稳的
在动手之前,技术选型得掰开了揉碎了说。很多新手一上来就想问:“老师,用什么框架最火?”对于本项目,我的原则是:能用原生解决的,绝不上框架;必须用框架的,选最轻量级的。
1. 核心容器:Tomcat 9 选 Tomcat 9 而不是 10,是因为 Tomcat 10 改动了命名空间,很多老版本的库不兼容。对于企业级项目,稳定性大于新特性。Tomcat 9 对 Java 11 的支持非常成熟,且社区资料多,遇到问题容易找答案。
2. 开发语言:Java 11 Java 8 太老,Java 17 是新 LTS,但很多公司内部环境还停留在 11。Java 11 引入了 HTTP Client 新 API,虽然本项目主要处理请求,但后续扩展调用第三方接口时,原生 HTTP Client 比 HttpClient 4.x 好用得多。
3. 模板引擎:JSP vs Thymeleaf
有人问,JSP 不是淘汰了吗?在内部工具站,JSP 依然是神器。为什么?因为运维大哥喜欢改样式,JSP 是 HTML 和 Java 代码混排,改个 <div> 不用重启服务,热部署极快。Thymeleaf 虽然更优雅,但调试时得看后端日志。为了降低沟通成本,我们选了 JSP。
4. 工具类:Jackson
处理 JSON 必选 Jackson。比 Gson 功能强,比 FastJSON 安全(避免反序列化漏洞)。在 pom.xml 中引入即可。
5. 日志:SLF4J + Logback
别用 System.out.println,那是新手村行为。必须接入日志框架,方便排查生产环境问题。
这里有个细节值得注意:虽然我们是从零搭建,但并不是说要从 JDK 源码写起,而是从项目骨架、目录结构、配置文件开始,一步步构建可运行的最小闭环。
核心实现:代码即文档,逻辑要透明
这一部分是全篇的重点。我不贴那种几百行的 Controller,只贴核心逻辑。Servlet 的本质就是一个实现了 HttpServlet 接口的类。
1. 目录结构设计
标准的 Servlet 项目结构如下,这种结构符合 W3C 标准 对 Web 应用部署的描述,也是大多数企业规范的基础:
src/main/java/com/company/monitor/controller/ // Servlet 控制器DataServlet.javamodel/ // 数据模型Metric.javautil/ // 工具类JsonUtil.java
src/main/webapp/WEB-INF/web.xml // 部署描述符(虽然可以用注解,但显式配置更清晰)jsp/index.jsp // 首页chart.jsp // 图表页static/css/js/
2. 核心 Servlet 代码:DataServlet.java
这个 Servlet 负责接收前端 AJAX 请求,返回最新的监控数据。注意,我们没有用任何注解 @WebServlet,而是使用 web.xml 配置,这样运维人员不用看代码就知道路由在哪。
package com.company.monitor.controller;import com.company.monitor.model.Metric;
import com.company.monitor.util.JsonUtil;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 数据接口 Servlet* 注意:高并发下,Servlet 是单例的,成员变量必须线程安全或使用局部变量*/
@WebServlet("/api/data")
public class DataServlet extends HttpServlet {// 模拟数据获取服务,实际项目中这里应该是从文件或API拉取private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(5);@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1. 设置响应头,避免缓存,确保数据实时性response.setContentType("application/json;charset=UTF-8");response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");response.setHeader("Pragma", "no-cache");response.setDateHeader("Expires", 0);try {// 2. 模拟耗时操作,实际场景中可能是读取本地 CSV 文件// 这里用异步执行来避免阻塞 Tomcat 工作线程List<Metric> metrics = fetchMetricsFromLocalFile();// 3. 序列化并输出String json = JsonUtil.toJson(metrics);response.getWriter().write(json);} catch (Exception e) {// 4. 异常处理,返回统一错误格式,不要暴露堆栈信息response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);response.getWriter().write("{\"error\":\"Server Error\"}");e.printStackTrace(); // 实际项目中应记录到日志文件}}private List<Metric> fetchMetricsFromLocalFile() {// 伪代码:读取 /data/metrics.csv// 这里省略具体 IO 操作,重点展示流程return MockDataGenerator.getLatestData();}
}
代码解析关键点:
- 单例陷阱:
HttpServlet实例是单例的,但每个请求对应一个新的HttpServletRequest和HttpServletResponse。所以严禁在 Servlet 中定义非static的成员变量来存储请求数据,否则会出现数据错乱。这是后端初学者最容易踩的坑。 - 异步处理:虽然
doGet是同步的,但如果数据源很慢,建议引入异步 Servlet 机制(AsyncContext),或者像上面那样,如果逻辑简单,直接快速返回。如果逻辑复杂,建议将业务逻辑剥离到 Service 层,Servlet 只做参数解析和结果封装。 - 字符编码:务必设置
UTF-8,否则中文乱码是家常便饭。
3. JSON 工具类:JsonUtil.java
package com.company.monitor.util;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;public class JsonUtil {private static final ObjectMapper MAPPER = new ObjectMapper();// 忽略 null 值,保持 JSON 干净static {MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);}public static String toJson(Object obj) {try {return MAPPER.writeValueAsString(obj);} catch (JsonProcessingException e) {throw new RuntimeException("JSON Serialization Error", e);}}
}
4. 前端 AJAX 调用:index.jsp 片段
<script>function loadMetrics() {fetch('/monitor/api/data').then(response => response.json()).then(data => {// 更新 DOMdocument.getElementById('cpu').innerText = data[0].cpu + '%';document.getElementById('mem').innerText = data[0].mem + 'MB';}).catch(error => console.error('Error:', error));}// 每 5 秒刷新一次setInterval(loadMetrics, 5000);loadMetrics(); // 初始加载
</script>
这段代码展示了做网站servlet从零搭建中最典型的前后端交互模式。没有 WebSocket,没有复杂的实时推送,就是简单的轮询。对于内部监控工具,5 秒一次的轮询完全够用,且实现成本极低。
上线与优化:从本地到生产的最后一公里
代码写完了,别急着发版。从本地 mvn jetty:run 到生产环境 Tomcat,还有几个坑要填。
1. 性能调优:Tomcat 线程池
默认 Tomcat 的线程数只有 150,对于高并发可能不够。修改 conf/server.xml 中的 Connector 配置:
<Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443"maxThreads="200"minSpareThreads="25"acceptCount="100" />
maxThreads:最大工作线程数,建议设置为 CPU 核心数的 2-4 倍,具体压测后调整。acceptCount:当所有线程都忙时,等待连接请求的队列长度。
2. 静态资源分离
JSP 和 Servlet 处理动态内容,静态资源(CSS, JS, 图片)应该由 Nginx 处理。不要把所有流量都压在 Tomcat 上。部署时,Nginx 配置 location /static/ 指向静态目录,location / 反向代理到 Tomcat。
3. 日志监控
配置 Logback,将日志按天滚动,并保留最近 30 天。生产环境日志级别设为 INFO,不要开 DEBUG,否则磁盘很快就满了。
4. 安全加固
- HTTPS:必须上 SSL 证书。即使是内网,数据加密也是基本要求。
- 输入校验:虽然本项目主要返回数据,但如果未来加入参数查询,务必对
request.getParameter()进行校验,防止 SQL 注入或 XSS 攻击。Servlet 原生没有 ORM,SQL 语句是手写的,更要小心。
5. 健康检查接口
增加一个 /health Servlet,返回 {"status":"UP"}。供 K8s 或负载均衡器做健康检查。
经验总结:为什么 Servlet 依然有生命力
做完这个项目,我最大的感受是:技术没有过时,只有场景不匹配。
很多后端新人觉得 Servlet 土,是因为他们在做 C 端高并发业务,那里确实需要 Spring Cloud、微服务、消息队列。但对于 B 端内部工具、遗留系统改造、或者对稳定性要求极高且资源受限的场景,做网站servlet从零搭建依然是最佳实践之一。
它的好处在于:
- 可控性强:每一行代码都清晰可见,出了问题好排查。
- 依赖极少:打包体积小,启动速度快,内存占用低。
- 标准化:符合 W3C 标准 和 Java EE 规范,跨平台能力强。
当然,如果你刚开始学习后端,我强烈建议你从零搭建一个 Servlet 项目。不要一上来就学 Spring Boot。先把 HTTP 请求生命周期、Servlet 容器原理、JDBC 连接池搞懂,再学框架,你会发现框架的底层原理其实没那么神秘。
最后,我想问大家一个很实际的问题:在你之前的项目中,有没有遇到过因为技术选型过度复杂,导致运维成本远高于开发成本的情况?或者你在用原生技术栈解决复杂业务时,踩过哪些意想不到的坑?欢迎在评论区分享你的真实经历,咱们一起避坑。