☰
使用 @midwayjs/one-shot 在 Midway 中执行一次性脚本任务
2026/10/9 12:45:41 网站建设 项目流程
  • 后端
  • 微服务
  • 云原生

【免费下载链接】midway

🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈

项目地址:https://gitcode.com/gh_mirrors/mi/midway
点击查看免费下载

本文围绕 Midway 官方扩展@midwayjs/one-shot展开,介绍如何在一个普通 Node.js 项目中以 IoC(依赖注入)方式编排一次性脚本(one-shot task),包括组件安装、入口配置、生命周期内执行、基于 request-scope 的runScript运行方式以及内置日志器的定制。读完本文,你将掌握一个不启动 HTTP/Serverless 服务、只跑一次业务逻辑、却能完整复用 Midway 依赖注入与中间件链路的轻量框架用法。

什么是 @midwayjs/one-shot

@midwayjs/one-shot是一个"只提供 Framework"的一次性脚本框架。它不包含独立的 HTTP 服务、不面向 Serverless 平台,也不参与应用集成场景,定位非常纯粹:在一个已有项目中,借助 Midway 的 IoC 容器执行一次性的任务。

官方文档给出了该组件的能力矩阵:

描述是否支持
可用于标准 Web 应用❌
可用于 Serverless 场景❌
可用于集成场景(integrated)❌
包含独立的核心(standalone core)✅
包含独立的日志器(standalone logger)✅

换句话说,它把 Midway 的"核心 + 日志"能力以独立框架的形式拆出来,供批处理、数据同步、定时补偿等场景复用,而不会引入多余的网络层。从仓库结构看,该组件位于 packages/one-shot,当前版本为4.2.5,依赖仅@midwayjs/core(见 package.json),运行时要求 Node.js >= 20。

安装组件

在已有项目中安装 one-shot 组件依赖:

$ npm i @midwayjs/one-shot@4 --save

也可以在package.json中直接声明依赖后重新安装:

{ "dependencies": { "@midwayjs/one-shot": "^4.0.0" } }

由于@midwayjs/one-shot在src/index.ts中通过export { MidwayOneShotFramework as Framework }与export { OneShotConfiguration as Configuration }对外暴露框架与配置入口(见 src/index.ts),安装后只需在配置类中导入即可使用。

启用组件

在入口配置src/configuration.ts中导入并注册组件:

// src/configuration.ts import { Configuration } from '@midwayjs/core'; import * as oneShot from '@midwayjs/one-shot'; @Configuration({ imports: [oneShot], }) export class MainConfiguration {}

组件内部通过@Configuration({ namespace: 'oneShot', importConfigs: [...] })注册了名为oneShot的命名空间,并顺带声明了默认日志配置(见 src/configuration.ts),因此无需任何额外初始化动作即可工作。

在生命周期中执行一次性逻辑

@midwayjs/one-shot不需要额外的 Runner 概念,官方推荐的执行时机是应用生命周期钩子onServerReady。此时应用已完成装配、所有 IoC 依赖均可注入,可以直接调用业务服务:

// src/configuration.ts import { Configuration, Inject } from '@midwayjs/core'; import * as oneShot from '@midwayjs/one-shot'; import { ScriptService } from './service/script'; @Configuration({ imports: [oneShot], }) export class MainConfiguration { @Inject() scriptService: ScriptService; async onServerReady() { await this.scriptService.runOnce(); } }

这里ScriptService是项目内普通@Provide()业务类,依赖注入完全走 Midway 容器。因为 one-shot 框架的run()方法是按需执行的(源码中为空的public async run(): Promise<void>,见 src/framework.ts),所以整体启动流程很短,适合"进程启动 → 跑一次 → 退出"的命令行任务形态。

当脚本需要请求级作用域(request scope)时

如果你的脚本依赖请求级(request-scope)服务,生命周期钩子里直接注入的方式就不够用了——生命周期钩子运行在应用级作用域,无法天然获得请求级上下文。此时应使用框架提供的runScriptAPI 并搭配一个固定的 Runner 类:

框架会为这一次执行创建一个独立的 context,并运行完整的中间件(middleware)与过滤器(filter)链路。

定义 Runner 类

Runner 需要实现OneShotRunner<T, R>接口,其中泛型T为入参 payload 类型、R为返回值类型:

// src/script/syncUser.ts import { Provide } from '@midwayjs/core'; import { OneShotRunner, Context } from '@midwayjs/one-shot'; @Provide() export class SyncUserScript implements OneShotRunner<{ id: number }, void> { async run(payload?: { id: number }, ctx?: Context) { // use payload / ctx void payload; void ctx; } }

接口定义位于 src/interface.ts:

export interface OneShotRunner<T = unknown, R = unknown> { run(payload?: T, ctx?: IMidwayOneShotContext): R | Promise<R>; }

