Skip to content

Quarkus(无 HTTP)整合 tio-boot(有 HTTP) ​

同 JVM 架构完整方案说明 ​


一、背景与问题动机 ​

在 Java 服务架构中,常见的两类诉求往往是冲突的:

  • 高性能、可控的网络层 希望完全掌控端口、协议、路由、线程模型(tio-boot 非常擅长)

  • 成熟、强大的后台能力 希望使用 CDI、配置、生命周期管理、生态组件(Quarkus 非常擅长)

传统做法往往是:

  • 要么只用 Quarkus(HTTP、容器、业务全绑在一起)
  • 要么只用网络框架,自己造容器轮子

本方案的目标是: 在同一个 JVM 内,让 tio-boot 和 Quarkus 各司其职,互不干扰。


二、总体设计目标 ​

1. tio-boot 的职责(唯一对外) ​

  • 唯一对外的 HTTP / 网络服务

  • 负责:

    • 端口监听
    • 路由
    • handler
    • 协议
  • 不关心业务容器、依赖注入

2. Quarkus 的职责(后台容器) ​

  • 不启动 HTTP

  • 只作为:

    • CDI 容器(Arc)
    • Bean 生命周期管理
  • 提供能力:

    • @ApplicationScoped
    • @Inject
  • 不感知 tio-boot 的存在

3. 业务代码的要求 ​

  • 业务只写在 Service 层
  • 不依赖 tio-boot
  • 不依赖 HTTP、Request、Response

三、整体架构关系图(逻辑) ​

HTTP 请求
   ↓
tio-boot handler
   ↓
Quarkus CDI Service
   ↓
业务逻辑 / 数据 / 外部资源

核心思想只有一句话:

tio-boot 负责“接请求”,Quarkus 负责“干活”


1. 依赖 ​

