编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

Ziglang编程实践- Zig 极快的后端库Zap ——封装facil.io库

wxchong 2024-10-27 15:36:32 开源技术 221 ℃ 0 评论

?zap? zig 极快的后端库

https://github.com/zigzap/zap

Zap 是我用 Flask 和 mongodb 等 Python 语言编写的 REST API 的 zig 替代品。它可以被视为网络应用程序的微框架。

作为替代,我需要的是一个可以与 Zig 配合使用的快速、健壮的 HTTP 服务器,于是我选择了封装一流的事件网络 C 库 facil.io。Zap 封装并修补了 C web 应用程序框架 facil.io。

?ZAP?快速, 健壮, 稳定

在生产中使用 ZAP 超过 6 个月后,我可以自信地断言,事实证明它确实如此:

  • ? 极快 ?
  • 极其强大

完全符合我设定的目标!

Most FAQ:

  • Q: Zap 无法与 Zig 主版本一起构建?
  • A: 请参见 0.12.0 分支。有关如何使用该分支的示例,请点击此处 。请注意,0.12.0 分支并非 ZAP 的正式主分支。尚未正式发布。直到 zig 0.12.0 发布。
  • Q: ZAP能够在Windows上运行吗?
  • A: 不能。这是由于底层的 facil.io C 库造成的。未来版本的 facil.io 可能会支持 Windows,但目前还没有时间表。您在 Windows 上的最佳选择是 WSL2 或 docker 容器
  • Q: ZAP 支持TLS / HTTPS?
  • A: 是的,ZAP 支持使用系统的 openssl。请参阅 https 示例,并确保使用 -Dopenssl=true 进行构建。

有效使用方法

建议查看基础的示例,以了解更真实的使用案例。大多数示例都非常精简,只包含展示功能所需的内容。

  • 超级简单的构建过程(Super easy build process) :Zap 的 build.zig 现在使用新的 Zig 软件包管理器来管理 C 依赖项,不再使用 git 子模块了.
  • tested on Linux and macOS (arm, M1)
  • hello : welcomes 的静态HTML
  • routes : 在 HTTP 路径上进行调度的超级简单示例。注意:示例中的调度是超基本的 DIY 式调度。请参阅基于端点的示例,了解更实际的使用案例 s.
  • serve :传统的静态网络服务器,可选择动态请求处理方式
  • sendfile : 发送文件、尊重压缩头等的简单示例, etc.
  • bindataformpost : 通过表单发送接收二进制文件的示例.
  • hello_json :路由提供json数据
  • endpoint : 简单的 JSON REST API 示例,其 /users 端点可执行 PUT/DELETE/GET/POST 操作并列出用户,还有一个简单的前端可供使用。它还引入了一个 /stop 端点,用于关闭 Zap,以便在 main() 中进行内存泄漏检测。
  • - 检查 main.zig 如何在关闭 ZAP 时使用 ZIG 出色的 GeneralPurposeAllocator 报告内存泄漏。StopEndpoint 只是在收到 /stop 路由请求时停止 ZAP。C.
  • mustache : 使用 mustache 模板的简单示例.
  • endpoint authentication : 一个简单的认证端点。了解有关身份验证的更多信息 here .
  • http parameters : 支持所有类型的查询参数的简单示例。
  • cookies : 一个简单的cookie 实例.
  • websockets : 浏览器的简单 Webockets 聊天工具。
  • Username/Password Session Authentication : 一个便捷的身份验证器,可将未经身份验证的请求重定向到登录页面,并根据通过 POST 请求收到的用户名/密码对发送包含会话令牌的 cookies.
  • MIDDLEWARE support : 以中间件样式将请求处理程序链接在一起。使用ZIG-CEPTION提供完全类型安全的自定义上下文结构。如果你来自GO,这可能会吸引你.
  • MIDDLEWARE with endpoint support : 与上面的示例相同,但这次我们通过 zap.Middleware.EndpointHandler 将端点封装,从而在链的末端使用端点。在中间件链中混合使用端点可以使用 Zap 的认证端点和自定义端点。由于端点使用的是更简单的 API,因此如果想从封装端点访问中间件上下文,就必须在请求中使用 r.setUserContext() 和 r.getUserContext()。由于这种机制使用了*anyopaque指针(为了不破坏端点 API),因此它的类型安全性不如 zap.Middleware 使用上下文时那么高。.
  • Per Request Contexts : 随着 setUserContext() 和 getUserContext() 的引入,你当然也可以在不使用 zap.SimpleEndpoint 或 zap.Middleware 的项目中使用它们,如果你真的真的绝对找不到其他方法来解决上下文问题的话。我们建议在一个结构体内部使用 zap.SimpleEndpoint,该结构体可以提供你需要的所有上下文。你可以通过 @fieldParentPtr() 技巧在回调中访问你的结构体,该技巧在 Zap 的示例中被广泛使用,比如端点 endpoint example .
  • Error Trace Responses : 现在,您可以在捕捉到错误时调用 r.sendError(err,status_code),并向客户端/浏览器返回堆栈跟踪.
  • HTTPS : 演示如何轻松使用 facil.io 的 openssl 支持。必须使用 -Dopenssl=true 进行编译,并要求在系统中安装 openssl dev 依赖项(头文件、库).

我们将继续封装更多的 facil.io 功能,并为 zap 添加内容,以便将其用作真正研究项目的 JSON REST API 后端,为成千上万的并发客户端提供服务。

?速度极快?

自称速度极快是新的黑科技。至少,Zap 不会拖慢你的速度,而且如果你的服务器性能不佳,也可能不完全是 Zap 的错。Zap 依靠的是 facil.io 框架,因此它无法为自己赢得任何性能声誉。在 Zap 的初始实现中,我根本不关心优化问题。

