30 行代码,从零构建你的第一个 ionet 服务器
前言
判断一个框架是否值得投入,最好的方式就是跑通一个完整 demo。不是看 PPT,不是听别人吹,而是亲手写代码、看它启动、发请求、拿到响应。
这篇文章将带你从零开始,用最少的代码搭建一个完整的 ionet 游戏服务器。你将完成以下事情:
- 定义一个业务数据协议
- 编写一个 Action(业务动作)
- 启动服务器
- 用模拟客户端发送请求
- 把服务器打成 jar 包独立运行
整个过程大约 10 分钟。让我们开始。
Step 1:创建项目并配置依赖
创建一个标准的 Maven 项目,在 pom.xml 中添加 ionet 依赖:
<properties>
<java.version>25</java.version>
<!-- 查看最新版本: https://central.sonatype.com/artifact/com.iohao.net/run-one -->
<ionet.version>xx.xx</ionet.version>
</properties>
<dependencies>
<dependency>
<groupId>com.iohao.net</groupId>
<artifactId>run-one</artifactId>
<version>${ionet.version}</version>
</dependency>
</dependencies>
就这一个依赖。没有 Spring、没有 Netty 的手动配置、没有中间件的 starter——一个 run-one 搞定一切。
Step 2:定义业务数据协议
在 ionet 中,前端和后端之间通过协议对象传递业务数据。定义一个非常简单的消息:
@ProtobufClass
public class HelloMessage {
public String name;
@Override
public String toString() {
return "HelloMessage{name='" + name + "'}";
}
}
@ProtobufClass 注解让这个普通 Java Bean 自动获得高效的二进制序列化能力。不需要手写 .proto 文件——虽然 ionet 也支持标准 Protobuf,但对于快速开发,用注解更直接。
Step 3:编写 Action
这一步是最关键的,也是 ionet 最"反直觉"的地方——因为它太简单了。
@ActionController(1)
public class DemoAction {
@ActionMethod(0)
private HelloMessage here(HelloMessage helloMessage) {
HelloMessage newHelloMessage = new HelloMessage();
newHelloMessage.name = helloMessage.name + ", I'm here";
return newHelloMessage;
}
@ActionMethod(1)
private HelloMessage jackson(HelloMessage helloMessage) {
// 断言:name 必须是 "Jackson",否则抛出错误码
ErrorCode.nameChecked.assertTrue("Jackson".equals(helloMessage.name));
helloMessage.name = "Hello, " + helloMessage.name;
return helloMessage;
}
@ActionMethod(2)
private List<HelloMessage> list() {
return IntStream.range(1, 5).mapToObj(id -> {
HelloMessage helloMessage = new HelloMessage();
helloMessage.name = "data:" + id;
return helloMessage;
}).toList();
}
}
让我们拆解一下:
@ActionController(1)定义了这个类的主路由为1@ActionMethod(0)定义了方法的子路由为0,完整路由就是1-0- 方法参数
HelloMessage helloMessage= 客户端发来的请求数据 return的值 = 自动发送给客户端的响应数据
这就是全部了。 没有 Channel、没有 ByteBuf、没有序列化框架的手动调用。和写一个 Spring Controller 的体验几乎一样。
关于异常处理
注意第二个方法 jackson 中的这行代码:
ErrorCode.nameChecked.assertTrue("Jackson".equals(helloMessage.name));
这是 ionet 的断言 + 异常机制。如果断言失败,框架会自动将错误码返回给客户端,你不需要写 try-catch,不需要封装 Result 对象。业务代码保持极致的清晰。
对应的错误码定义:
public enum ErrorCode implements ErrorInformation {
nameChecked(100, "The name must be Jackson");
final int code;
final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
@Override
public String getMessage() { return this.message; }
@Override
public int getCode() { return this.code; }
}
Step 4:启动服务器
public class DemoApplication {
static void main() {
// 开启源码文档解析,以显示代码行数
CoreGlobalConfig.setting.parseDoc = true;
CoreGlobalConfig.setting.print = true;
// 创建对外服,端口 10100
int port = ExternalGlobalConfig.externalPort;
var externalServer = ExternalMapper.builder(port).build();
var aeron = new AeronLifecycleManager().getAeron();
new RunOne()
.setAeron(aeron)
.enableCenterServer()
.setExternalServer(externalServer)
.setLogicServerList(List.of(new DemoLogicServer()))
.startup();
}
}
启动时需要添加 VM options:
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED
--enable-native-access=ALL-UNNAMED
启动后,控制台会输出所有已注册的 Action 信息,包括路由、所在类、方法名、参数类型、返回值,甚至代码行数。点击控制台中的 DemoAction.java:43,IDE 会直接跳转到对应代码——这个特性在大型项目中极其实用。
Step 5:模拟客户端请求
添加模拟客户端依赖:
<dependency>
<groupId>com.iohao.net</groupId>
<artifactId>extension-client</artifactId>
<version>${ionet.version}</version>
</dependency>
编写模拟客户端:
public class DemoClient {
static void main() {
new ClientRunOne()
.setInputCommandRegions(List.of(new DemoRegion()))
.startup();
}
static class DemoRegion extends AbstractInputCommandRegion {
@Override
public void initInputCommand() {
this.setCmd(DemoCmd.cmd, "TestDemo");
// 模拟请求 1-0
ofCommand(DemoCmd.here).setTitle("here").setRequestData(() -> {
HelloMessage msg = new HelloMessage();
msg.name = "1";
return msg;
}).callback(result -> {
HelloMessage value = result.getValue(HelloMessage.class);
log.info("{}", value);
});
// 模拟请求 1-1(会触发异常)
ofCommand(DemoCmd.jackson).setTitle("Jackson").setRequestData(() -> {
HelloMessage msg = new HelloMessage();
msg.name = "1"; // 不是 "Jackson",会触发错误码
return msg;
}).callback(result -> {
HelloMessage value = result.getValue(HelloMessage.class);
log.info("{}", value);
});
// 模拟请求 1-2
ofCommand(DemoCmd.list).setTitle("list").callback(result -> {
List<HelloMessage> list = result.listValue(HelloMessage.class);
log.info("{}", list);
});
}
}
}
启动客户端后,在控制台输入 . 可以列出所有可用的模拟命令。输入 1-0、1-1、1-2 即可触发对应请求。
你会看到:
1-0返回HelloMessage{name='1, I'm here'}1-1返回错误码 100(因为 name 不是 "Jackson")1-2返回一个包含 4 个元素的 List
这个模拟客户端可以反复执行命令,不需要每次重启。它模拟的是真实的网络环境——和你的前端同事联调时,再也不用喊"再点一下、再点一下"了。
Step 6:打包运行
mvn clean package
在 target/ 目录下会生成一个 jar 文件,大约 15MB。启动它:
java \
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED \
--enable-native-access=ALL-UNNAMED \
-jar target/ionet-quick-demo.jar
通常在 0.x 秒内完成启动。没有预热、没有"Application started in 8.3 seconds"——就是快。
回顾:我们做了什么?
| 步骤 | 做了什么 | 代码量 |
|---|---|---|
| 协议定义 | HelloMessage 类 |
5 行 |
| 业务逻辑 | DemoAction 类,3 个 Action |
20 行 |
| 启动器 | DemoApplication |
12 行 |
| 合计 | 一个完整的分布式服务器 | ~37 行 |
这 37 行代码写出来的服务器:
- 支持 TCP、WebSocket、UDP
- 具备分布式扩展能力
- 内置异常机制和错误码
- 有模拟客户端可以即时测试
- 打成 jar 包后只有 15MB
更多资源
- 🔗 完整示例源码(路径:
ionet-quick-demo) - 📖 快速从零编写服务器示例 — 官方文档详细版
- 📖 Action 详解
- 📖 断言 + 异常机制
下一篇预告:[一个 Java 方法就是一个 Action —— ionet 的零学习成本之道]
浙公网安备 33010602011771号