【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
项目地址:
https://gitcode.com/gh_mirrors/wi/wing
点击查看 免费下载
导读
本文以仓库中 http-server.test.w_compile_tf-aws.md 这一 hangar 测试快照为起点,完整还原 Wing 语言中 cloud.Service(云服务生命周期抽象)与 HTTP 服务器联动的测试用例:从快照文件本身的内容结构、被测源码 http-server.test.w 的写法,到 extern 桥接的 JavaScript 实现、SDK 底层 Service 类的语义设计,以及 sim 与 tf-aws 两个编译目标为何产生截然不同的产物。读完本文,你将能读懂 Wing 测试快照的生成与命名规则,掌握 cloud.Service 的 start/stop 生命周期编程模型,并理解目标平台条件编译(@target == "sim")如何影响最终 Terraform 产物。
快照文件是什么:hangar 测试快照体系
该文档位于 tools/hangar/snapshots/test_corpus/sdk_tests/service/ 目录下,是 Wing 仓库中 hangar(测试挂架)工具自动生成的快照文件。其命名遵循 tools/hangar/src/utils.ts 中 createMarkdownSnapshot 的定义:
const snapPath = join(
snapshotDir,
"test_corpus",
dirname(relativePath),
`${wingFile}_${testCase}_${target}.md`
);
即 文件名_测试类型_编译目标.md。因此:
- http-server.test.w_compile_tf-aws.md 表示源文件 http-server.test.w 以 compile(仅编译) 方式在 tf-aws 目标下的产物快照;
- 同目录还存在 http-server.test.w_test_sim.md,表示同一源文件以 test(运行测试) 方式在 sim 目标下的运行快照。
快照头部 # [http-server.test.w](https://link.gitcode.com/i/8a3797bde0d0904e00743a889de82039) | compile | tf-aws 中的相对链接指向被测源文件 tests/sdk_tests/service/http-server.test.w,这是 hangar 自动计算出的从快照目录到测试目录的相对路径。
快照内容逐字段解读:main.tf.json
快照正文只有一个文件段落 ## main.tf.json,内容如下(与快照一致):
{
"//": {
"metadata": {
"backend": "local",
"stackName": "root"
},
"outputs": {}
},
"provider": {
"aws": [
{}
]
}
}
逐字段解读:
- //.metadata.backend = "local":Terraform 后端为本地状态(local backend),即不配置远程 state 存储;
- //.metadata.stackName = "root":Wing 应用根构造(root stack)的栈名,与 packages/@winglang/sdk/src/target-tf-aws/app.ts 中栈的组织方式对应;
- //.outputs = {}:该编译产物没有导出任何 Terraform output;
- provider.aws:声明了 AWS provider,配置为空对象 {},表示使用默认凭证与区域配置。
这份 main.tf.json 是整个 tf-aws 编译产物中唯一生成的文件。也就是说,把 http-server.test.w 编译到 tf-aws 目标时,Wing 只会产出一个声明了 AWS provider 的空骨架——这正是下一节要解释的关键点。
被测源码:http-server.test.w 完整解读
被测源文件 tests/sdk_tests/service/http-server.test.w 完整内容如下:
bring cloud;
bring http;
struct Address {
port: num;
}
inflight interface IHttpServer {
address(): Address;
close(): void;
}
// only supported in the "sim" target for now
if @target == "sim" {
class MyService {
b: cloud.Bucket;
body: str;
pub s: cloud.Service;
new(body: str) {
this.b = new cloud.Bucket();
this.body = body;
this.s = new cloud.Service(inflight () => {
log("starting service");
let server = MyService.createServer(this.body);
let port = server.address().port;
log("listening on port {port}");
this.b.put("port", "{port}");
return () => {
log("closing server…");
server.close();
};
});
}
pub inflight port(): num {
return num.fromStr(this.b.get("port"));
}
extern "./http-server.js" static inflight createServer(body: str): IHttpServer;
}
let foo = new MyService("bang bang!");
test "http server is started with the service" {
let response = http.get("http://localhost:{foo.port()}");
log(response.body);
assert(response.body == "bang bang!");
}
test "service.stop() closes the http server" {
let before = http.get("http://localhost:{foo.port()}");
assert(before.ok);
foo.s.stop();
// now the http server is expected to be closed
let var error = false;
try {
http.get("http://localhost:{foo.port()}");
} catch {
error = true;
}
assert(error);
}
}
这段测试的核心设计值得逐一拆解:
目标平台守卫:if @target == "sim" 是 Wing 内建的目标平台条件编译宏。整个测试体(MyService 类与两个 test)都被包裹在其中,注释也明确写着 only supported in the "sim" target for now(目前仅在 sim 目标下支持)。这正是 tf-aws 编译快照中没有任何业务资源(Bucket、Service 等)的根本原因——条件为假时整段代码不会进入编译产物。
inflight interface IHttpServer:定义了一个运行期(inflight)接口,包含 address() 与 close() 两个异步方法,用于抽象外部 JavaScript 模块返回的服务器句柄。
MyService 类:封装了一个 cloud.Service 与一个 cloud.Bucket。Service 的 handler(inflight 闭包)负责:启动时打印日志、通过 extern 的 createServer 创建 HTTP 服务器、读取端口号并写入 Bucket;返回值是一个清理闭包,在服务停止时被调用,用于关闭服务器。
pub inflight port(): num:通过 num.fromStr(this.b.get("port")) 从 Bucket 中读回端口号,供测试发起 HTTP 请求使用——这是用共享 Bucket 在服务 handler 与外部测试代码之间传递运行期数据的典型手法。
extern 静态 inflight 方法:extern "./http-server.js" static inflight createServer(body: str): IHttpServer; 声明将方法实现委托给同目录下的 http-server.js,这是 Wing 的 extern 外部函数桥接机制。
两个测试用例:
- 第一个测试验证"服务启动后 HTTP 服务器随之可用":请求 http://localhost:{foo.port()},断言响应体等于 "bang bang!"(即构造时传入的 body);
- 第二个测试验证生命周期控制:先确认服务器可访问,随后调用 foo.s.stop() 停止服务,再发起请求并断言抛错,证明服务器已被关闭。
为什么 tf-aws 编译产物如此精简
从快照本身看,tf-aws 产物只有 provider 声明;这并非编译器故障,而是测试代码被目标平台条件守卫隔离的必然结果。可以从仓库源码得到佐证:
- 在 packages/@winglang/sdk/src/target-tf-aws/service.ts 中,cloud.Service 在 AWS 目标的实现会真实生成一整套基础设施资源:EcsCluster、EcsService(FARGATE 启动类型)、EcsTaskDefinition、IamRole、SecurityGroup、CloudWatch 日志组、Docker 镜像与 ECR 仓库等。若 http-server.test.w 去掉 @target 守卫,tf-aws 快照绝不可能只有 20 行——这一点反过来印证了快照的"精简"正是条件编译生效的证据。
- 另外 tools/hangar/src/utils.ts 显示,快照在写入前会经过清理:删除 terraform 字段与 cdktf 版本信息(delete finalObj.terraform、delete finalObj["//"]["metadata"]["version"]),因此快照展示的是"业务相关"的 Terraform 内容。
extern 桥接:http-server.js 与 extern.d.ts
被测测试依赖 tests/sdk_tests/service/http-server.js 提供真实 HTTP 服务器实现:
const http = require("http");
exports.createServer = async function(body) {
return new Promise((resolve, reject) => {
const server = http.createServer();
server.on("request", (_, res) => res.end(body));
server.on("error", reject);
server.on("listening", () => resolve({
address: () => server.address(),
close: () => new Promise((resolve) => server.close(resolve)),
}));
server.listen();
});
};
要点:
- 使用 Node.js 原生 http 模块,监听随机端口(server.listen() 不传端口);
- request 事件中直接将 body 作为响应体返回,这解释了测试断言 response.body == "bang bang!";
- 返回对象暴露 address()(返回 server.address(),内含 port)与 close()(Promise 化的 server.close),与 Wing 侧的 inflight interface IHttpServer 一一对应。
同目录的 http-server.extern.d.ts 是该 extern 的类型声明,明确定义了 createServer: (body: string) => Promise<IHttpServer$Inflight>、Address.port: number 以及 IHttpServer$Inflight 的 address/close 签名——Wing 编译器据此完成类型检查与 inflight 客户端生成。
cloud.Service 的 SDK 语义与实现原理
从 packages/@winglang/sdk/src/cloud/service.ts 可以完整还原 cloud.Service 的设计:
- 生命周期三方法:ServiceInflightMethods 枚举了 START、STOP、STARTED,对应 inflight 客户端 IServiceClient 的 start()、stop()、started() 三个异步方法(service.ts);
- 构造参数:ServiceProps 支持 env(环境变量)与 autoStart(是否自动启动,默认 true,service.ts);
- handler 与清理闭包:IServiceHandlerClient.handle() 返回 IServiceStopHandler | undefined,即服务停止时执行的可选清理闭包(service.ts);
- entrypoint 生成:_preSynthesize() 会基于 handler 生成一段 Node 代码并写入 <assetName>.cjs 入口文件(service.ts),其中 start 调用 client.handle() 拿到清理函数并暂存到 $stop,stop 则调用 $stop() 并置空;若重复 start 会抛 service already started,未启动就 stop 会抛 service not started——这解释了测试中"先 stop 再请求必失败"的确定性。
在 sim 目标下,packages/@winglang/sdk/src/target-sim/service.ts 将服务描述为"在 Node vm 内派生的进程",通过 autoStart 属性控制是否随应用自动拉起,与官方文档 service.md 中"sim 目标下服务只是 node vm 中的进程"的描述一致。
sim 目标运行快照:测试结果验证
同目录的 http-server.test.w_test_sim.md 记录了该测试在 sim 目标下的真实运行输出:
[INFO] http server is started with the service | starting service
[INFO] http server is started with the service | listening on port <PORT>
[INFO] http server is started with the service | bang bang!
[INFO] http server is started with the service | closing server…
[INFO] service.stop() closes the http server | starting service
[INFO] service.stop() closes the http server | listening on port <PORT>
[INFO] service.stop() closes the http server | closing server…
pass ─ http-server.test.wsim » root/Default/test:http server is started with the service
pass ─ http-server.test.wsim » root/Default/test:service.stop() closes the http server
Tests 2 passed (2)
这份运行快照与 tf-aws 编译快照互为镜像,完整呈现了完整生命周期:两个测试各自经历"starting service → listening on port →(请求成功/失败)→ closing server",最终 Tests 2 passed (2)。其中 <PORT>、<DURATION> 为快照规范化后的占位符(随机端口与耗时被抹平以保证快照确定性)。
AWS 目标的底层实现(延伸阅读)
若去掉 @target 守卫,tf-aws 目标的 cloud.Service 会真实落地为 ECS/Fargate 基础设施(packages/@winglang/sdk/src/target-tf-aws/service.ts):
- inflight 闭包被封装为 Dockerfile(createServiceDockerfile)并构建、推送到 ECR 仓库;
- 任务定义使用 FARGATE 兼容性、memory: "512"、cpu: "256"、awsvpc 网络模式;
- 默认创建安全组(全放通 egress/ingress)、CloudWatch 日志组(/ecs/<assetName>,保留 7 天)与执行/任务 IAM 角色;
- 运行期 start/stop/started 由 shared-aws/service.inflight.ts 中的 ServiceClient 实现:start 通过 UpdateServiceCommand 将 desiredCount 置为 1,stop 置为 0,started 通过 DescribeServicesCommand 判断 desiredCount >= 1——这与 SDK 文档 service.md 中"AWS 下使用 ECS + FARGATE,未指定 VPC 时创建默认 VPC"的描述相互印证。
如何复现与查看
- 快照仅用于展示编译/运行结果,属于只读测试产物;要复现其生成过程,需要运行仓库的 hangar 测试套件(tools/hangar),其入口逻辑位于 tools/hangar/src/generate_tests.ts;
- 想要动手实验这段测试,可将 http-server.test.w 与同目录的 http-server.js、http-server.extern.d.ts 放在同一目录,并在 wing.toml 中将默认目标配置为 sim 后运行 wing test;注意该用例目前仅支持 sim 目标,tf-aws 目标下因 @target 守卫不会生成业务资源;
- 如需深入了解 cloud.Service 的 API 语义,可继续阅读 SDK 文档 service.md,其中涵盖了 autoStart: false 的手动启停、start/stop 幂等性(重复调用无副作用)以及返回清理闭包传递上下文的推荐模式。
小结
一个看似只有 20 行的 tf-aws 编译快照,背后串联起了 Wing 的整套测试与抽象设计:hangar 快照命名约定、@target 目标平台条件编译、extern 外部代码桥接、cloud.Service 的 start/stop/清理闭包生命周期模型,以及 sim(Node vm 进程)与 AWS(ECS/Fargate + ECR)两种目标实现的巨大差异。读懂这类快照,是理解 Wing 编译器产物与标准库抽象的最佳切入点之一。
赞
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
项目地址:
https://gitcode.com/gh_mirrors/wi/wing
点击查看 免费下载
相关推荐
三步构建专属虚拟骑行训练室:告别网络依赖的全新体验
YimMenu免费GTA5辅助工具:新手快速上手终极指南
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网硕互联帮助中心



评论前必须登录!
注册