# netty-proxy-spring-boot **Repository Path**: a-crud-boy/netty-proxy-spring-boot ## Basic Information - **Project Name**: netty-proxy-spring-boot - **Description**: 类似nginx的springboot starter 可以代理静态页面 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-02-05 - **Last Updated**: 2026-06-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Netty 仿Nginx 静态文件服务器 Spring Boot Starter 基于 Netty 的高性能静态文件服务器 Spring Boot Starter,提供独立的静态资源服务能力。 ## 特性 - 🚀 **高性能**:基于 Netty 异步 I/O,零拷贝文件传输 - 📦 **开箱即用**:零配置即可使用,支持 Spring Boot 自动配置 - 🔧 **灵活配置**:支持多路径映射、缓存控制、Gzip 压缩等 - 🎨 **美观界面**:内置美观的目录浏览页面 - 🌐 **SPA 支持**:完美支持单页应用(Vue、React 等) - 🔒 **安全防护**:路径遍历防护、文件名过滤等安全机制 - 📋 **MIME 类型**:内置 40+ 种文件类型的 MIME 类型映射 ## 快速开始 ### 1. 添加依赖 在 `pom.xml` 中添加依赖: ```xml com.spring.boot netty-proxy-spring-boot-starter-2x 1.0.0 ``` ```xml com.spring.boot netty-proxy-spring-boot-starter-3x 1.0.0 ``` ### 2. 基础配置 在 `application.yml` 中添加配置: ```yaml static-server: enabled: true port: 18080 host: 0.0.0.0 mappings: - path: "/" location: "D:/dist" ``` ### 3. 启动应用 启动 Spring Boot 应用,访问 `http://localhost:18080` 即可查看静态文件。 ## 配置说明 ### 服务器配置 #### 单服务器模式 当未配置 `servers` 列表时,使用顶层配置作为单服务器配置: | 配置项 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | `enabled` | boolean | true | 是否启用静态服务器 | | `port` | int | 18080 | 监听端口 | | `host` | String | 0.0.0.0 | 绑定地址 | | `server-name` | String | localhost | 服务器名称 | | `keepalive-timeout` | int | 70 | HTTP Keep-Alive 超时时间(秒) | | `worker-threads` | int | 0 | 工作线程数(0=CPU核心数) | | `gzip` | boolean | true | 是否开启 Gzip 压缩 | | `gzip-min-length` | int | 1024 | Gzip 压缩最小文件大小(字节) | #### 多服务器模式 当配置了 `servers` 列表时,每个服务器可以独立配置,配置项与单服务器模式相同,但需要额外配置: | 配置项 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | `name` | String | - | 服务器名称(用于日志标识) | | `port` | int | 18080 | 监听端口 | | `host` | String | 0.0.0.0 | 绑定地址 | | `server-name` | String | localhost | 服务器名称 | | `keepalive-timeout` | int | 70 | HTTP Keep-Alive 超时时间(秒) | | `worker-threads` | int | 0 | 工作线程数(0=CPU核心数) | | `gzip` | boolean | true | 是否开启 Gzip 压缩 | | `gzip-min-length` | int | 1024 | Gzip 压缩最小文件大小(字节) | ### 映射配置(通用) | 配置项 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | `path` | String | "/" | URL 路径前缀 | | `location` | String | - | 本地文件系统路径(静态文件服务时必填) | | `proxy-pass` | String | - | 反向代理目标地址(配置后走代理逻辑) | | `index` | List | [index.html, index.htm] | 默认首页文件列表 | | `cache-seconds` | int | 0 | 缓存时间(秒),0 表示不缓存 | | `spa` | boolean | false | SPA 模式,找不到文件时返回 index.html | | `auto-index` | boolean | false | 是否开启目录浏览 | **注意**:`location` 和 `proxy-pass` 二选一,配置了 `proxy-pass` 时会走反向代理逻辑。 ## 使用示例 ### 示例 1:托管前端应用 ```yaml static-server: port: 18080 gzip: true mappings: - path: "/" location: "/var/www/frontend/dist" index: ["index.html"] spa: true cache-seconds: 3600 ``` ### 示例 2:多路径映射(单服务器模式) ```yaml static-server: port: 18080 mappings: - path: "/" location: "./public" auto-index: true - path: "/static" location: "./assets/static" cache-seconds: 86400 - path: "/admin" location: "./admin/dist" spa: true ``` ### 示例 3:图片服务 ```yaml static-server: port: 18080 gzip: false mappings: - path: "/images" location: "/data/images" cache-seconds: 31536000 # 缓存一年 ``` ### 示例 4:开发环境配置 ```yaml static-server: port: 3000 worker-threads: 4 gzip: true mappings: - path: "/" location: "./src/main/resources/static" auto-index: true cache-seconds: 0 # 不缓存 ``` ## 高级特性 ### SPA 模式 SPA 模式适用于 Vue、React 等单页应用。当请求的文件不存在时,服务器会自动返回 `index.html`。 ```yaml static-server: mappings: - path: "/" location: "./dist" spa: true index: ["index.html"] ``` ### 目录浏览 开启目录浏览后,访问目录时会显示文件列表: ```yaml static-server: mappings: - path: "/" location: "./files" auto-index: true ``` ### 缓存控制 设置缓存时间可以减少服务器负载: ```yaml static-server: mappings: - path: "/static" location: "./static" cache-seconds: 3600 # 缓存 1 小时 ``` ### Gzip 压缩 自动压缩文本类文件(HTML、CSS、JS、JSON 等): ```yaml static-server: gzip: true gzip-min-length: 1024 # 大于 1KB 的文件才压缩 ``` ## 支持的文件类型 ### 文档 - HTML: `.html`, `.htm` - CSS: `.css` - JavaScript: `.js`, `.mjs` - JSON: `.json` - XML: `.xml` - Markdown: `.md` - PDF: `.pdf` - Text: `.txt` ### 图片 - PNG: `.png` - JPEG: `.jpg`, `.jpeg` - GIF: `.gif` - SVG: `.svg` - ICO: `.ico` - WebP: `.webp` - AVIF: `.avif` ### 字体 - WOFF: `.woff` - WOFF2: `.woff2` - TTF: `.ttf` - OTF: `.otf` - EOT: `.eot` ### 媒体 - Audio: `.mp3`, `.wav`, `.ogg` - Video: `.mp4`, `.webm` ### 压缩包 - ZIP: `.zip` - GZIP: `.gz` - TAR: `.tar` ### 其他 - WASM: `.wasm` - Source Map: `.map` ## 安全特性 - **路径遍历防护**:防止 `../` 等路径遍历攻击 - **文件名过滤**:防止恶意文件名 - **隐藏文件保护**:不显示隐藏文件 - **URI 清理**:防止 URL 编码攻击 - **请求方法限制**:仅支持 GET 和 HEAD 请求 ## 性能优化 - **零拷贝文件传输**:使用 `DefaultFileRegion` 实现高效文件传输 - **Gzip 压缩**:减少传输体积 - **HTTP Keep-Alive**:支持连接复用 - **可配置工作线程数**:根据服务器性能调整 ## 扩展与插件机制 ### HttpHandler 扩展点(核心处理能力) 从 `1.0.0` 版本开始,核心路由基于 `HttpHandler` 抽象进行处理分发,内置了: - 静态文件处理:`StaticHttpHandler`(基于 `StaticFileHandler`) - 反向代理处理:`ProxyHttpHandler`(基于 `ProxyHandler`) - 访问日志:`AccessLogHttpHandler`(包装所有 Handler,记录访问日志) 你可以实现自己的 `HttpHandler` 来扩展功能,例如鉴权、限流、自定义业务逻辑、WebSocket 升级等: ```java public class MyCustomHttpHandler implements HttpHandler { @Override public String name() { return "my-custom"; } @Override public boolean supports(FullHttpRequest request, StaticMapping mapping) { // 根据路径 / 方法 / 映射等判断是否由当前 Handler 处理 return request.uri().startsWith("/api/custom"); } @Override public boolean handle(ChannelHandlerContext ctx, FullHttpRequest request, StaticMapping mapping) throws Exception { // 在这里完成自定义处理逻辑 // 返回 true 表示已经完全处理,后续 Handler 不再处理 // 返回 false 表示可以交给后续 Handler 继续处理 return true; } } ``` > 说明:当前版本的 `HttpHandler` 接口为 `supports(..) + handle(..)`,未来如果扩展为返回 `boolean` 以支持“链式过滤”,接口会保持兼容演进。 ### StaticServerModule SPI 插件机制 为了方便第三方在 **不修改源码** 的情况下扩展能力,核心模块提供了基于 JDK `ServiceLoader` 的 SPI 接口: - 接口:`com.spring.boot.staticserver.StaticServerModule` - 上下文:`com.spring.boot.staticserver.StaticServerModuleContext` 实现步骤: 1. 在你的扩展 jar 中实现 `StaticServerModule`: ```java public class MyStaticServerModule implements StaticServerModule { @Override public void initialize(StaticServerModuleContext context) { ServerConfig serverConfig = context.getServerConfig(); // 可根据不同 serverConfig(端口 / serverName 等)决定是否注册 context.registerHttpHandler(new MyCustomHttpHandler()); } } ``` 2. 在你的 jar 中添加 SPI 声明文件: `META-INF/services/com.spring.boot.staticserver.StaticServerModule`: ```text com.example.MyStaticServerModule ``` 3. 将你的扩展 jar 放到应用的依赖中(例如在 Spring Boot 应用的 `pom.xml` 中添加依赖)。 > 当 `NettyStaticServer` 启动时,会自动通过 `ServiceLoader` 发现并初始化所有 `StaticServerModule`,并把它们注册的 `HttpHandler` 插入到内置处理链之前。 ### 典型扩展场景 - 自定义鉴权:在模块中注册一个 `HttpHandler`,在 `supports` 中匹配需要鉴权的路径,在 `handle` 中做 Token 校验,不通过则直接返回 401。 - 限流 / 熔断:实现 `HttpHandler` 或后续的 `HttpFilter`,根据 QPS、IP 等维度限制请求。 - WebSocket 支持:实现一个 `HttpHandler`,在 `supports` 中判断 `Upgrade: websocket` 请求,在 `handle` 中完成协议升级并切换到 WebSocket pipeline。 以上扩展能力对使用者是透明的:**只要引入你的扩展 jar,重新启动应用即可生效**。 ## 监控和日志 服务器启动时会输出映射配置信息: ``` ┌────────────────────────────────────────────────────────────┐ │ Static Server Mappings │ ├────────────────────────────────────────────────────────────┤ │ ✓ / -> ./public [AutoIndex] │ │ ✓ /static -> ./assets/static │ │ ✓ /admin -> ./admin/dist [SPA] │ └────────────────────────────────────────────────────────────┘ ``` ## 常见问题 ### Q: 如何修改默认端口? A: 在配置文件中设置 `static-server.port`: ```yaml static-server: port: 18080 ``` ### Q: 如何禁用静态服务器? A: 设置 `static-server.enabled` 为 `false`: ```yaml static-server: enabled: false ``` ### Q: 如何支持 HTTPS? A: 从当前版本开始,内置支持基于证书的 HTTPS,你可以为单个或多个 server 分别开启 SSL: 单服务器模式示例(PKCS12 证书): ```yaml static-server: enabled: true port: 8443 ssl-enabled: true ssl-key-store: classpath:certs/server.p12 ssl-key-store-password: changeit ssl-key-store-type: PKCS12 # 可选,默认 PKCS12 mappings: - path: "/" location: "/var/www/https-site" ``` 多服务器模式示例(为其中一个 server 开启 HTTPS): ```yaml static-server: enabled: true servers: - name: http-site host: 0.0.0.0 port: 18080 mappings: - path: / location: /data/www/site-http - name: https-site host: 0.0.0.0 port: 18443 ssl-enabled: true ssl-key-store: classpath:certs/server.p12 ssl-key-store-password: changeit ssl-key-store-type: PKCS12 mappings: - path: / location: /data/www/site-https ``` > 证书可以使用 JKS 或 PKCS12,`ssl-key-store` 支持文件路径或 `classpath:` 前缀。启用 SSL 后,直接访问 `https://host:port` 即可。 ### Q: 如何限制访问? A: 建议使用防火墙或反向代理进行访问控制。 ### Q: 如何像 Nginx 一样配置多个站点? A: 使用 `static-server.servers`(多服务器模式)。当配置了 `servers` 列表时,会启用多服务器模式,每个服务器可以独立配置端口和映射;如果未配置 `servers` 或列表为空,则使用单服务器模式(使用顶层配置): ```yaml static-server: enabled: true servers: - name: site-a host: 0.0.0.0 port: 18080 server-name: site-a.local keepalive-timeout: 70 mappings: - path: / location: /data/www/site-a spa: true cache-seconds: 3600 - name: site-b host: 0.0.0.0 port: 18081 server-name: site-b.local mappings: - path: / location: /data/www/site-b ``` ### Q: 如何做简单的反向代理? A: 在 `mappings` 中使用 `proxy-pass`,当配置了 `proxy-pass` 时会走代理逻辑: ```yaml static-server: enabled: true servers: - name: api-proxy host: 0.0.0.0 port: 19000 mappings: - path: / proxy-pass: http://localhost:8080 ``` ### Q: 文件上传后立即生效吗? A: 是的,服务器会实时读取文件系统,无需重启。 ## 技术栈 - **Netty**: netty-all(版本由 Spring Boot 依赖管理) - **Spring Boot**: 2.5.15+ / 3.2.0+ - **Java**: 8+(Boot 2.x starter)/ 17+(Boot 3.x starter) ## 许可证 本项目采用 MIT 许可证。 ## 贡献 欢迎提交 Issue 和 Pull Request!