去年,我写了一篇关于 使用 Groovy 编程语言从 API 访问外汇汇率数据,以简化我的费用记录的文章。 我展示了两个汇率网站 fixer.io 和 apilayer.net(现在是 apilayer.com)如何提供我所需的数据,使我能够使用前者在印度卢比 (INR) 和加元 (CAD) 之间进行转换,并使用后者在智利比索 (CLP) 和加元之间进行转换。
最近,ExchangeRate-API.com 的 David 联系我说:“您提到的免费 API (Fixer) 已被 CurrencyLayer 收购,并且取消了无需注册/无限访问的政策。” 他还告诉我:“我运营着一个名为 ExchangeRate-API.com 的免费 API,它具有与原始 Fixer 相同的 JSON 格式,无需任何注册,并允许无限请求。”
在交换了几封电子邮件后,我们决定将我们的对话变成一次访谈。 在访谈下方,您可以找到 脚本和使用说明。 (访谈内容已稍作编辑以使其更清晰。)
关于 ExchangeRate-API
Chris: ExchangeRate-API 与其他在线汇率服务有何不同? 是什么促使您提供这项服务?
David: 当我和一位朋友在 2010 年启动 ExchangeRate-API 时,我们构建并免费发布了它,因为我们确实需要这项服务来支持另一个项目,但在广泛的谷歌搜索后却找不到。 现在大约有 20 个这样的 API,它们提供了相当多的不同方法。 多年来,我尝试了许多不同的方法,但事实证明,免费提供高质量的数据是最受欢迎的。 我还受到这样一个想法的激励:即使开发者没有预算,也应该可以免费访问这些数据,以便他们构建有用的工具。
因此,我们的货币兑换 API 的主要区别在于它是无限的,并且无需注册。 这也使得开始使用它非常快速——您只需复制端点 URL 就可以开始使用了。
还有一两个其他免费且无限的 API,但这些 API 通常只提供欧洲中央银行提供的每日参考汇率。 ExchangeRate-API 从多家中央银行收集公开参考汇率,然后将它们混合以降低异常值的风险。 它还进行验收检查以确保汇率没有严重错误(例如,反向数据捕获记录美元兑智利比索而不是智利比索兑美元),并根据其历史准确性对不同的来源进行加权。 这使得该服务非常可靠。 我目前正在进行一个透明度项目,以比较和展示这种公共参考汇率混合与专有数据源的准确性,以便潜在用户可以就哪种类型的货币数据服务适合他们做出更明智的决定。
Chris: 我很高兴您包含了加元和印度卢比,因为这是我需要解决的一个问题。 我很遗憾看到您没有智利比索(这是我需要解决的另一个问题)。 您能告诉我们您如何选择货币列表吗? 您预计会在您的列表中添加其他货币吗?
David: 由于我对这项服务的主要目标是提供稳定可靠的汇率数据,因此我只在某个货币代码有多个数据源时才将其包含在内。 例如,在您提到您正在寻找智利比索数据后,我将智利中央银行发布的每日参考汇率添加到我们的系统中。 如果我能找到另一个包含智利比索的来源,它将被包含在我们的支持货币列表中,但在那之前,很遗憾不行。 目标是支持尽可能多的货币。
需要注意的是,对于某些货币,该服务至少有两个来源,但一些货币对(例如美元/欧元)几乎包含在每组公共参考汇率中。 我提到的透明度准确性项目有望使这种差异变得清晰,以便用户可以了解为什么我们的美元/欧元汇率可能比智利比索/印度卢比等不太常见的货币对更准确,以及货币对之间准确性差异的程度。 让这些信息快速且易于理解需要一些工作。
API 的架构
Chris: 您能向我们介绍一下您的 API 的架构吗? 您是否使用开源组件来交付您的服务?
David: 我完全使用开源软件来运行 ExchangeRate-API。 我绝对是一位开源爱好者,并且总是让朋友切换到开源,解释许可证,并在力所能及的范围内向我最常用的项目捐款。 我还尽量给我使用的项目的维护者发送电子邮件以表示感谢,但我做得不够。
该堆栈目前是 Ubuntu LTS、MariaDB、Nginx、PHP 7 和 Memcached。 我还使用了 Bootstrap 和 Picnic 开源 CSS 框架。 我使用 Let's Encrypt 通过电子前沿基金会的开源 ACME 客户端 Certbot 获取 HTTPS 证书。 该服务广泛使用了 UFW/iptables、cURL、OpenSSH 和 Git 等经典工具。
我的方法通常是尽可能保持一切简单,同时使用经过验证的开源构建块。 对于一个旨在始终可用于用户转换货币的项目来说,这感觉是实现可靠性的最佳途径。 我喜欢阅读可能对这样的项目有用的创新项目(例如 CockroachDB),但在它们被认为是真正万无一失之前,我不会使用它们。 显然,像 Heartbleed 这样的事件表明,即使是“枯燥”的项目也存在风险——但我认为这些风险比更新、前沿项目中的潜在未知风险更容易管理。
在基础设施设置方面,我在过去九年中稳步构建和改进了系统,现在它大致包含三个层级。 主集群在 Amazon Web Services (AWS) 上运行,由 Ubuntu EC2 服务器和高可用性 MariaDB 关系数据库服务 (RDS) 实例组成。 EC2 实例分布在多个 AWS 可用区中,并由托管的 AWS Elastic Load Balancing (ELB) 服务提供前端。 在具有自动跨区域故障转移的 RDS 数据库实例和分布在可用区中的 ELB 前端 EC2 实例之间,此设置非常可用。 然而,它仅在一个区域内。 因此,我在不同的地理位置设置了第二层虚拟专用服务器 (VPS) 实例,以减少延迟并将负载从更昂贵的 AWS 基础设施中分散出去。 这些目前与 Linode 合作,但我最近也使用了 DigitalOcean 和 Vultr。
最后,所有这些都受到 Cloudflare 的保护。 对于免费服务,一些用户不可避免地会选择滥用该系统,而 Cloudflare 是一款出色的产品,对于 ExchangeRate-API 至关重要。 我们的服务器可以得到保护,我们的用户可以获得低延迟的区域内缓存。 Cloudflare 配置了负载均衡和流量转向产品,以减少延迟并将流量从不健康的基础设施部分立即转移到可用的源。
通过这种非常冗余的方法,大约三年以来,由于基础设施问题或用户负载而没有发生停机。 这段时间内经历的少数服务降级时期都是由于代码、部署策略或配置错误问题造成的。 该设置目前每月处理数亿次请求,负载水平较低且成本可控,因此仍有很大的增长空间。
实际的应用程序代码是 PHP,大量使用了 Memcached。 Memcached 是 Brad Fitzpatrick 在 2003 年启动的一个出色的开源项目。 它不是很引人注目,但它是一个非常可靠且高性能的分布式内存键值存储。
与开源社区互动
Chris: 您的配置中使用了令人印象深刻的开源数量。 您如何与这些项目中更广泛的用户社区互动?
David: 在运营一个副业 SaaS 的同时,我真的很难找到成为一个优秀的开源公民的最佳方式。 我曾考虑构建某种开源库并发布它,但我还没有想到任何尚未完成且我能够投入时间来可靠维护的东西。 只有当我有信心确保选择该项目的用户不会突然发现自己依赖于弃用软件时,我才会启动这样的项目。 我还研究过为 ExchangeRate-API 所依赖的项目做出贡献,但由于我只使用最大、最成熟的选项,我缺乏专业知识来为如此重要的项目做出有意义的贡献。
我目前正在为该服务开发一个新的“Pro”计划,我将把这部分收入的一部分捐赠给我的开源依赖项。 然而,这仍然感觉像是一个权宜之计——回答这个问题让我意识到我需要投入更多时间来启动一个以 ExchangeRate-API 为家的开源项目!
展望未来
Chris: 我们目前只能查询最新的汇率,但看起来您可能会在今年晚些时候提供历史汇率。 您能告诉我们更多关于提供历史数据的技术挑战吗?
David: 有一个使用我们相同的算法从多个中央银行参考集混合而成的历史汇率数据集。 然而,由于数据质量方面的一些问题,我停止了新的注册。 该数据集可以追溯到 1990 年,并且有一些早期时段需要更好的数据验证。 因此,我正在构建一个更好的系统,用于检查和比较摄取的数据,并添加一个额外的数据源。 计划是在今年晚些时候提供一个干净且经过更全面验证的准确数据集。
在技术方面,历史数据比实时数据稍微复杂一些。 与实时数据集(只有几个字节)相比,历史数据是数百万个数据库行。 这些数据最初是从数据库基础设施提供的,并具有较长的生存时间 (TTL) 中间缓存层。 这在很大程度上是高性能的,但在用户希望以网络能够处理的速度尽可能快地转储整个数据集的情况下会遇到困难。 如果缓存足够热,这很好,但如果最近发生了重启、新服务器部署等,这些大型请求集会在缓存中“丢失”足够多的内容,导致数据库出现有问题的负载峰值。
显然,目标是构建一个即使在积极的使用场景下也能以正常性能处理的基础设施,因此新的历史汇率数据集将伴随一个抢占式内存缓存,而不是请求驱动的缓存。 值得庆幸的是,如今 RAM 很便宜,即使对于像 ExchangeRate-API.com 这样的小型项目,将几百兆字节的数据完全放入 RAM 中也是一种可行的方法。
Chris: 听起来您已经经历了该服务的多次迭代才达到今天的水平! 您认为它在未来几年会走向何方?
David: 我的目标是使其覆盖全球每一种货币,以便任何寻找此类软件的人都可以轻松地以编程方式免费获得他们需要的汇率。
我也绝对希望有一个价格实惠的 Pro 计划,能够真正引起用户的共鸣。 正确地做到这一点也意味着为免费用户提供更好的基础设施和更低的延迟。
最后,我希望在 ExchangeRate-API 的旗帜下拥有一些有用的开源库。 启动一个能够找到热心社区的小型项目将是非常有意义的。 运营一些免费如啤酒的东西很棒,但如果其中一部分也是免费如言论自由,那就更好了。
如何使用该服务
使用 wget 测试该服务非常容易,如下所示
clh@marseille:~$ wget -O - https://api.exchangerate-api.com/v4/latest/INR
--2019-04-26 13:48:23-- https://api.exchangerate-api.com/v4/latest/INR
Resolving api.exchangerate-api.com (api.exchangerate-api.com)... 2606:4700:20::681a:c80, 2606:4700:20::681a:d80, 104.26.13.128, ...
Connecting to api.exchangerate-api.com (api.exchangerate-api.com)|2606:4700:20::681a:c80|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/json]
Saving to: ‘STDOUT’
- [<=>
] 0 --.-KB/s {"base":"INR","date":"2019-04-26","time_last_updated":1556236800,"rates":{"INR":1,"AUD":0.020343,"BRL":0.056786,"CAD":0.019248,"CHF":0.014554,"CNY":0.096099,"CZK":0.329222,"DKK":0.095497,"EUR":0.012789,"GBP":0.011052,"HKD":0.111898,"HUF":4.118615,"IDR":199.61769,"ILS":0.051749,"ISK":1.741659,"JPY":1.595527,"KRW":16.553091,"MXN":0.272383,"MYR":0.058964,"NOK":0.123365,"NZD":0.02161,"PEN":0.047497,"PHP":0.744974,"PLN":0.054927,"RON":0.060923,"RUB":0.921808,"SAR":0.053562,"SEK":0.135226,"SGD":0.019442,"THB":0.457501,"TRY":0- [ <=> ] 579 --.-KB/s in 0s
2019-04-26 13:48:23 (15.5 MB/s) - written to stdout [579]
clh@marseille:~$
结果以 JSON 负载形式返回,提供从印度卢比(我在 URL 中请求的货币)到 ExchangeRate-API 处理的所有货币的兑换率。
Groovy shell 可以访问 API
clh@marseille:~$ groovysh
Groovy Shell (2.5.3, JVM: 1.8.0_212)
Type ':help' or ':h' for help.
----------------------------------------------------------------------------------------------------------------------------------
groovy:000> import groovy.json.JsonSlurper
===> groovy.json.JsonSlurper
groovy:000> result = (new JsonSlurper()).parse(
groovy:001> new InputStreamReader((new URL('https://api.exchangerate-api.com/v4/latest/INR')).newInputStream())
groovy:002> )
===> [base:INR, date:2019-04-26, time_last_updated:1556236800, rates:[INR:1, AUD:0.020343, BRL:0.056786, CAD:0.019248, CHF:0.014554, CNY:0.096099, CZK:0.329222, DKK:0.095497, EUR:0.012789, GBP:0.011052, HKD:0.111898, HUF:4.118615, IDR:199.61769, ILS:0.051749, ISK:1.741659, JPY:1.595527, KRW:16.553091, MXN:0.272383, MYR:0.058964, NOK:0.123365, NZD:0.02161, PEN:0.047497, PHP:0.744974, PLN:0.054927, RON:0.060923, RUB:0.921808, SAR:0.053562, SEK:0.135226, SGD:0.019442, THB:0.457501, TRY:0.084362, TWD:0.441385, USD:0.014255, ZAR:0.206271]]
groovy:000>
相同的 JSON 负载作为 Groovy JSON slurper 在 URL 上运行的结果返回。 当然,由于这是 Groovy,JSON 会被转换为 Map,因此您可以执行以下操作
groovy:000> println result.base
INR
===> null
groovy:000> println result.date
2019-04-26
===> null
groovy:000> println result.rates.CAD
0.019248
===> null
就是这样!
您是否使用 ExchangeRate-API 或类似的服务? 在评论中分享您如何使用汇率数据。
评论已关闭。