但是,它到底有多快?快得惊人是相对而言的。与简单的 GO HTTP 服务器相比,简单的 Zig Zap HTTP 服务器在我的机器(x86_64-linux)上表现非常好:

  • Zig Zap 比GO快 30%左右
  • Zig Zap 的吞吐量比 GO 多 50% 以上

Update : Thanks to @felipetrz, I got to test against more realistic Python and Rust examples. Both python sanic and rust axum were easy enough to integrate.

Update : I have automated the benchmarks. See blazingly-fast.md for more information. Also, thanks to @alexpyattaev, the benchmarks are fairer now, pinning server and client to specific CPU cores.

Update : I have consolidated the benchmarks to one good representative per language. See more details in blazingly-fast.md . It contains rust implementations that come pretty close to Zap's performance in the simplistic testing scenario.




健壮


ZAP 非常强大。事实上,它是如此强大,以至于在我迄今为止的所有 ZAP 项目中(超过 5 个大型在线研究实验),我都能自信地只使用内存数据(RAM)。没有数据库,没有文件持久性,直到我在最后点击 "保存"。

因此,我得以推迟我狡猾的数据持久化策略,该策略类似于标记-清扫垃圾收集器,只在流量较低时持久化 "脏 "数据,以便更快地在线处理数据。但即使实施了这样的持久化策略,也是有风险的,因为当流量不低时,就意味着系统处于(重)负载状态。当负载高、数据变化最频繁-->潜在数据丢失最大化时,你会自信地不保存数据吗?

为了回答这个问题,我直接跳过了。在通过 API 接收到 "保存 "信号之前,我没有保存任何数据。结果成功了。ZAP 就这样不停地扫描。当交通平静下来或所有实验参与者都完成实验后,我点击 "保存",继续分析数据。

处理所有错误终究是有回报的。无隐藏控制流、无隐藏错误或异常是 Zig 的优势之一。

老实说,Zig 仍然存在缺陷。例如,如果你要求为工作线程设置较大的堆栈大小,Zig 就会不高兴,并产生恐慌。因此,请确保你的局部变量不需要几十兆字节的堆栈空间。


? 内存安全

请参见端点示例中的 StopEndpoint。该示例使用 ZIG 出色的通用分配器(GeneralPurposeAllocator)来报告 ZAP 关闭时的内存泄漏。StopEndpoint 只是在收到 /stop 路由上的请求时停止 ZAP。

您可以在调试构建和测试中使用相同的策略来检查代码是否泄漏内存。

Getting started 开始

使用zig最新版本 (0.11.0) .

$ git clone https://github.com/zigzap/zap.git
$ cd zap
$ zig build run-hello
$ # open http://localhost:3000 in your browser

在自己项目中使用?zap?

使用 zig release最新版本 (0.11.0) .

如果没有zig项目,那就创建一个项目:

$ mkdir zaptest && cd zaptest
$ zig init-exe
$ git init      ## (optional)

注意:Nix/NixOS 用户是幸运的;你可以使用现有的 flake.nix 并运行 nix develop 来获得一个开发 shell,其中提供了 zig 和所有依赖项,用于构建和运行 GO、python 和 rust 示例,以进行 wrk 性能测试。对于单纯的 zap 项目构建,nix develop .#build 只能获取 zig 0.11.0。


在已有的 Zig project中增加 Zap 很容易:

  1. build.zig.zon增加zap
  2. build.zig 增加zap

build.zig.zon :

.{
    .name = "My example project",
    .version = "0.0.1",

    .dependencies = .{
        // zap v0.2.5
        .zap = .{
            .url = "https://github.com/zigzap/zap/archive/refs/tags/v0.2.5.tar.gz",
            .hash = "1220c33e0ecc01b862ff6d929a948f5a8b5526a66f8d883a6d10eaff1620b8d4d605",
        }
    }
}

build.zig 's build 函数, add the following before b.installArtifact(exe) :

    const zap = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
    });
    exe.addModule("zap", zap.module("zap"));
    exe.linkLibrary(zap.artifact("facil.io"));

从此,你就可以在项目中使用 Zap 软件包了。查看示例了解如何使用 Zap。

将项目更新为最新版本的 zap

您可以在 build.zig.zon 中更改 Zap 的 URL。

- 最简单的方法:使用标记的

- 或标签版本之一,例如 0.0.9

- 或 zap 的最新提交版本

使用标记版本

转到发布页面。每个版本都会说明其版本号,并提供更改 build.zig.zon 和 build.zig 的说明。

例子

构建和运行例子 via:

$ zig build [EXAMPLE]$ 
./zig-out/bin/[EXAMPLE]

... [EXAMPLE]替代 hello , routes , serve , ... 查看例子列表 .

Example:构建运行 hello example:

$ zig build hello$ 
./zig-out/bin/hello

仅仅运行一个例子, 像routes , 不生成可执行文件 :

$ zig build run-[EXAMPLE]

Example: 构建运行理由实例 :

$ zig build run-routes

hello

const std = @import("std");
const zap = @import("zap");

fn on_request(r: zap.SimpleRequest) void {
    if (r.path) |the_path| {
        std.debug.print("PATH: {s}\n", .{the_path});
    }

    if (r.query) |the_query| {
        std.debug.print("QUERY: {s}\n", .{the_query});
    }
    r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
}

pub fn main() !void {
    var listener = zap.SimpleHttpListener.init(.{
        .port = 3000,
        .on_request = on_request,
        .log = true,
    });
    try listener.listen();

    std.debug.print("Listening on 0.0.0.0:3000\n", .{});

    // start worker threads
    zap.start(.{
        .threads = 2,
        .workers = 2,
    });
}

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表