在经过几年时间的开发之后,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