← [ BACK_TO_NOTES ]

使用 Java 构建 HTTP 服务

用 JDK 内置 HttpServer 构建可测最小 HTTP 服务:路由、状态码与优雅关闭入口。

#howto #growing #dev #java

[!info] related notes

使用 Java 构建 HTTP 服务

目标

  1. 启动本机 HTTP 端口
  2. 实现健康检查与简单业务 GET
  3. 返回正确状态码
  4. 能关闭服务器

这个场景为什么出现

先不引入 Spring,也能理解请求-响应、线程与路由。框架是装配加速器;HTTP 语义是底座。

[!abstract] 一句话理解 用 com.sun.net.httpserver.HttpServer(JDK 自带)绑定端口与上下文处理器,完成最小可测服务闭环。

最小可运行示例

场景:订单查询服务提供 /health 与 /orders/{id}

import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

public class MiniOrderServer {
    public static void main(String[] args) throws Exception {
        Map<String, String> db = Map.of("1", "PAID", "2", "CREATED");
        int port = args.length == 0 ? 8080 : Integer.parseInt(args[0]);
        HttpServer server = HttpServer.create(new InetSocketAddress("127.0.x.x", port), 0);
        server.createContext("/", ex -> {
            if (!ex.getRequestMethod().equals("GET")) {
                ex.getResponseHeaders().set("Allow", "GET");
                write(ex, 405, "method-not-allowed");
                return;
            }
            String path = ex.getRequestURI().getPath();
            if (path.equals("/health")) {
                write(ex, 200, "ok");
            } else if (path.startsWith("/orders/")) {
                String id = path.substring("/orders/".length());
                String status = db.get(id);
                if (status == null) write(ex, 404, "not-found");
                else write(ex, 200, status);
            } else {
                write(ex, 404, "not-found");
            }
        });
        ExecutorService workers = Executors.newFixedThreadPool(4);
        server.setExecutor(workers);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try { server.stop(1); }
            finally { workers.shutdown(); }
        }));
        server.start();
        System.out.println("listening " + server.getAddress().getPort());
        // HttpServer 的非 daemon 后台线程保持进程运行;Ctrl+C 触发关闭钩子。
    }

    static void write(com.sun.net.httpserver.HttpExchange ex, int code, String body) throws IOException {
        byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
        try {
            ex.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
            ex.sendResponseHeaders(code, bytes.length);
            try (OutputStream os = ex.getResponseBody()) { os.write(bytes); }
        } finally {
            ex.close();
        }
    }
}
javac MiniOrderServer.java
java MiniOrderServer
# 另开终端:curl -i http://127.0.x.x:8080/health
# 在服务终端按 Ctrl+C 关闭

期望:GET /health 返回 200 和 ok;GET /orders/1 返回 200 和 PAID;未知路径/id 返回 404;本例对非 GET 请求统一返回 405 并附 Allow: GET。可用 java MiniOrderServer 0 让系统分配测试端口,以打印值为准。

该 API 位于 JDK 的 jdk.httpserver 模块;命名模块项目需声明 requires jdk.httpserver。本例仅绑定回环地址用于学习,没有完整请求体限制、过载保护或生产级停机协调。

结合场景再看三个关注点

  1. 状态码是契约。
  2. 线程池执行请求;newFixedThreadPool 的任务队列无界,固定线程数不代表负载已受控。
  3. 关闭包含服务器与自建线程池;server.stop(1) 停止接入并最多等待约定延迟后关闭交换,自建 executor 仍需单独关闭。

核心步骤与准确模型

  1. 选择服务器抽象(JDK/框架);HttpServer context 按路径前缀匹配,精确业务路由应在处理器中进一步核实。
  2. 定义路由与媒体类型
  3. 注入业务服务
  4. 错误映射
  5. 观测与关闭

工程实践

  1. 超时、请求体大小限制。
  2. 统一 JSON 错误体。
  3. 健康检查与就绪检查分离。
  4. 再引入 Spring 对照。

本节总结

  • 标准路径先跑通 HTTP
  • 框架后置
  • 状态码/线程/关闭是核心

自测题

  1. 404 与 500 如何分工?
  2. 为什么处理器里要限制 body 大小?
参考答案
  1. 404 资源不存在;500 服务器未处理异常。
  2. 防 OOM/慢请求攻击。

延伸阅读与资料来源

资料类型支撑内容
HttpServer APIAPIJDK 服务器
创建于 2026/7/15 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本