ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

iii 存量系统渐进式迁移指南:不重写架构,把现有 HTTP 服务分片接入 iii 实时编排引擎

iii 存量系统渐进式迁移指南:不重写架构,把现有 HTTP 服务分片接入 iii 实时编排引擎 iii 存量系统渐进式迁移指南不重写架构把现有 HTTP 服务分片接入 iii 实时编排引擎【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii本篇技术指南围绕官方教程《OverviewIncremental Adoption》展开讲解如何在不做大爆炸式big-bang重写的前提下把一个已上线运行、通过 HTTP 对外提供服务的存量系统以“切片slice”为单位逐步迁移到 iii。读完本文你将掌握三套可独立落地、可随时回滚的迁移手段用 HTTP-invokable Function 为存量 API 增加 iii 形态的入口、用 iii-queue 把慢调用卸载到带重试的队列、用 iii-state 状态原语逐片接管持久化——最终让系统在无需整体切换full cutover的情况下完整跑在 iii 上。一、核心思路按切片迁移而不是整体重写原教程docs/0-17-0/tutorials/incremental-adoption/overview.mdx的核心主张非常明确把 iii 引入现有系统是一个增量过程整个迁移被拆成三个相互独立、各自可逆的切片slice把存量 HTTP 服务包装成一个 iii Function让系统内其他 Worker 可以通过function_id寻址它——此时不迁移任何流量只是增加一个 iii 形态的入口点把慢任务长耗时调用挪到队列 Worker 后面让调用方立即返回、重试交给队列处理把持久化状态按片迁移进 iii 的状态原语state primitives每次只搬一个切片其余系统保持原样直到准备好再搬下一片。按顺序完成这三个子教程系统就一片一片地“长”到了 iii 上。由于每个切片都独立可逆你可以在任意边界暂停或回滚而不会拖垮系统的其余部分。从源码层面看这种“按片叠加”的设计与 iii 的架构直接对应在 engine/src/main.rs 中iii命令本身就是一个分层入口——引擎启动时按config.yaml里的workers:列表逐个拉起 Worker 安装迁移过程中新增的任何 Worker 都只是往这个列表里追加一项原有进程和代码完全不需要改动。这一点在 docs/0-17-0/using-iii/engine.mdx 中表述为“引擎只是一个路由器”它接收请求、路由给 Worker、再把响应路由回来本身不持有业务逻辑因此天然支持渐进式接入。二、前置条件引擎、可寻址的存量服务与 SDK原文档列出的前置条件有三项下面结合仓库资料逐条落实1. 一个运行中的 iii 引擎安装引擎docs/0-17-0/install.mdxcurl -fsSL https://install.iii.dev/iii/main/install.sh | sh iii --version # 应输出版本号启动一个“一次性/练手scratch”实例的推荐做法是进入一个空目录直接运行iii让它自动生成config.yaml然后按需用iii worker add添加迁移所需的 Worker。这与 docs/0-17-0/using-iii/engine.mdx 中“默认配置Default configuration”一节描述的行为一致引擎从项目根目录的config.yaml启动可用--config path指向其他文件。版本注意以当前仓库源码为准教程的 skill 渲染版提到iii --use-default-config这个 flag。但从当前仓库源码看该 flag 已被移除——engine/src/main.rs#L521-L530 的测试用例use_default_config_is_no_longer_a_flag明确断言--use-default-config不再被解析因为iii现在会在config.yaml缺失时自动创建它该测试注释还说明了移除动机旧 flag 只会绕过config.yaml的初始化流程包括 worker add 与 reload watcher。因此在最新源码上直接运行iii即可获得默认配置当需要自定义端口、适配器或 Worker 集合时再切换到显式的config.yaml。2. 一个可通过 HTTP 到达的存量服务这是你要迁移的对象。它不需要任何改造只需要在当前运行环境中可以被 HTTP 访问到——iii 会在稍后的“包装”步骤中以引擎侧发起 HTTP 调用的方式与它通信。3. 与你服务语言匹配的 SDKiii 官方 SDK 覆盖 TypeScript/Node、Python、Rust 三种语言仓库中的对应实现分别位于 sdk/packages/node、sdk/packages/python、sdk/packages/rust。SDK 通过 WebSocket 与引擎建立连接registerWorker(url)/register_worker(...)连接建立后 Worker 向引擎声明自己可执行的 Functions 与要注册的 Triggers。关于 Worker 与引擎的连接方式与四要素心智模型Worker、Function、Trigger、Engine可进一步阅读 docs/0-17-0/understanding-iii/index.mdx。三、三步迁移路线图总览原教程给出了清晰的步骤导航三个子教程在仓库中的位置为步骤子教程仓库路径1Wrap an existing APIdocs/0-17-0/tutorials/incremental-adoption/wrap-existing-api.mdx2Offload work to a queuedocs/0-17-0/tutorials/incremental-adoption/offload-to-queue.mdx3Migrate persistencedocs/0-17-0/tutorials/incremental-adoption/migrate-persistence.mdx需要说明这三个子教程页面当前在仓库中仍是占位状态frontmatter 中description为 “Placeholder.”但其对应的三项能力——HTTP-invokable Function、队列 TriggerAction、iii-state 状态原语——已分别由 docs/0-17-0/creating-workers/functions.mdx、docs/0-17-0/using-iii/triggers.mdx 和 docs/0-17-0/quickstart.mdx 完整覆盖。下文将基于这些真实文档与仓库配置把三步走的具体做法做实。四、第一步包装存量 API——增加一个 iii 形态的入口4.1 这一步在做什么“包装”的含义是不搬流量先给存量 HTTP 服务加上一个可由function_id寻址的 iii Function 入口。iii 支持把外部 HTTP 端点直接注册为函数引擎在该函数被调用时发出 HTTP 请求你的 Worker 只需声明端点本身无需在 Worker 进程内实现业务逻辑。官方文档 docs/0-17-0/creating-workers/functions.mdx 明确说明这适合把现有 API Gateway、webhook、Serverless 平台Lambda、Azure Functions、Google Cloud Functions或任何第三方 API 以普通 iii Function 的形式暴露出来。4.2 注册一个 HTTP-invokable Function普通函数注册需要id与 handlerHTTP 可调用函数需要id与HttpInvocationConfig。以下为三种 SDK 的注册示例摘自 docs/0-17-0/creating-workers/functions.mdx// TypeScript / Node import { registerWorker } from iii-sdk; const url process.env.III_URL; if (!url) throw new Error(III_URL must be set); const worker registerWorker(url); worker.registerFunction( notifications::send, // function_id形如 service::name { url: https://hooks.provider.example.com/notify, method: POST, timeout_ms: 5000, headers: { X-Service: iii-worker }, auth: { type: bearer, token_key: PROVIDER_API_TOKEN }, }, { description: POST a notification to the provider webhook }, );# Python import os from iii import HttpInvocationConfig, InitOptions, register_worker from iii.iii_types import HttpAuthBearer worker register_worker( os.environ.get(III_URL), InitOptions(worker_namenotifications-worker), ) worker.register_function( notifications::send, HttpInvocationConfig( urlhttps://hooks.provider.example.com/notify, methodPOST, timeout_ms5000, headers{X-Service: iii-worker}, authHttpAuthBearer(token_keyPROVIDER_API_TOKEN), ), descriptionPOST a notification to the provider webhook, )// Rust use iii_sdk::{ HttpAuthConfig, HttpInvocationConfig, HttpMethod, InitOptions, RegisterFunctionMessage, register_worker, }; let url std::env::var(III_URL).expect(III_URL must be set); let worker register_worker(url, InitOptions::default()); worker.register_function(( RegisterFunctionMessage::with_id(notifications::send.into()) .with_description(POST a notification to the provider webhook.into()), HttpInvocationConfig { url: https://hooks.provider.example.com/notify.into(), method: HttpMethod::Post, timeout_ms: Some(5000), headers: /* HashMapString, String */, auth: Some(HttpAuthConfig::Bearer { token_key: PROVIDER_API_TOKEN.into(), }), }, ));4.3HttpInvocationConfig字段说明字段类型默认值说明urlstring必填函数被调用时引擎请求的端点methodGET \| POST \| PUT \| PATCH \| DELETEPOSTHTTP 方法timeout_msnumber30000单次请求超时毫秒headersRecordstring, string{}每次调用附加的请求头authHttpAuthConfig无支持bearer/hmac/api_key三种鉴权配合token_key/secret_key/value_key字段一个重要的安全细节auth中的token_key、secret_key、value_key字段填写的是环境变量的名字而不是密钥本身。引擎在注册时从自身进程环境中解析这些变量因此密钥只存在于引擎宿主机上永远不会经过 SDK 的 WebSocket 通道传输见 docs/0-17-0/creating-workers/functions.mdx#L285-L289 的 Note。4.4 行为语义错误处理与可发现性引擎把调用 payload 作为 JSON 请求体发送任何非 2xx 响应或网络错误都会被视为调用失败并把失败传播回调用方HTTP-invokable Function 与进程内 handler 一样出现在engine::functions::list中可被 iii Console 发现docs/0-17-0/creating-workers/functions.mdx#L291-L296注册后该函数即可像其他函数一样被触发worker.trigger、iii triggerCLI以及绑定到任何既有触发类型queue、cron、state、http——包装这一层之后存量 API 立即获得了 iii 的“寻址能力”而流量与原有调用方都无需任何改动。这一步的收益是零风险入口加好了但没有任何调用方会走它系统行为完全不变。五、第二步把慢任务卸载到队列——调用方立即返回重试交给队列5.1 这一步在做什么存量系统里常有一类“慢调用”处理耗时长调用方同步等待失败还需要自己处理重试。迁移的第二步就是把这些调用挪到一个命名队列后面让调用方在消息入队后立即返回消费与重试全部交给 iii-queue Worker 处理。5.2 三种调用语义TriggerAction在 docs/0-17-0/using-iii/triggers.mdx 中worker.trigger支持三种 action正好对应三种不同的卸载程度action语义适用场景默认同步等待函数返回结果或超时调用方确实需要返回值TriggerAction.Void()fire-and-forget立即返回函数照常执行但调用方看不到结果纯副作用任务TriggerAction.Enqueue({ queue })经 iii-queue 路由到命名队列并带重试调用在消息入队后即返回慢任务卸载本步核心三语言调用示例// TypeScript路由到 iii-queue 的命名队列 math const result await worker.trigger({ function_id: math::add, payload: { a: 2, b: 3 }, action: TriggerAction.Enqueue({ queue: math }), });# Pythonasyncio 环境下可用 awaitable 形式 trigger_async result worker.trigger({ function_id: math::add, payload: {a: 2, b: 3}, action: TriggerAction.Enqueue(queuemath), })// Rust let result worker .trigger(TriggerRequest { function_id: math::add.to_string(), payload: json!({ a: 2, b: 3 }), action: Some(TriggerAction::Enqueue { queue: math.to_string() }), timeout_ms: None, }) .await?;5.3 队列作为 Worker 的一等公民从 docs/0-17-0/understanding-iii/index.mdx 的模型看队列本身就是一个 Workeriii-queue它对外提供TriggerAction.Enqueue这个 action而enqueue只是触发函数的入口之一——同一个函数既能被 HTTP、cron、state 变化触发也能被队列消息触发handler 代码无需任何改动。这意味着迁移第二步你只需要修改调用侧的 action 参数被调用的函数逻辑保持原样即可获得“立即返回 队列重试”的收益。前提说明使用该能力前需要先安装 iii-queue Workeriii worker add iii-queue并将其纳入config.yaml该 Worker 的具体队列机制以其在 workers.iii.dev 的 Worker 文档为准。六、第三步迁移持久化——每次只搬一个状态切片6.1 这一步在做什么第三步把系统里的持久化数据数据库表、KV、文件等按业务切片逐片迁入 iii 的状态原语——即 iii-state Worker 提供的持久化 KV 存储。每次只迁移一个切片其余部分保持原样直到准备好再搬下一片因此迁移过程可以随时暂停。6.2 安装并配置 iii-state使用 docs/0-17-0/quickstart.mdx 的做法从含config.yaml的目录运行iii worker add iii-stateiii worker add name会把 Worker 安装进你的项目、写入config.yaml并自动启动详见 docs/0-17-0/using-iii/workers.mdx。iii-state 在config.yaml中的典型配置见 docs/0-17-0/using-iii/engine.mdxworkers: - name: iii-state config: adapter: name: kv config: store_method: file_based file_path: ./data/state_store.db注意config.yaml的顶层只有一个键workers:每个条目包含nameregistry slug 或本地 Worker 名与一个由该 Worker 定义的config块。配置文件还支持${VAR:default}环境变量展开docs/0-17-0/using-iii/engine.mdx#L64-L76例如port: ${HTTP_PORT:3111}可在不 fork 配置文件的情况下按环境切换端口、URL 与特性开关。6.3 用state::get/state::set读写状态切片iii-state 以函数形式对外暴露读写原语state::get读与state::set写payload 中通过scope与key定位数据。Quickstart 中的累积求和示例docs/0-17-0/quickstart.mdx#L136-L159演示了如何把计算中间态持久化到状态切片def add_handler(payload: dict) - dict: a payload.get(a, 0) b payload.get(b, 0) result {c: a b} # 读取当前切片 running_total worker.trigger( { function_id: state::get, payload: {scope: math, key: running_total}, } ) new_total (running_total or 0) result[c] # 写回持久化 worker.trigger( { function_id: state::set, payload: {scope: math, key: running_total, value: new_total}, } ) result[running_total] new_total return result连续调用验证持久化iii trigger math::add a2 b3 # { c: 5, running_total: 5 } iii trigger math::add a10 b20 # { c: 30, running_total: 35 }累计值跨调用持久存在包括经由其他函数转发的调用。这正对应迁移第三步的用法把一个业务切片的“真相source of truth”迁入 state worker其余切片继续留在原存储每迁一片都是自包含、可回滚的一次变更。iii Consoleiii console还支持对状态做增删改查编辑会触发已注册的state:updated/state:deleted触发器docs/0-17-0/using-iii/console.mdx可用于迁移过程中的人工核对。6.4 双写与回滚边界由于每个状态切片以scope隔离迁移过程中可以采取“新写旧读 / 双写比对 / 逐 key 切换”等策略任意时刻都可以把读写路径切回原存储。这与教程结论一致每个切片独立可逆暂停或回滚都不影响系统其余部分。七、把三步串起来一次典型迁移的完整路径综合以上三步一次典型的增量迁移是这样一个过程包装iii启动引擎自动生成config.yaml→ 用对应语言 SDK 注册一个指向存量服务的 HTTP-invokable Function如legacy::orders通过iii trigger legacy::orders ...或worker.trigger验证寻址链路——此时零流量改动卸载调用侧把慢任务改为TriggerAction.Enqueue({ queue: orders })调用方立即返回重试由 iii-queue 接管原函数 handler 不动迁移状态iii worker add iii-state把订单状态这一切片用state::set写入、state::get读取逐步把读路径切到 iii确认一致后再搬下一片收尾所有切片迁移完成后系统已整体跑在 iii 上全程没有出现过一次整体切换full cutover。每一步执行时可随时用iii worker list查看项目 Worker 状态、用iii worker status name/iii worker logs name检查运行情况docs/0-17-0/using-iii/workers.mdx这为“任意边界暂停或回滚”提供了运维侧的可观测支撑。八、结论按片推进随时可退原教程的结论可以凝练为两句话按顺序完成三个子教程系统会一片一片地迁移到 iii而不是一次性切换每个切片独立可逆可以在任意边界暂停或回滚其余系统不受影响。这背后的工程哲学在于 iii 把“路由器”引擎与“执行者”Worker解耦引擎不持有业务逻辑Worker 可按需增量加入iii worker add本身就体现了这种 npm 式的增量安装模型。因此渐进式迁移不是对 iii 的妥协而是其架构的自然结果——你可以在任意时刻停下脚步系统依然完整可用。【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表