Cangjie_test1:基于仓颉(Cangjie)的 Node.js 服务迁移案例研究项目

用户可通过该项目学习将 Node.js 小服务迁移到仓颉语言的完整过程,包含迁移步骤、问题解决办法及性能对比。项目提供可复现的测试资产,帮助工程师与研究者评估仓颉迁移可行性,理解关键差异并展开优化。【此简介由AI生成】

分支1Tags0
文件最后提交记录最后更新时间
7 个月前
7 个月前
7 个月前
7 个月前
7 个月前

迁移案例研究:将 Node.js 小服务迁移到 仓颉(Cangjie)

概要

本档为一项“仓颉语言迁移案例研究”:将一个现有的小型 Node.js 服务完整迁移为仓颉(Cangjie)实现,记录迁移步骤、遇到的问题与解决办法,量化性能与代码量差异,并提供可复现的测试资产(脚本、基准输出与一键运行包)。文档面向希望评估或实践仓颉迁移的工程师与研究者,帮助快速复现、理解关键差异并展开后续优化。

目标与范围

  • 功能等价:实现与 Node 相同的 HTTP 端点,保持 Content-Type 与返回格式兼容。
  • 最小化依赖:使用 stdx.net.http(vendor 化以便离线复现),避免外部数据库或复杂中间件。
  • 可复现性:提供一键运行脚本、bench 脚本与说明,便于他人复现结果。

项目结构(关键路径)

test1文件夹下

  • src/ — 仓颉实现源代码(入口 main.cj,controllers 等)。
  • node-ref/ — Node 参考实现与基准脚本(node-ref/bench/simple-bench.js)。
  • bench/ — 运行脚本、日志与结果(bench/run-cj-bench.ps1bench/results/ 等)。
  • bench/one-click/ — 一键打包脚本与说明(run-all.ps1、README、LICENSE)。

迁移步骤摘要

  1. 搭建仓颉工程骨架(cjpm.toml 指向 vendor stdx,src/ 放置 handler)。
  2. 将每个路由移植为仓颉 handler(GET/POST),对 POST 的 body 使用 InputStream 逐块读取并按需合并。若可知 Content-Length,优先预分配缓冲区以提高性能。
  3. 处理 HTTP 头:显式设置 Content-Type,并在高并发测试中避免一开始就强制 Connection: close(移除 Connection: close 后稳定性更好)。
  4. 运行并调试:使用 bench/run-cj-bench.ps1 启动服务、轮询就绪并运行 node-ref/bench/simple-bench.js 进行压测,保存输出到 bench/results/
  5. 记录问题并迭代:针对 socket is closed 警告、echo 实现的拼接效率、Windows 下的 OpenSSL 依赖和文件锁问题逐项修复或给出规避建议。

环境与先决条件

  • 操作系统:Windows(PowerShell 环境)
  • 必备:Node.js(用于运行基准脚本)、Cangjie 工具链(cjpm / cjo,如需运行 Cangjie 服务)
  • 运行时库:Windows 下 stdx.net.http 可能需要 OpenSSL DLL(libssl/libcrypto),请把 DLL 放入 vendor/ 或系统 PATH。

运行与复现(快速命令示例)

在仓库根目录的 PowerShell 中:

# 启动 Cangjie(背景)并把日志写入文件
Start-Process -FilePath 'powershell.exe' -ArgumentList '-NoProfile','-Command','cjpm run' -WorkingDirectory (Resolve-Path .).Path -RedirectStandardOutput 'bench\cj_stdout.log' -RedirectStandardError 'bench\cj_stderr.log' -PassThru

# 等待服务就绪(轮询)
for ($i=0; $i -lt 60; $i++) { try { $r = Invoke-WebRequest -Uri 'http://127.0.0.1:3000/' -UseBasicParsing -TimeoutSec 1 -ErrorAction Stop; if ($r.StatusCode -eq 200) { break } } catch { Start-Sleep -Milliseconds 200 } }

# 运行 bench(示例:50 并发 10 秒)
node node-ref\bench\simple-bench.js -c 50 -d 10 http://127.0.0.1:3000/api/hello | Tee-Object -FilePath bench\results\cj-api-hello-$(Get-Date -Format yyyyMMdd-HHmmss).txt

# 启动 Node 参考服务(若单独跑 Node 基线)
node node-ref\index.js

我还添加了 bench/one-click/run-all.ps1,可以自动:运行 CJ 的 bench(若 CANGJIE_RUN_CMD 已设置)、运行 Node 的 bench、收集输出并把 src/node-ref/cjpm.toml 等拷贝到 bench/one-click-output-<ts>/ 以便打包发布。

关键基准结果(摘选)

  • Cangjie — GET /hello-min (50c × 10s): requests 37917, errors 0, mean 13.15 ms, rps ≈ 3791.7。
  • Cangjie — GET /api/hello(移除 Connection: close)(50c × 10s): requests 32876, errors 0, mean 15.18 ms, rps ≈ 3287.6。
  • Node — GET /api/hello (50c × 10s, Node 运行于 3001): requests 108912, errors 0, mean 4.57 ms, rps ≈ 10891.2。

说明:Node 在该机与实现上显示更高吞吐与更低延迟;Cangjie 在调整后表现稳定且无错误。完整测试数据见 bench/results/benchmark-comparison.mdbench/results/ 中的具体输出文件。

遇到的问题与已采取的解决办法

  • socket 警告(ConnectionException: socket is closed
    • 原因分析:与短连接(Connection: close)与流式/阻塞读取逻辑有关,客户端或服务器在高并发下可能提前关闭连接。
    • 解决:在多数 handler 中移除显式 Connection: close,启用 keep-alive;并把 echo handler 改为先完整读取 body 后解析。问题明显缓解,错误数降为 0。
  • Echo 的缓冲合并性能
    • 问题:使用 acc = acc.concat(part) 在大体量或多次拼接场景下会产生 O(N^2) 行为。
    • 建议:使用 parts 列表并在最后一次性合并;或在可知 Content-Length 时预分配缓冲区;或实现可增长的 ByteBuffer。
  • Windows OpenSSL 与文件锁
    • 建议:将必要的 OpenSSL DLL 复制到 vendor/ 并在 README 中注明;在构建脚本中停止运行实例以避免文件锁。

建议的后续工作(优先级)

  1. 优化 POST /api/echo:改为 parts 列表 + 最后合并;添加单元/集成测试覆盖大/小 payload。
  2. 并发阶梯测试:自动化对并发级别 [1,5,10,20,50] 的短时测试,生成 CSV 汇总以便制图比较。
  3. 增加 DEBUG 日志(可选)以记录请求/连接 id,便于在高并发下追踪何时/为何连接被关闭。
  4. 若计划开源:完善 bench/one-click/README.md、选择并统一许可证(bench/one-click/LICENSE 已为 MIT 示例),并考虑把大型二进制(如 OpenSSL DLL)以合规方式提供或指向安装说明。

参考与附录


项目介绍

用户可通过该项目学习将 Node.js 小服务迁移到仓颉语言的完整过程,包含迁移步骤、问题解决办法及性能对比。项目提供可复现的测试资产,帮助工程师与研究者评估仓颉迁移可行性,理解关键差异并展开优化。【此简介由AI生成】

定制我的领域