Urban Airship 是一家帮助领导品牌吸引其移动用户的公司,他们可以帮助这些公司在客户下载完应用后就与公司建立起高价值的关系。目前,Urban Airship已经有了数量庞大的客户群,涵盖的领域有零售业、媒体与娱乐、运动与旅游、医疗等。这些公司都通过Urban Airship来增强其与客户的连结性。近日,来自于Urban Airship的开发者开源了他们所使用的一款开发工具: frock ,该框架用于简化模拟服务套件的管理。如果你在工作中使用了面向服务的架构,那么frock是很值得你去尝试的一个工具。
解决开发环境的复杂性
Urban Airship使用了 微服务 ,这对于一家SaaS公司来说是非常常见的模式:我们有很多服务,每个服务都包装了少量的功能,并且每个服务都能通过定义良好的协议为其他服务所请求。这对于可伸缩性与稳定性来说是非常棒的,不过对于开发者与其环境来说却产生了问题。我们的团队从事Go的开发工作(Go是Urban Airship Engagement仪表盘产品),它是最早期的Urban Airship代码基之一,包含了大量的功能:消息生成与报告、账单与使用报告,以及服务管理(比如说前不久发布的 Urban Airship Connect )。Go会调用大量的服务,这导致团队的开发环境产生了很多问题。之前,我们的环境包含了一个单体的、运行于Vagrant之上的虚拟机,它依赖于仪表盘的所有服务,这么做有很多问题:
frock就是用来帮助我们解决上述所有难题的优秀工具。
何为frock?
我们可以通过frock使用自己定义的数据来模拟服务,使用团队习惯的语言与运行时来编写模拟服务:JavaScript与Node.js。frock的核心只提供了如下一些功能:
所有这一切都是通过一个JSON配置文件(根据约定,该文件名为frockfile.json)来配置的,并且通过包含了所有自定义模拟服务的仓库进行共享。frock从grunt与gulp获得了灵感,其配置就位于你的项目当中。对于HTTP模拟来说,一个frockfile会包含一个简单的服务与路由定义,比如说:
{ "servers": [ { "port": 8080, "routes": [ { "path": "/api/devices", "methods": ["GET"], "handler": "frock-static", "options": { "file": "./fixtures/devices.json", "contentType": "application/json" } }, { "path": "*", "methods": "any", "handler": "frock-proxy", "options": { "url": "http://localhost:8052" } } ] } ] }
这是我们frockfile中服务定义的一个高度简化的版本。在访问Go仪表盘时,你实际上会命中一个高可用的代理:该代理会在内部将流量导向不同的服务,其中的主服务是Go,不过一些API路由则是由二级服务所处理的。相比于在本地运行这个二级服务,我们可以通过frock选择这个路由,并使用模拟的版本将其替换掉。在上述代码示例中,模拟的仅仅是个静态文件,它由frock-static通用静态服务器插件所处理。所有其他的请求则会直接转向本地运行的Go开发服务器,服务器的端口是8052。
编写frock插件
实际上,frock是我们解决这一问题的第2个平台,第1个平台是个名为“multimock”的工具,它是个单体Python应用,能够创建出多个服务线程,并在到达自定义处理器之前将其传递给一些通用的转换函数。它能够解决不少问题,不过我们还是重写了它,最后才有了frock。为什么要这么做呢?这是因为在multimock中编写插件是非常困难的事情。在编写frock时,我们的核心原则是“插件的编写应该保持简单,内建的假设越少越好”。frock通过对插件施加极少的限制来实现这个目标,并且将Node.js HTTP模块的简单性直接应用到了实现中。如下代码展示了最简单的一个frock HTTP插件,它只是向命中路由的任何请求响应了“hello world!”而已:
// file ./hello-world.js module.exports = createPlugin function createPlugin (frock, logger, options) { return handler function handler (req, res) { res.end('hello world!') } }
如果之前编写过Node.js的请求处理器,那么上述代码就很容易理解了;该frock插件只包含了一个返回路由处理器的工厂函数。在上述frockfile示例中,我们将这个插件替换为静态文件服务器:
{ "servers": [ { "port": 8080, "routes": [ { "path": "/api/devices", "methods": ["GET"], "handler": "./hello-world" }, { "path": "*", "methods": "any", "handler": "frock-proxy", "options": { "url": "http://localhost:8052" } } ] } ] }
现在,对http://localhost:8080/api/devices的任何请求都会返回“hello world!”。
frock对我们的帮助作用
大的特性发布常常都是非常复杂的:你经常要等待依赖的服务就绪,还要阅读如何使用这些服务的规范。在快节奏的项目中,这意味着你要不断追赶集成的日期;当最终所有组件都就位后,你希望他们能够彼此调用成功。Connect就是这样一个项目,frock可以帮助Web团队构建出令我们充满自信的项目,并且能够很好地实现集成。
其实,上面所介绍的关于frock的一切都不是什么革命性的功能,不过frock却能够帮助我们以一种更简单的方式做到这些。建议各位读者尝试一下,你会发现它在你的开发环境中是非常有用的。frock README 包含了一个快速起步指南,还有一个内容丰富的文档与示例代码,可以帮助你快速起步,在项目中 使用frock 并 实现自己的插件 。