微服务 API 模拟框架 frock 介绍

阅读数:2991 2016 年 2 月 17 日

话题:语言 & 开发微服务

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 之上的虚拟机,它依赖于仪表盘的所有服务,这么做有很多问题:

  • 速度非常慢。服务与依赖的数量非常多,包括几种不同的数据库都需要保持在运行状态才行。
  • 非常脆弱。我们的团队缺少 JVM 专家。上游对服务的修改会破坏我们的开发环境,导致我们很多人都不敢更新。
  • 导致开发者无法继续工作。当开发者的环境被破坏后,他们常常无法继续工作。环境的复杂性意味着这种破坏是会常常发生的。这会浪费大量的时间,并导致开发者非常沮丧。
  • 难以将数据导入到环境中。在开发阶段,你需要多种多样的数据来测试代码。之前的环境通常要求你手工运行测试数据,这是通过与持有数据的服务进行直接的通信来做到的。

frock 就是用来帮助我们解决上述所有难题的优秀工具。

何为 frock?

我们可以通过 frock 使用自己定义的数据来模拟服务,使用团队习惯的语言与运行时来编写模拟服务:JavaScript 与 Node.js。frock 的核心只提供了如下一些功能:

  • 高度可配置的路由,这使得我们可以轻松拦截对服务的调用。
  • 基于插件的架构,它并未对如何编写模拟服务进行强制性约束。
  • 针对常见任务的开箱即用的通用插件,即一个静态文件服务器与一个代理服务器。
  • 支持 HTTP 与 Socket 服务。
  • HTTP 中间件支持,可以自定义服务或路由的行为;比如说,它提供了一个延迟中间件,可以延迟请求的时间。

所有这一切都是通过一个 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实现自己的插件