与 Henrik Feldt 谈论 Suave 1.0

  • Pierre-Luc Maheu
  • 邵思华

2016 年 1 月 29 日

话题:.NETWeb框架语言 & 开发架构

在经过几年时间的开发之后,Suave 1.0终于于近期正式发布了。InfoQ 与 Suave 的维护者,同时也是 qvitoo 的 CEO Henrik Feldt 进行了一次访谈,以深入地了解这个框架的功能以及它的开发历史。

InfoQ:Suave 的开发是怎样启动的?

HF:Suave 的第一次代码提交是大约 6 年前由 Ademar Gonzales 开始的。我大约在 2 年前加入了这一项目,当时我正在寻找一个合适的 web 服务器,它应当符合我的用例的需求,能够在 Windows 及 Linux 上通过 puppet 部署由 F# 编写的微服务。当时,Suave 的开发已经停滞了几年的时间,但我喜欢它的代码库,因为它非常容易理解。

当时的 Suave 有一些稳定性方面的问题,但 Ademar 给予了我非常积极的响应,每次我编写了一个单元测试或是 repository 的时候,他都能快速地修复其中的问题,而且经常是连夜回复。

因此,我们之间的合作具有很高的生产力。从我的角度来看,初次的互动是非常积极的。大约两个月之后,在经过了大量的测试之后,我们将其发布到生产环境中。随后我们克服了一些小小的障碍,在一周之后部署了修正代码。Suave 的表现正如它的名字一样:非常平滑(smooth)

InfoQ:如何创建一个能够处理几百个路由(route)的大规模 Suave 应用,同时又能够保持它的可维护性呢?

HF:我常用的做法是让每个路由的前缀对应着一个不同的 F# 模块,记录在 Filters.pathStars 中。如此一来,在开发过程中,我就能够轻易地对 App.fs 文件进行扫描,以找到我想要改动的前缀。App.api 是一个庞大的“选择”列表,任何互不相关的关注点,例如登录功能都可以通过“context”功能以加入这个路由列表中。

对于每个模块,例如帐号(Accounts)模块来说,会通过 Accounts.api 列出所有谓词 + 路径的组织(以及其他过滤器)。在这个“选择”列表中的每一行将负责为调用者生成一个响应,哪怕发生了校验失败的情况(通过“bindReq”实现)。

在 App.api 中的前缀 / 位置列表中的最下方部分是文件模块的使用,因此如果某个 API 的路由找不到匹配,至少该请求可能会对应一个静态文件。对于 qvitoo 来说,如果该文件也找不到,那么请求也将始终能够匹配并返回 index.html。因为我们使用了 React.JS 以及 react-router,因此 react-router 需要负责应用的深度链接与导航,并找到正确的页面。

这种结构能够让我们以一种非常灵活的方式添加更多的路由。在 Suave 中不需要担心可维护性的问题,因为这个库不会干扰你的行为,让你能够自由地加以选择。

另外一种方式是通过类似于 generator://github.com/rayokota/generator-angular-suave 这样的工具自动生成路由。此外,我认为将来在这方面可能会用到一些元编程技术(例如 camlp4)。

InfoQ:Suave 最适合于什么样的场景?

当然是构建 web 应用了!你可以构建各种应用,包括 API、微服务以及一个大型的 web 网站。你也可以把它当作一个服务端类型的 F# 或 C# 的 CLR 应用,作为访问你的 CLR 客户端的入口。你也可以在 TopShelf Windows 服务中通过它暴露 /health 功能,并结合使用 Logary,它能够让运维工作显得更简单。

InfoQ:为了运行 Suave 应用,在服务器上要进行什么配置?

HF:你需要安装 Mono 或者.NET,然后运行 fsx 文件或是 exe 文件,仅此而已!

主要的麻烦在于,如何在没有登录的情况下,保证你的服务器始终在运行这个可执行文件。

如果你的服务器是 Windows 环境,那么可以选择 TopShelf(我们在 GitHub 上的库中有一个示例),或者运行 winsw 并直接指向你的控制台应用。Suave 本身是非常稳定的,因此你可以在很长的时间内运行这些微服务,并且无需任何人工的介入。

在 qvitoo 中,我们的开发工作都在 OS X 上完成,并且在 CentOS、Ubuntu 以及 CoreOS 的某些扩展等操作系统中进行预发布和实际部署。当我们运行 Linux 系统时,所要做的就是在你所选择的进程管理器中保持一个入口。我们所选择的是 systemd,因此对于 qvitoo.com,我们编写了一个 qvitoo.service 文件,如下所示:

[Unit] 
Description=Logibit Qvitoo
Wants=bus-org.freedesktop.NetworkManager.target
After=bus-org.freedesktop.NetworkManager.target

[Service]
EnvironmentFile=/etc/sysconfig/qvitoo
User=qvitoo
ExecStart=/opt/qvitoo/start
Restart=always
RestartSec=5

在我看来,Suave 如今所受到的关注是因为 Suave 非常容易上手。

