多数 Java Web 框架会把默认 HTTP 服务器打进起步依赖,开发者往往直到排查线程、连接或容器兼容性问题时,才意识到应用已经和某个引擎深度绑定。Solon 采用了不同的拆分方式:约 0.3MB 的内核不固定内置 HTTP 服务器,而是通过独立插件接入服务器引擎。
来源提到 Solon 提供 10 种 HTTP 服务器选项。真正值得关注的并不只是数量,而是这些引擎共享同一套应用编程模型:切换服务器时,通常只需要替换一项构建依赖,不必修改路由和业务代码。
服务器是运行时插件,不是业务层前提
一个典型的 Solon 应用可以只依赖框架 API 编写入口和路由:
package demo;
import org.noear.solon.Solon;
public class App {
public static void main(String[] args) {
Solon.start(App.class, args, app -> {
app.get("/health", ctx -> ctx.output("ok"));
app.get("/hello/:name", ctx -> {
String name = ctx.param("name");
ctx.output("Hello, " + name);
});
});
}
}
这里没有直接创建某个服务器的 Server、Handler 或事件循环。服务器插件负责启动监听端口,再把请求交给 Solon 的路由层。这层边界让同一份应用代码可以运行在不同引擎之上。
这种结构尤其适合以下情况:
- 本地开发需要启动快、占用小的服务器。
- 线上环境更看重吞吐量、连接数或成熟的运维能力。
- 企业平台对线程模型、协议支持或容器组件有统一要求。
- 框架升级期间需要并行验证两个服务器引擎。
“改一行依赖”究竟改了什么
下面是一个可直接改造的 Maven 配置。由于不同 Solon 版本的插件坐标可能变化,示例把服务器插件名做成属性;运行前应将 SERVER_PLUGIN_A 和 SERVER_PLUGIN_B 替换成当前 Solon 版本实际提供的 artifactId。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>solon-server-switch</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<solon.version>请替换为项目使用的版本</solon.version>
<server.plugin>SERVER_PLUGIN_A</server.plugin>
</properties>
<dependencies>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon</artifactId>
<version>${solon.version}</version>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>${server.plugin}</artifactId>
<version>${solon.version}</version>
</dependency>
</dependencies>
</project>
替换引擎时,应用源码保持不动,只调整属性:
mvn clean package -Dserver.plugin=SERVER_PLUGIN_B
也可以用 Maven Profile 固化开发和生产环境,避免手工编辑 pom.xml:
<profiles>
<profile>
<id>dev-server</id>
<properties>
<server.plugin>SERVER_PLUGIN_A</server.plugin>
</properties>
</profile>
<profile>
<id>prod-server</id>
<properties>
<server.plugin>SERVER_PLUGIN_B</server.plugin>
</properties>
</profile>
</profiles>
对应的构建命令是:
mvn clean package -Pdev-server
mvn clean package -Pprod-server
应确保最终产物只带入一个负责监听端口的服务器插件。若同时选择两个插件,需要先确认当前版本的选择优先级和配置方式,否则可能出现重复启动、端口冲突或实际生效引擎不明确的问题。
十种选择不等于盲目轮换
服务器引擎没有脱离场景的绝对排名。评估 Solon 提供的 10 种选项时,可以把候选范围压缩到几个可测量维度:
| 维度 | 需要验证的问题 |
|---|---|
| 启动与内存 | 冷启动耗时、空载 RSS 和类加载数量是否满足部署要求? |
| 并发模型 | 阻塞请求、异步请求、长连接分别如何调度线程? |
| 协议能力 | 项目是否需要 HTTPS、HTTP/2、WebSocket 或大文件上传? |
| 运维接入 | 指标、访问日志、优雅停机和故障诊断是否完整? |
| 生态兼容 | 过滤器、中间件、代理头和现有基础设施能否正常工作? |
不要只用一个“Hello World”吞吐数字做决定。真实服务还包含 JSON 编解码、数据库访问、鉴权和日志写入;当业务处理占据主要耗时时,服务器之间的微基准差距未必会转化为端到端收益。
可以这样实践一个最小回归测试:
# 启动应用后检查健康接口
curl --fail --silent http://127.0.0.1:8080/health
# 验证路径参数和响应内容
curl --fail http://127.0.0.1:8080/hello/Solon
# 使用已有压测工具对同一个接口执行可重复测试
wrk -t4 -c100 -d30s http://127.0.0.1:8080/health
每次只替换服务器依赖,并保持 JDK、堆大小、机器规格、请求内容和压测并发一致。除了吞吐量,还要记录 P95/P99 延迟、CPU、RSS、GC 暂停和错误率。
切换引擎时仍要检查边界
“不改应用代码”指的是 Solon 路由和业务层可以保持稳定,并不意味着所有运行行为完全相同。上线前至少检查以下项目:
- 请求体、上传文件和响应缓冲区的默认上限。
- Keep-Alive、读取超时、写入超时和空闲连接超时。
- 工作线程、I/O 线程以及队列长度的默认值。
- 反向代理后的客户端 IP、Host 和 HTTPS 协议识别。
- WebSocket、流式响应和优雅停机的实际表现。
- 服务器插件是否被完整打入最终 JAR,以及是否混入多个实现。
可以用 Maven 查看最终依赖树:
mvn dependency:tree -Dincludes=org.noear
Solon 的价值在于把服务器选择从架构重写降为构建和部署决策。实际采用时,先选一个团队熟悉的引擎作为基线,再用同一套接口测试、回归测试和生产流量模型比较候选项。依赖确实可能只改一行,但上线决策仍应由兼容性数据和真实负载结果支撑。