Skip to content

宿主总览

通用主机统一应用启动/停止、配置、日志和 DI;Web 主机在通用主机之上扩展 HTTP 服务器和中间件管道。两者共享同一套底座,不是两套独立体系。

适用场景

  • 需要理解 Spire 应用的启动流程、生命周期和优雅关闭机制
  • 需要编写后台服务(IHostedService / BackgroundService
  • 需要理解通用主机和 Web 主机的差异与继承关系
  • 需要理解中间件管道的洋葱模型和四种中间件注册形式

核心问题

宿主层解决的是"应用怎么跑起来"的问题——不是业务逻辑怎么写,而是启动顺序、生命周期管理、配置加载、DI 组装这些所有程序共享的基础流程。核心问题是:

  1. 通用主机和 Web 主机的关系 — Web 主机继承通用主机的全部能力,再加 HTTP 层,一个字母都不少
  2. 中间件管道的工作原理 — 洋葱模型、next() 调用、四种中间件注册形式的适用场景

能力全景


两个主机,一个底座

Spire 提供两层主机抽象。它们不是两套独立的东西——Web 主机继承通用主机的全部能力,再加 HTTP 层。

主机一句话概括入口适用场景
通用主机统一启动/停止、配置、日志、DI 和生命周期管理Host.createBuilder(args)控制台工具、后台服务、定时任务——任何非 Web 程序
Web 主机在通用主机之上扩展 HTTP 服务器和中间件管道WebHost.createBuilder(args)Web 应用、API 服务——需要处理 HTTP 请求的程序

关键事实WebHost.createBuilder() 内部与 Host.createBuilder() 执行完全相同的初始化流程(环境识别、配置加载、日志注册),再额外组装 HTTP 层。通用主机的能力全盘继承,一个字母都不少。


通用主机:所有程序的统一底座

没有通用主机时,每个程序都要手搭配置、日志、DI,启动顺序靠程序员自觉,关闭流程没人管。通用主机把这些重复劳动收进三步。

三步跑起一个程序

cangjie
import soulsoft_extensions_hosting.*

main(args: Array<String>): Int64 {
    let builder = Host.createBuilder(args)     // ① 创建构建器:自动加载环境、配置、日志、DI
    builder.services.addHostedService<MyWorker>()  // ② 注册你的服务
    let host = builder.build()                      // ③ 构建:固化容器
    host.run()                                      // ④ 启动 + 阻塞等待关闭信号
    return 0
}

启动流程

host.run() 内部走的是严格顺序。理解这个顺序,你就理解了优雅关闭、生命周期回调在什么时候触发。

text
start()
  ├── 1. 打印环境信息
  ├── 2. 执行启动校验器(IStartupValidator)
  ├── 3. 注册信号处理器(Linux/macOS: SIGINT/SIGTERM)
  ├── 4. 启动所有 IHostedService
  └── 5. 触发 onStarted 回调

[外部信号: Ctrl+C / SIGTERM]
  └── 6. 触发 onStopping 回调
      └── 7. stopApplication() 解除阻塞

waitForShutdown()
  ├── 8. 阻塞直到 stopApplication() 被调用

stop()
  ├── 9. 停止所有 IHostedService
  └── 10. 触发 onStopped 回调

close()
  └── 11. 释放 DI 容器

托管服务:主机负责生命周期,你负责业务

托管服务(IHostedService / BackgroundService)是通用主机最核心的扩展点。主机在启动时调用 start(),在关闭时调用 stop()——你的业务逻辑只需实现这两个方法。

cangjie
import soulsoft_extensions_hosting.*
import soulsoft_extensions_logging.*

class DataSyncWorker <: BackgroundService {
    private let logger: ILogger

    public init(factory: ILoggerFactory) {
        logger = factory.createLogger<DataSyncWorker>()
    }

    public func run(): Unit {
        while (!Thread.currentThread.hasPendingCancellation) {
            logger.info("同步数据...")
            sleep(Duration.second * 30)
        }
        logger.info("数据同步已停止")
    }
}

main(args: Array<String>): Int64 {
    let builder = Host.createBuilder(args)
    builder.services.addHostedService<DataSyncWorker>()
    let host = builder.build()
    host.run()
    return 0
}

要点:addHostedService<T>() 注册为 Singleton,不能直接注入 Scoped 服务。需要 Scoped 时注入 IServiceScopeFactory 手动创建作用域。

完整用法见 通用主机


Web 主机:在通用主机之上加 HTTP

Web 主机继承通用主机的全部初始化流程,然后额外做两件事:启动 HTTP 服务器、组装中间件管道。

三步跑起一个 Web 应用

cangjie
import soulsoft_web_http.*
import soulsoft_web_hosting.*

main(args: Array<String>): Int64 {
    let builder = WebHost.createBuilder(args)      // ① 同 Host.createBuilder + 组装 HTTP 层
    let app = builder.build()                      // ② 固化
    app.use { context, next =>                     // ③ 添加中间件
        context.response.write("Hello, World!")
        next(context)
    }
    app.run()                                      // ④ 启动 HTTP 服务器 + 阻塞
    return 0
}

中间件管道:洋葱模型

每个 HTTP 请求进入一条管道,逐个穿过中间件。每个中间件在 next() 之前是"请求阶段"(读入水侧),之后是"响应阶段"(读出水侧)。

text
        请求(入水口)                      响应(出水口)
           │                                    ▲
           ▼                                    │
     ╔═════╧══════╗                      ╔═════╤══════╗
     ║  日志段    ║                      ║  日志段    ║
     ║  记录方法、 ╞═════ next() ════════╡  记录状态码 ║
     ║  路径      ║                      ║            ║
     ╚═════╤══════╝                      ╚═════╤══════╝
           │                                    ▲
           │  next()                            │
           ▼                                    │
     ╔═════╧══════╗                      ╔═════╤══════╗
     ║  认证段    ║                      ║  认证段    ║
     ║  Token有效 ╞═════ next() ════════╡  (无操作) ║
     ║  → 放行    ║                      ║            ║
     ║  Token无效 ║                      ║            ║
     ║  → 关阀门  ║                      ║            ║
     ╚═════╤══════╝                      ╚═════╤══════╝
           │                                    ▲
           │       ╔═══════════════╗            │
           └═══════╣  U 型弯(终端)╠════════════┘
                   ╚═══════════════╝

三条规则:

  1. 每个中间件决定是否调用 next()——调用则继续,不调用则短路
  2. next() 之前是请求阶段,之后是响应阶段——同一个中间件可以同时在两个阶段做事
  3. 响应沿调用栈逆序返回——中间件 1 → 2 → 3 进入,3 → 2 → 1 返回

中间件的四种形式

cangjie
// 形式一:(RequestDelegate) -> RequestDelegate — 初始化与请求处理分离
app.use {
    next => {                          // ① build() 时执行一次(初始化)
        let logger = resolveLogger()
        return { context =>            // ② 每个请求执行(处理逻辑)
            logger.info("${context.request.path}")
            next(context)
        }
    }
}

// 形式二:(HttpContext, RequestDelegate) -> Unit — 最简形式
app.use { context, next =>
    next(context)
}

// 形式三:IMiddleware 实例 — 可复用中间件
app.use(AuthMiddleware())

// 形式四:泛型 — DI 容器自动解析构造函数依赖
app.use<AuthMiddleware>([])

完整用法见 Web 主机


能力矩阵与组合效应

能力所属主机一句话概括核心类型
环境识别通用自动读取环境名,切换配置和日志级别IHostEnvironment
配置加载通用JSON → 环境变量 → 命令行,后加载覆盖先加载IConfiguration (自动注册)
日志系统通用控制台输出 + 配置文件控制过滤规则ILoggerFactory (自动注册)
DI 容器通用统一注册出入口,构造器自动注入ServiceCollectionIServiceProvider
生命周期通用onStarted / onStopping / onStopped 三大回调IHostApplicationLifetime
托管服务通用主机管生命周期,你写业务逻辑IHostedService / BackgroundService
优雅关闭通用Ctrl+C → 停止托管服务 → 释放容器,不漏资源ConsoleLifetime
启动校验通用validateOnBuild / validateScopes 提前暴露 DI 配置错误非生产环境自动开启
HTTP 服务器Web监听端口,接收请求,驱动管道IServer (自动注册)
中间件管道Web洋葱模型串联请求处理,use() 注册RequestDelegate
HTTP 上下文Web请求/响应封装,参数解析HttpContext
静态文件根路径Webwwwroot/ 目录IWebHostEnvironment.webRootPath
服务器配置Web超时、请求体大小、TLS 等底层参数configureServer()

组合效应

组合涉及能力组合价值示例
后台服务 + DI + 配置托管服务 · DI · 配置托管服务通过构造器注入 IOptions<T>,参数从 JSON 读取addHostedService<OrderCleanupWorker>()
Web 应用 + 生命周期Web 主机 · 通用主机生命周期启动后 onStarted 发就绪信号给 K8s,收到 SIGTERM 后 onStopping 排空请求再退出host.lifetime.onStarted { signalReady() }
中间件 + DI中间件管道 · DI 容器use<TMiddleware>() 自动从 DI 解析中间件依赖app.use<ExceptionHandlerMiddleware>()
全栈部署Web 主机 · 中间件管道 · 静态文件前端构建到 wwwroot/,后端一个进程同时托管 API 和 SPAuseFileServer() + mapControllers()

无论你写的是后台服务还是 Web 应用,都是同一条公式:

text
1. createBuilder(args)    → 自动加载环境、配置、日志、DI 容器
2. services.addXxx()      → 注册你的服务
3. build()                → 固化容器
4. (Web 特有)useXxx()   → 挂载中间件
5. run()                  → 启动 + 阻塞等待关闭信号

通用主机用步骤 1→2→3→5,Web 主机在步骤 3 和 5 之间多了步骤 4(挂载中间件)。除此之外,完全相同。


推荐阅读顺序

  1. 通用主机 — 从三步跑起一个程序开始,理解环境、配置、日志、生命周期和托管服务
  2. Web 主机 — 在通用主机基础上理解 HTTP 服务器和中间件管道

当前边界

  • 已覆盖:通用主机的完整生命周期、托管服务模型、环境识别、Web 主机的 HTTP 服务器和中间件管道、四种中间件注册形式
  • 未覆盖:第三方日志 Provider 实现、自定义配置源、分布式托管服务编排

相关专题

  • 通用主机 — 完整的环境、配置、日志、生命周期、托管服务文档
  • Web 主机 — 中间件管道、HTTP 上下文、服务器配置
  • Web 总览 — MVC、认证、CORS 等 Web 层能力
  • 基础设施总览 — DI、配置、日志等底层能力