其中上下文IMidwayOneShotContext继承自 Midway 通用上下文,并额外带有一个payload字段(见 src/interface.ts),因此你既可以通过run的第二个参数拿到ctx,也可以在 Runner 内部用@Inject() ctx: Context注入上下文并读取ctx.payload。

调用 runScript

在配置类生命周期中通过注入的Framework调用:

// src/configuration.ts import { Configuration, Inject } from '@midwayjs/core'; import * as oneShot from '@midwayjs/one-shot'; import { Framework } from '@midwayjs/one-shot'; import { SyncUserScript } from './script/syncUser'; @Configuration({ imports: [oneShot], }) export class MainConfiguration { @Inject() framework: Framework; async onServerReady() { await this.framework.runScript(SyncUserScript, { id: 42 }); } }

runScript的完整签名支持三个参数(见 src/framework.ts):

public async runScript<T = unknown, R = unknown>( Runner: new (...args: unknown[]) => OneShotRunner<T, R>, payload?: T, ctxData: Partial<IMidwayOneShotContext> = {} ): Promise<R>

底层执行链路(源码解读)

runScript的内部实现(src/framework.ts)清晰地展现了它的设计意图:

  1. 创建匿名上下文:调用this.app.createAnonymousContext({ ...ctxData, payload })生成一次执行专属的上下文,payload被直接挂到上下文中。createAnonymousContext是BaseFramework提供的能力(见 packages/core/src/baseFramework.ts),它会为上下文补齐startTime、上下文日志器、requestContext(请求级容器)以及traceId等属性——这正是 one-shot 能够运行请求级依赖和中间件的根本原因。
  2. 链路追踪:通过MidwayTraceService.runWithEntrySpan创建名为oneshot ${Runner.name}的入口 span,并写入midway.protocol: 'one-shot'、midway.oneshot.runner等属性。tracing配置项可通过oneShot.tracing.enable关闭,并支持自定义extractor/meta回调来提取链路载体。
  3. 执行中间件链:用applyMiddleware包裹真实业务逻辑,然后在匿名上下文上运行整条中间件链。真正执行业务时,先从ctx.requestContext(请求级容器)中getAsync(Runner)解析出 Runner 实例——这一步保证了 Runner 可以注入 request-scope 服务——随后校验实例上存在run()方法,不存在则抛出MidwayCommonError('One-shot runner must implement run().'),最后调用instance.run(payload, ctx)并把结果作为R返回。

仓库自带的测试 test/index.test.ts 验证了这套链路:SampleScript通过@Inject() ctx: Context注入上下文,runScript(SampleScript, { id: 42 })返回'42:42'(payload 与 ctx.payload 一致);第二个用例还通过监听MidwayTraceService.runWithEntrySpan断言每次runScript都会创建入口 span。

日志:内置 oneShotLogger

组件默认注册一个名为oneShotLogger的日志器,写入文件midway-one-shot.log。默认配置在组件的importConfigs中声明(见 src/configuration.ts)。

你可以在脚本服务中通过@Logger('oneShotLogger')注入并使用:

import { Logger, ILogger } from '@midwayjs/core'; export class ScriptService { @Logger('oneShotLogger') logger: ILogger; async runOnce() { this.logger.info('run one-shot task'); } }

如果想自定义日志文件名或日志级别,可以在应用配置中覆盖midwayLogger.clients.oneShotLogger:

// src/config/config.default.ts export default { midwayLogger: { clients: { oneShotLogger: { fileLogName: 'my-one-shot.log', level: 'info', }, }, }, };

由于默认配置与用户配置都会合并进midwayLogger.clients,因此只需覆写需要变更的字段(如fileLogName、level)即可,其余日志器行为仍沿用 Midway 统一的日志配置体系。

小结与适用场景

@midwayjs/one-shot适合以下场景:

  • 需要跑一次的数据迁移、数据同步、批量补偿任务;
  • 希望复用项目既有 IoC 服务与配置,又不想引入完整 Web 或 Serverless 框架;
  • 脚本逻辑依赖 request-scope 服务或需要经过中间件/过滤器链路时,使用runScript+ Runner 类。

需要注意的前提:它不适用于标准 Web 应用、Serverless 或集成模式(见上文能力矩阵),核心定位就是"跑一次即退出的 IoC 化脚本"。实际使用中只需安装组件、在configuration.ts注册,并在onServerReady中按需触发即可,源码层面的运行链路可继续参考 packages/one-shot/src/framework.ts 与 packages/one-shot/test/index.test.ts。

  • 后端
  • 微服务
  • 云原生

【免费下载链接】midway

🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈

项目地址:https://gitcode.com/gh_mirrors/mi/midway
点击查看免费下载

相关推荐

上一篇:SciPy 文档体系中的 Sphinx Autosummary 属性模板:attribute.rst 逐行解析与生成链路
下一篇:Pandoc 脚注内引文(Citation in Note)机制深度解析:以 Chicago Full Note 风格回归测试 7394 为例

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询