当然,你也可以部署至 Heroku 中,这是一种十分简便的方法,因此你完全无需管理服务器的问题。如果乐意的话,你也可以选择使用 Azure。

InfoQ:在这次主要的发布之前,在过去几年中还有哪些亮点吗?

这个项目有以下几个里程碑:

  • 将首个版本 0.0.2 发布为 NuGet 包。
  • 设计了产品的文档以及 suave.io 网站,这起到了非常大的作用,人们开始对这一项目感兴趣。
  • 对于公共 API 实现了完全的文档化,各种 HTTP 的组合完全按照 HTTP RFC 实现了文档化。
  • 通过 Ademar 设计的 monadic socket builder,让产品的稳定性得到了很大的改进。
  • 0.10 版本开始支持服务端事件发送(Server Sent Events)。
  • 大约在 0.16 版本开始,人们对此项目的兴趣逐渐上升,人们开始在推文中提到 Suave。
  • 从 0.19 版本开始支持加密的强会话状态,并支持在 cookie 中保存状态。
  • 0.20 版本对 API 进行了大量的重新组织,让这些 API 更受欢迎,并且易于理解。
  • 0.21 修复了在过载的情况下出现末端缓冲区溢出的状况。
  • 从 0.25 版本开始,这一项目得到了 Don Syme 的关注,他为我们免费提供了一次 API 的审查,并且通过一个内容丰富的 pull request 改进了许多小地方。
  • 0.26 版本开始支持 Razor。
  • 0.27 版本开始支持 WebSocket!
  • 0.32 版本开始支持 OWIN,从此能够支持来自于社区的大量用例。
  • 0.33 版本创建了 LibUV 这个 tcp factory。
  • 1.0 版本使 API 稳定了下来,并且通过托管代码实现了 TLS(来自于框架)。

InfoQ:对于 Suave 的性能,你是否能够提供一些数据?

HF:qvitoo 采取了事件溯源的领域驱动设计(DDD)及命令查询职责分离(CQRS)系统,运行在先进的硬件上,写操作的延迟通常在毫秒级以下,而读操作也差不多同样快,性能上的延迟主要取决于对于其他子系统的 IO 操作。

我们还没有对 Suave 进行过基准测试,在第 1 个版本发布之前,我们的主要目标是发布一个优秀的功能性 API,它需要做到易用与易于上手。我们希望 Suave 能够在高吞吐量的网站与服务中得到更广泛的应用,我们将看到更多对于性能方面的贡献。

我在运行 OS X 的笔记本上进行过一些测试,它每秒种能够运行 5600 次 Hello

World。按照我们的假设,如果我们能够转换到其他异步框架,例如 Hopac 上,那么将能够获得指数级的吞吐量性能提升,因为它的内存分配比起 F# 中的 async 实现要少。而如果在对关键路径中进行的对象分配能够做得更好,那么还有可能获得指数级的提高。

如果某位读者觉得这是一个有趣的挑战,那不妨来尝试一下贡献代码。我们会频繁并快速的合并各种 pull

request,并对合并的要求提供大量的反馈,这会使得贡献代码的过程更有趣,而且新人也能够很容易上手。

InfoQ:你对于今后的发展是否有一个路线图呢?

HF:我们准备支持 HTTP2。另外,我们也在讨论如何通过更少的内存分配实现 1 至 2 个数量级的性能提高。我们或许会转为使用 Hopac,它在异步性能上的表现比起 F# 中内置的异步功能更出色。

F# 团队提供了一个支持 CoreCLR 的 pull request,等到 CoreCLR for F# 方面的工具比较稳定之后,我们就打算合并这个 pull request。

另外,如果所有的 F# HTTP 客户端都能够使用相同的 SocketOp 编程模型,也将有一定的实用性,它非常适合于对由 Suave 开发的网站与 API 进行单元测试与集成测试时。

作为一门编程语言,F# 正在逐渐成熟的过程中。如果 Suave 能够通过 F# 进行编译,那么或许由 Suave 开发的网站或许能够在编译后运行在 Mirage 这种 exo kernel(也叫做云操作系统)中?甚至也许能够将 Suave 网站编译为原生代码,在不依赖于类库的情况下运行。

我们乐意为在 Alpine Linux 上通过 Mono 或 CoreCLR 进行部署提供一个良好的用例。经过几个月之前开始的工作,Mono 已经开始了测试 repo。一个 Suave 服务的部署包大约有 5MiB(约等于 5MB)用于操作系统,30MiB 用于 CoreCLR,还有 5MiB 用于服务本身。

InfoQ:你还有什么要补充的吗?

HF:如果你是一位熟练的开发者,并且有兴趣在工作中使用一些令人振奋的技术,例如 F#、Suave、AI 以及 web,那么你或许很适合在 qvitoo 工作。请发送邮件至 hi@qvitoo.com。

如果你想要更多地了解如何编写 Suave 代码,以及通用的 F# 代码,你可以订阅F# TV,这也是对 Suave 的开发与维护的一种帮助。

查看英文原文:Interview with Henrik Feldt on Suave 1.0

.NETWeb框架语言 & 开发架构