一、开篇:Server 插件化设计哲学
Java Web 开发中,应用框架与底层服务器通常是强绑定的。你选了一个框架,就绑定了它支持的服务器实现。切换?往往意味着大量适配工作。
Solon 的设计哲学不同:业务代码与底层容器完全解耦。
在 Solon 中,你的 Controller、Service、Repository 等业务代码是"不变的",而底层服务器是"可插拔的"。只需要换一个 Maven 依赖,就能从 JDK 内置 HTTP 服务器(0.3MB)切换到 Jetty(2.7MB)、Undertow(4.6MB)或 Vert.x(6.3MB)——业务代码零修改。
这就是 Solon Server 插件化设计的核心价值:
| 维度 | 传统方式 | Solon 方式 |
|---|---|---|
| 框架与服务器关系 | 强绑定 | 可插拔 |
| 切换服务器 | 改代码/改配置 | 换一个 Maven 依赖 |
| 包大小 | 通常较大 | 按需选择,最小 0.3MB |
| 协议支持 | 通常只有 HTTP | HTTP + WebSocket + Socket.D |
本文将带你深入解析 Solon 的 Server 启动模式体系。
二、应用启动入口与生命周期
2.1 标准启动方式
所有 Solon 应用的入口都是Solon.start():
@SolonMain public class App { public static void main(String[] args) { Solon.start(App.class, args); } }2.2 带初始化函数的启动
第三个参数是初始化函数,在应用初始化时机点执行:
@SolonMain public class App { public static void main(String[] args) { Solon.start(App.class, args, app -> { // 控制信号启停、订阅早期事件等 app.enableHttp(true); app.enableWebSocket(true); }); } }2.3 应用生命周期全景
Solon 应用的生命周期包含四个层次的时机点:
① 一个初始化函数时机点-Solon.start()的第三个参数回调
② 六个应用事件时机点
| 事件 | 说明 | 订阅方式 |
|---|---|---|
| AppInitEndEvent | 应用初始化完成 | 只支持手动订阅 |
| AppPluginLoadEndEvent | 插件加载完成 | 只支持手动订阅 |
| AppBeanLoadEndEvent | Bean 扫描完成 | 自动/手动 |
| AppLoadEndEvent | 应用启动完成 | 自动/手动 |
| AppPrestopEndEvent | 应用预停止 | 自动/手动 |
| AppStopEndEvent | 应用停止 | 自动/手动 |
③ 三个插件生命时机点
public interface Plugin { void start(AppContext context) throws Throwable; default void prestop() throws Throwable {} default void stop() throws Throwable {} }④ 两个容器生命时机点-AppContext::start()— 扫描完成后执行 -AppContext::stop()— 插件 stop 后执行
AppBeanLoadEndEvent之前的事件需要在启动前完成订阅,否则会错过时机。三、启动参数体系
3.1 完整参数表
启动参数在应用启动后会被静态化(启动后不可修改)。
| 启动参数 | 对应配置 | 描述 |
|---|---|---|
| --env | solon.env | 环境变量(配置切换) |
| --scanning | — | 是否扫描(默认 1) |
| --debug | solon.debug | 调试模式(0 或 1) |
| --setup | solon.setup | 安装模式(0 或 1) |
| --white | solon.white | 白名单模式(0 或 1) |
| --drift | solon.drift | 漂移模式(k8s 部署设为 1) |
| --alone | solon.alone | 单体模式(0 或 1) |
| --extend | solon.extend | 扩展目录 |
| --locale | solon.locale | 默认地区 |
| --config.add | solon.config.add | 增加外部配置 |
| --app.name | solon.app.name | 应用名 |
| --app.group | solon.app.group | 应用分组 |
| --app.title | solon.app.title | 应用标题 |
| --stop.safe | solon.stop.safe | 安全停止(0 或 1) |
| --stop.delay | solon.stop.delay | 安全停止延时秒数(默认 10) |
3.2 三种等价写法
所有带.的启动参数同时会成为应用配置,以下三种写法完全等价:
# 方式一:JVM 系统属性 java -Dsolon.env=dev -jar demo.jar # 方式二:完整的命令行参数 java -jar demo.jar --solon.env=dev # 方式三:简写命令行参数 java -jar demo.jar --env=dev同理,server.port也支持三种写法:
java -Dserver.port=8081 -jar demo.jar java -jar demo.jar --server.port=8081四、Server 插件全景矩阵
Solon Server 系列包含所有通讯"服务启动器"插件。切换 Boot 插件只需更换 Maven/Gradle 依赖。
4.1 HTTP 类 Server 插件
| 插件 | 框架版本 | 包大小 | 信号协议 | JDK要求 | 开源协议 |
|---|---|---|---|---|---|
| solon-server-jdkhttp | JDK | 0.3MB | http | 8+ | Apache 2.0 |
| solon-server-smarthttp [国产] | — | 0.8MB | http, ws | 8+ | Apache 2.0 |
| solon-server-grizzly | — | 1.8MB | http, ws, http2 | 8+ | EPL-2.0 |
| solon-server-vertx | — | 6.3MB | http, ws, http2 | 8+ | EPL-2.0 |
| solon-server-jetty | v9 | 2.7MB | http, ws | 8+ | EPL-2.0 |
| solon-server-jetty-jakarta | v12 | 3.9MB | http, ws, http2 | 17+ | EPL-2.0 |
| solon-server-undertow | v2.2 | 4.6MB | http, ws, http2 | 8+ | Apache 2.0 |
| solon-server-undertow-jakarta | v2.3 | — | http, ws, http2 | 17+ | Apache 2.0 |
| solon-server-tomcat | v9 | — | http, ws, http2 | 8+ | Apache 2.0 |
| solon-server-tomcat-jakarta | v11 | — | http, ws, http2 | 17+ | Apache 2.0 |
4.2 WebSocket 类 Server 插件
| 插件 | 包大小 | 信号协议 | JDK要求 | 开源协议 |
|---|---|---|---|---|
| solon-server-websocket | 0.4MB | ws | 8+ | MIT |
| solon-server-websocket-netty | 3.6MB | ws | 8+ | Apache 2.0 |
4.3 Socket.D 类 Server 插件
| 插件 | 包大小 | 信号协议 | JDK要求 | 开源协议 |
|---|---|---|---|---|
| solon-server-socketd | 0.4MB | tcp, udp, ws | 8+ | Apache 2.0 |
4.4 插件切换方法
切换 Boot 插件只需更换依赖,业务代码无需任何修改:
<!-- 替换前:使用 JDK 内置 HTTP 服务器(0.3MB) --> <dependency> <groupId>org.noear</groupId> <artifactId>solon-server-jdkhttp</artifactId> </dependency> <!-- 替换后:使用 Jetty(2.7MB,支持 WebSocket) --> <dependency> <groupId>org.noear</groupId> <artifactId>solon-server-jetty</artifactId> </dependency>4.5 二级扩展插件
部分 Boot 插件有配套的二级插件,按需添加:
| Boot 插件 | 二级插件 | 说明 |
|---|---|---|
| solon-server-jetty | solon-server-jetty-add-jsp | 增加 JSP 视图 |
| solon-server-jetty | solon-server-jetty-add-websocket | 增加 WebSocket |
| solon-server-tomcat | solon-server-tomcat-add-jsp | 增加 JSP 视图 |
| solon-server-tomcat | solon-server-tomcat-add-websocket | 增加 WebSocket |
| solon-server-undertow | solon-server-undertow-add-jsp | 增加 JSP 视图 |
五、HTTP 模式详解
5.1 基础 Web 应用
以最轻量的solon-server-jdkhttp(0.3MB)为例:
<dependency> <groupId>org.noear</groupId> <artifactId>solon-server-jdkhttp</artifactId> </dependency> @SolonMain public class DemoApp { public static void main(String[] args) { Solon.start(DemoApp.class, args); } } @Controller public class DemoController { @Mapping("/hello") public String hello(@Param(defaultValue = "world") String name) { return "Hello " + name + "!"; } }5.2 SSL/HTTPS 配置
方式一:配置文件
server: ssl: keyStore: "/data/_ca/demo.jks" # 或 "demo.pfx" keyPassword: "demo"方式二:自定义 SSLContext(v2.5.9+)
不走配置文件,完全代码控制:
@SolonMain public class AppDemo { public static void main(String[] args) { Solon.start(AppDemo.class, args, app -> { SSLContext sslContext = buildSSLContext(); // 自行构建 app.onEvent(HttpServerConfigure.class, e -> { e.enableSsl(true, sslContext); }); }); } }5.3 额外 HTTP 端口
启用 HTTPS 后仍需保留 HTTP 端口的场景(v2.2.18+):
@SolonMain public class SeverDemo { public static void main(String[] args) { Solon.start(SeverDemo.class, args, app -> { app.onEvent(HttpServerConfigure.class, e -> { e.addHttpPort(8082); }); }); } }5.4 控制端口启停
// 关闭 HTTP 自动启动 @SolonMain public class SeverDemo { public static void main(String[] args) { Solon.start(SeverDemo.class, args, app -> { app.enableHttp(false); }); } }六、WebSocket 模式
6.1 启用 WebSocket
使用 Jetty 为例(需添加二级插件):
<dependency> <groupId>org.noear</groupId> <artifactId>solon-server-jetty</artifactId> </dependency> <dependency> <groupId>org.noear</groupId> <artifactId>solon-server-jetty-add-websocket</artifactId> </dependency> @SolonMain public class DemoApp { public static void main(String[] args) { Solon.start(DemoApp.class, args, app -> { app.enableWebSocket(true); // 启用 WebSocket }); } }6.2 WebSocket 端点
@ServerEndpoint("/ws/demo/{id}") public class WebSocketDemo extends SimpleWebSocketListener { @Override public void onMessage(WebSocket socket, String text) throws IOException { socket.send("我收到了:" + text); } @Override public void onOpen(WebSocket socket) { System.out.println("连接建立:" + socket.param("id")); } }七、Socket.D 模式
7.1 依赖配置
<dependency> <groupId>org.noear</groupId> <artifactId>solon-server-socketd</artifactId> </dependency> <!-- 按需选择传输协议包 --> <dependency> <groupId>org.noear</groupId> <artifactId>socketd-transport-netty</artifactId> </dependency>7.2 启用 Socket.D 服务
@SolonMain public class DemoApp { public static void main(String[] args) { Solon.start(DemoApp.class, args, app -> { app.enableSocketD(true); }); } }7.3 Socket.D 端点
@ServerEndpoint("/demo/{id}") public class SocketDDemo extends SimpleListener { @Override public void onMessage(Session session, Message message) throws IOException { session.send("test", new StringEntity("我收到了:" + message)); // session.param("id"); // 获取路径变量 } }7.4 三种协议架构的端口分配
| 协议 | 端口计算 | 示例(server.socket.port=28080) |
|---|---|---|
| sd:tcp | ${server.socket.port} | 28080 |
| sd:udp | ${server.socket.port} + 1 | 28081 |
| sd:ws | ${server.socket.port} + 2 | 28082 |
7.5 Socket.D 配置项全表
server: socket: name: "waterapi.tcp" # 信号名称 port: 28080 # 信号端口 host: "0.0.0.0" # 绑定主机 wrapPort: 28080 # 包装端口(Docker + 注册时用) wrapHost: "0.0.0.0" # 包装主机 coreThreads: 0 # 最小线程(0=自动) maxThreads: 0 # 最大线程(0=自动) idleTimeout: 0 # 闲置超时(0=自动,ms) ioBound: true # IO密集型八、Server 配置体系
8.1 四大配置系列
| 系列 | 说明 | 备注 |
|---|---|---|
| server.? | 主配置 | 供信号配置继承 |
| server.http.? | HTTP 信号配置 | — |
| server.socket.? | Socket 信号配置 | — |
| server.websocket.? | WebSocket 信号配置 | — |
8.2 配置继承关系
- 当没有"信号配置"时,使用"主配置"
- 例如:没有
server.http.ssl时,使用server.ssl
端口默认值:
| 信号 | 默认端口 | 说明 |
|---|---|---|
| http | server.port(默认 8080) | 主端口 |
| websocket | 主端口 + 15000 | 23080 |
| socket | 主端口 + 20000 | 28080 |
8.3 完整配置模板
solon: app: name: "demo" group: "demo" env: "dev" stop: safe: 1 delay: 10 threads: virtual: enabled: false server: port: 8080 host: "0.0.0.0" # SSL 主配置(所有信号共享) ssl: keyStore: "/data/_ca/demo.jks" keyPassword: "demo" # HTTP 信号配置 http: port: 8080 coreThreads: 0 maxThreads: 0 idleTimeout: 0 ioBound: true # WebSocket 信号配置 websocket: port: 23080 # Socket 信号配置 socket: name: "demo.tcp" port: 28080 host: "0.0.0.0" coreThreads: 0 maxThreads: 0 idleTimeout: 0 ioBound: true九、线程数配置
9.1 配置项
server: http: coreThreads: 0 # 最小线程(0=自动) maxThreads: 0 # 最大线程(0=自动) idleTimeout: 0 # 闲置超时(ms) ioBound: true # IO密集型 # 虚拟线程池(v2.7.3+) solon: threads: virtual: enabled: false9.2 自动计算规则
IO 密集型(ioBound: true,默认)
| 配置项 | 计算公式 | 2c4g 实例 |
|---|---|---|
| coreThreads | CPU 内核数 × 2 | 4 |
| maxThreads | coreThreads × 32 | 128 |
CPU 密集型(ioBound: false)
| 配置项 | 计算公式 | 2c4g 实例 |
|---|---|---|
| coreThreads | CPU 内核数 × 2 | 4 |
| maxThreads | coreThreads × 8 | 32 |
9.3 调优建议
coreThreads一般不需要配置(BIO 太大不收缩,NIO 不能太大)maxThreads一般默认即可;单实例且流量大或请求慢时适当调大- 线程数不是越多越好(切换需要时间),也不是越少越好(会不够用)
- Java 21+ 可考虑开启虚拟线程:
solon.threads.virtual.enabled: true
十、嵌入式启动
Solon 的所有 Server 插件均支持嵌入式启动,可直接通过 Server 实现类手动控制生命周期:
// JDK HTTP Server JdkHttpServer server = new JdkHttpServer(); server.start(null, 8080); // ... 使用中 server.stop(); // Jetty Server JettyServer server = new JettyServer(); server.start(null, 8080); // Undertow Server UndertowServer server = new UndertowServer(); server.start(null, 8080); // Vert.x Server VertxServer server = new VertxServer(); server.start(null, 8080);这种模式适合: - 将 Solon 嵌入到已有 Java 应用中 - 在单元测试中启动轻量级服务器 - 构建自定义的启动流程
十一、实战案例:三种服务同时运行
一个应用同时提供 HTTP API + WebSocket + Socket.D 三种服务:
11.1 Maven 依赖
<!-- HTTP + WebSocket (通过 Jetty) --> <dependency> <groupId>org.noear</groupId> <artifactId>solon-server-jetty</artifactId> </dependency> <dependency> <groupId>org.noear</groupId> <artifactId>solon-server-jetty-add-websocket</artifactId> </dependency> <!-- Socket.D --> <dependency> <groupId>org.noear</groupId> <artifactId>solon-server-socketd</artifactId> </dependency> <dependency> <groupId>org.noear</groupId> <artifactId>socketd-transport-netty</artifactId> </dependency> <!-- 其他基础依赖 --> <dependency> <groupId>org.noear</groupId> <artifactId>solon-web</artifactId> </dependency>11.2 配置文件
server: port: 8080 http: port: 8080 coreThreads: 4 maxThreads: 128 websocket: port: 8080 # 与 http 共用端口 socket: name: "demo.tcp" port: 28080 coreThreads: 2 maxThreads: 6411.3 启动类
@SolonMain public class App { public static void main(String[] args) { Solon.start(App.class, args, app -> { app.enableHttp(true); app.enableWebSocket(true); app.enableSocketD(true); }); } }11.4 HTTP 控制器
@Controller public class ApiGateway { @Inject OrderService orderService; @Mapping("/api/order/create") public OrderDTO createOrder(@Body OrderRequest req) { return orderService.create(req); } @Mapping("/api/order/{id}") public OrderDTO getOrder(int id) { return orderService.getById(id); } }11.5 WebSocket 端点
@ServerEndpoint("/ws/notify/{userId}") public class NotifyWebSocket extends SimpleWebSocketListener { @Override public void onOpen(WebSocket socket) { String userId = socket.param("userId"); SessionManager.register(userId, socket); } @Override public void onClose(WebSocket socket) { String userId = socket.param("userId"); SessionManager.unregister(userId); } @Override public void onMessage(WebSocket socket, String text) { // 处理客户端推送的消息 } }11.6 Socket.D 端点
@ServerEndpoint("/sd/rpc") public class RpcSocketD extends SimpleListener { @Override public void onMessage(Session session, Message message) throws IOException { if ("order.query".equals(message.topic())) { String orderId = message.bodyAsString(); // 处理 RPC 请求 session.reply(message, new StringEntity("{\"status\":\"ok\"}")); } } }十二、总结与选型建议
选型决策表
| 场景 | 推荐 Server 插件 | 理由 |
|---|---|---|
| 微服务 API、RESTful | solon-server-jdkhttp | 0.3MB 极简,够用 |
| 需要 WebSocket | solon-server-jetty 或 solon-server-smarthttp | 原生 WS 支持 |
| 高性能 HTTP/2 | solon-server-undertow 或 solon-server-vertx | HTTP/2 支持 |
| 传统 JSP 项目 | solon-server-tomcat + JSP 二级插件 | JSP 兼容 |
| RPC 长连接 | solon-server-socketd | Socket.D 协议 |
| 最小化部署 | solon-server-jdkhttp | 0.3MB 包大小 |
| 国产化/信创 | solon-server-smarthttp | 国产 HTTP 服务器 |
核心要点
- 业务代码与 Server 完全解耦— 只需换依赖,代码零修改
- 最小 0.3MB— jdkhttp 适合微服务和 Serverless
- 三种信号并存— HTTP + WebSocket + Socket.D 可以同一应用运行
- 配置继承— 信号配置缺省时自动使用主配置
- 线程数自适应— 根据 CPU 内核数和 IO 类型自动计算
- 嵌入式支持— 可以嵌入到已有 Java 应用中