xml
  <properties>
    <compiler-plugin.version>3.12.1</compiler-plugin.version>
    <maven.compiler.release>21</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
    <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
    <quarkus.platform.version>3.8.6</quarkus.platform.version>
    <skipITs>true</skipITs>
    <surefire-plugin.version>3.2.5</surefire-plugin.version>

    <!-- 版本属性 -->
    <lombok-version>1.18.44</lombok-version>
    <fastjson2.version>2.0.52</fastjson2.version>

    <hotswap-classloader.version>1.2.9</hotswap-classloader.version>
    <tio-boot.version>2.1.8</tio-boot.version>
    <jfinal-aop.version>1.4.0</jfinal-aop.version>

  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>${quarkus.platform.artifact-id}</artifactId>
        <version>${quarkus.platform.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-arc</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <scope>test</scope>
    </dependency>

    <!-- Tio Boot 框架 -->
    <dependency>
      <groupId>nexus.io</groupId>
      <artifactId>tio-boot</artifactId>
      <version>${tio-boot.version}</version>
    </dependency>

    <!-- 热加载类加载器 -->
    <dependency>
      <groupId>nexus.io</groupId>
      <artifactId>hotswap-classloader</artifactId>
      <version>${hotswap-classloader.version}</version>
    </dependency>

    <!-- FastJSON2 用于 JSON 解析 -->
    <dependency>
      <groupId>com.alibaba.fastjson2</groupId>
      <artifactId>fastjson2</artifactId>
      <version>${fastjson2.version}</version>
    </dependency>

    <!-- Lombok 用于简化代码 -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok-version}</version>
      <optional>true</optional>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>quarkus-maven-plugin</artifactId>
        <version>${quarkus.platform.version}</version>
        <extensions>true</extensions>
        <executions>
          <execution>
            <goals>
              <goal>build</goal>
              <goal>generate-code</goal>
              <goal>generate-code-tests</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${compiler-plugin.version}</version>
        <configuration>
          <compilerArgs>
            <arg>-parameters</arg>
          </compilerArgs>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${surefire-plugin.version}</version>
        <configuration>
          <systemPropertyVariables>
            <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
            <maven.home>${maven.home}</maven.home>
          </systemPropertyVariables>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>${surefire-plugin.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>integration-test</goal>
              <goal>verify</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <systemPropertyVariables>
            <native.image.path>
              ${project.build.directory}/${project.build.finalName}-runner</native.image.path>
            <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
            <maven.home>${maven.home}</maven.home>
          </systemPropertyVariables>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <profiles>
    <profile>
      <id>native</id>
      <activation>
        <property>
          <name>native</name>
        </property>
      </activation>
      <properties>
        <skipITs>false</skipITs>
        <quarkus.package.type>native</quarkus.package.type>
      </properties>
    </profile>
  </profiles>

四、关键技术点说明 ​

1. 为什么需要 QuarkusBeans 工具类 ​

tio-boot 的 handler 不是 Quarkus 管理的对象, 因此不能直接 @Inject。

解决方式是: 在 handler 中,通过 Arc 容器“按需获取” Bean。

java
package nexus.io.quarkus.config;

import io.quarkus.arc.Arc;

public final class QuarkusBeans {
  private QuarkusBeans() {
  }

  public static <T> T get(Class<T> type) {
    return Arc.container().instance(type).get();
  }
}

这个类的角色非常明确:

  • 是 tio 世界 → quarkus 世界的“桥”
  • 不引入 HTTP、线程、协议耦合
  • 只做一件事:从 CDI 容器拿 Bean

五、业务层(完全不感知 tio-boot) ​

MyService:标准 Quarkus Service ​

java
package nexus.io.quarkus.service;

import jakarta.enterprise.context.ApplicationScoped;

import java.util.HashMap;
import java.util.Map;

@ApplicationScoped
public class MyService {

  public Map<String, Object> work() {
    Map<String, Object> data = new HashMap<>();
    data.put("from", "quarkus");
    data.put("status", "ok");
    data.put("time", System.currentTimeMillis());
    return data;
  }
}

设计要点:

  • 使用 @ApplicationScoped
  • 无 HTTP 相关代码
  • 无 tio 相关依赖
  • 可被测试、可被替换、可迁移

六、Handler 层(tio-boot 世界) ​

HelloHandler:HTTP 与业务的“边界层” ​

java
package nexus.io.quarkus.handler;

import nexus.io.quarkus.service.MyService;
import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpRequest;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.tio.http.server.handler.HttpRequestHandler;

import io.quarkus.arc.Unremovable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped
@Unremovable
public class HelloHandler implements HttpRequestHandler {

  @Inject
  MyService myService;

  @Override
  public HttpResponse handle(HttpRequest httpRequest) throws Exception {
    return TioRequestContext.getResponse().setJson(myService.work());
  }
}

关键说明(非常重要) ​

1. 为什么是 CDI Bean ​

  • 让 Quarkus 管理生命周期
  • 可以 @Inject MyService

2. 为什么必须加 @Unremovable ​

  • Quarkus 会在构建期移除“看起来没被用到的 Bean”
  • handler 是 被 tio-boot 在运行期使用的
  • CDI 无法静态分析到这一点
  • 所以必须明确告诉 Quarkus:这个 Bean 不能删

七、tio-boot 路由注册(桥接点) ​

WebHelloConfig:tio-boot 的配置入口 ​

java
package nexus.io.quarkus.config;

import nexus.io.context.BootConfiguration;
import nexus.io.quarkus.handler.HelloHandler;
import nexus.io.tio.boot.server.TioBootServer;
import nexus.io.tio.http.server.router.HttpRequestRouter;

public class WebHelloConfig implements BootConfiguration {

  public void config() {

    TioBootServer server = TioBootServer.me();
    HttpRequestRouter requestRouter = server.getRequestRouter();

    HelloHandler helloHandler = QuarkusBeans.get(HelloHandler.class);
    requestRouter.add("/hello", helloHandler);
  }
}

这里是整个架构最关键的连接点:

  • tio-boot 在这里注册路由
  • handler 实例来自 Quarkus CDI
  • tio-boot 完全不知道 CDI 的存在

八、生命周期整合(Quarkus 启动 tio-boot) ​

TioBootLifecycle ​

java
package nexus.io.quarkus.config;

import nexus.io.context.Context;
import nexus.io.tio.boot.TioApplication;

import io.quarkus.logging.Log;
import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;

@ApplicationScoped
public class TioBootLifecycle {
  private Context context;

  void onStart(@Observes StartupEvent ev) {
    long start = System.currentTimeMillis();
    context = TioApplication.run(new WebHelloConfig());
    long end = System.currentTimeMillis();
    System.out.println((end - start) + "ms");
  }

  void onStop(@Observes ShutdownEvent ev) {
    if (context != null) {
      Log.info("close");
      context.close();
    }
  }
}

作用说明:

  • Quarkus 启动完成 → 启动 tio-boot
  • Quarkus 关闭 → 关闭 tio-boot
  • 两者 同 JVM、同生命周期

九、程序入口 ​

java
package nexus.io.quarkus;

import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain
public class MyApplication {

  public static void main(String... args) {
    Quarkus.run(args);
  }
}
  • 入口仍然是 Quarkus
  • 但 HTTP 完全由 tio-boot 提供

十、运行效果 ​

访问:

http://127.0.0.1/hello

返回:

json
{
  "from": "quarkus",
  "time": "1770044478509",
  "status": "ok"
}

十一、总结(核心价值) ​

这套方案实现了:

  1. 职责彻底分离

    • tio-boot:网络
    • Quarkus:容器
  2. 零侵入业务代码

    • 业务只依赖 CDI
  3. 同 JVM、无 RPC、无序列化开销

  4. 可演进

    • 后续可接数据库、MQ、缓存
    • handler 数量增加不影响架构

代码

MIT Licensed