一些公司,包括我们的客户,都是通过会员网络开发产品的。 例如,大型在线商店与送货服务集成在一起-您订购商品并很快收到包裹的跟踪号。 另一个例子-您与机票一起购买保险或Aeroexpress机票。
为此,使用了一个API,必须通过Gateway API将其发布给合作伙伴。 我们已经解决了这个问题。 本文将提供详细信息。
给定的:具有用户注册,接收信息等接口的生态系统和API门户。 我们需要制作一个方便可靠的网关API。 在此过程中,我们需要提供
- 报名
- API连接控制
- 监视用户如何使用最终系统
- 业务指标核算。

在本文中,我们将讨论我们在创建网关API方面的经验,在此期间,我们解决了以下任务:
- 用户认证
- 用户授权
- 修改原始请求,
- 请求代理
- 后处理响应。
API管理有两种类型:
1.标准,其工作原理如下。 在进行连接之前,用户先测试可能性,然后在其站点上付款并嵌入。 最常用于中小型企业。
2.大型B2B API管理,当公司首先做出有关连接的业务决策时,将成为具有合同义务的合作伙伴公司,然后再连接到API。 在解决所有手续后,公司可以进行测试访问,通过测试并进入销售。 但是,如果没有管理决策就无法连接。

我们的决定
在这一部分中,我们将讨论创建网关API。
创建的API网关最终用户是我们的客户合作伙伴。 对于他们每个人,我们已经有必要的合同。 我们只需要扩展功能,注意对网关的许可访问即可。 因此,需要受控的连接和控制过程。
当然,可以采用一些现成的解决方案来解决API管理任务,尤其是创建API网关。 例如,这可以是
Azure API Management 。 它不适合我们,因为在我们的情况下,我们已经有一个API门户和围绕它构建的巨大生态系统。 所有用户都已经注册,他们已经了解了在何处以及如何获得必要的信息。 API门户中已经存在必需的接口,我们只需要API网关即可。 实际上,我们开始开发它。
我们所谓的网关API是一种代理。 在这里,我们再次有选择-您可以编写代理,也可以选择现成的东西。 在这种情况下,我们走了第二条路,选择了nginx + Lua捆绑包。 怎么了 我们需要一个可靠的,经过测试的软件来支持扩展。 在实现之后,我们不想检查业务逻辑的正确性和代理的正确性。
任何Web服务器都有一个请求处理管道。 对于nginx,它看起来像这样:

(来自
GitHub Lua Nginx的图表)
我们的目标是在我们可以修改原始请求的时刻将其集成到该管道中。
我们想要创建一个透明的代理,以便请求在功能上保持原样。 我们仅控制对最终API的访问,我们帮助请求到达最终API。 如果请求不正确,则最终的API应该显示错误,而不是我们。 我们拒绝请求的唯一原因是由于缺乏对客户端的访问权限。
对于nginx,
Lua上已经存在
扩展名 。 Lua是一种脚本语言,非常轻巧且易于学习。 因此,我们使用Lua实现了必要的逻辑。
可以理解完成所有工作的Nginx配置(类似于应用程序路由)。 值得注意的是最后一个指令-post_action。
location /middleware { more_clear_input_headers Accept-Encoding; lua_need_request_body on; rewrite_by_lua_file 'middleware/rewrite.lua'; access_by_lua_file 'middleware/access.lua'; proxy_pass https://someurl.com; body_filter_by_lua_file 'middleware/body_filter.lua'; post_action /process_session; }
考虑一下此配置中发生的情况:
more_clear_input_headers-清除指令后指定的标头的值。
lua_need_request_body-控制是否在执行rewrite / access / access_by_lua指令之前读取请求的源主体。 默认情况下,nginx不读取客户端请求主体,并且如果您需要访问它,则应将此指令设置为on。
rewrite_by_lua_file-脚本的路径,描述了修改请求的逻辑
access_by_lua_file-脚本的路径,该脚本描述检查访问资源的逻辑。
proxy_pass-请求将被代理
到的 URL。
body_filter_by_lua_file-脚本的路径,它描述了在返回客户端之前过滤请求的逻辑。
最后,
post_action是正式未记录的指令,可在将响应提供给客户端后用于执行其他任何操作。
接下来,我们将依次描述如何解决问题。
授权/认证和请求修改
登入我们使用证书访问建立了授权和认证。 有一个根证书。 客户的每个新客户都会生成其个人证书,他可以使用该证书来访问API。 该证书在nginx设置服务器部分中配置。
ssl on; ssl_certificate /usr/local/openresty/nginx/ssl/cert.pem; ssl_certificate_key /usr/local/openresty/nginx/ssl/cert.pem; ssl_client_certificate /usr/local/openresty/nginx/ssl/ca.crt; ssl_verify_client on;
修改方式可能会产生一个公平的问题:如果我们突然想要将其与系统断开连接,该如何处理认证客户? 不要为所有其他客户端重新颁发证书。
因此,我们顺利完成了下一个任务-修改原始请求。 通常,原始客户请求对于最终系统无效。 任务之一是将缺少的部分添加到请求中以使其有效。 关键是每个客户丢失的数据是不同的。 我们知道客户会向我们提供证书,我们可以从中获取指纹并从数据库中提取必要的客户数据。
如果在某个时候您需要断开客户端与我们的服务的连接,则该客户端的数据将从数据库中消失,并且他将无法执行任何操作。
处理客户数据
我们需要确保解决方案的高可用性,尤其是我们如何获取客户数据。 困难在于该数据的来源是第三方服务,不能保证不间断且相当高的速度。
因此,我们需要确保客户数据的高可用性。 作为工具,我们选择了
Hazelcast ,它为我们提供了:
- 快速访问数据
- 能够组织多个节点的集群,并在不同的节点上复制数据。
我们采用了最简单的缓存交付策略:

最终系统的工作在会话框架内进行,并且最大数量有限制。 如果客户没有关闭会话,我们将必须这样做。
开放会话数据来自目标系统,最初在Lua端进行处理。 我们决定使用Hazelcast通过.NET编写器保存此数据。 然后,每隔一段时间,我们检查公开比赛的生命权并结束犯规。
从Lua和.NET访问Hazelcast
Lua上没有可与Hazelcast一起使用的客户端,但是Hazelcast具有REST API,我们决定使用它。 对于.NET,有一个
客户端 ,我们计划通过该
客户端访问.NET端的Hazelcast数据。 但事实确实如此。

通过REST保存数据并通过.NET客户端检索时,将使用不同的序列化器/反序列化器。 因此,不可能通过REST放置数据,而只能通过.NET客户端获取数据,反之亦然。
如果您有兴趣,我们将在另一篇文章中进一步讨论此问题。 扰流板-在shemka上。

记录与监控
我们通过.NET登录的企业标准是Serilog,所有日志最终都存储在Elasticsearch中,我们通过Kibana对其进行分析。 在这种情况下,我想做类似的事情。 发现的唯一一个在Elastic上与Elastic合作的
客户端在第一个需求上就失败了。 我们使用了Fluentd。
Fluentd是用于提供单个应用程序日志记录层的开源解决方案。 允许您从应用程序的不同层收集日志,然后将其转换为单个源。
网关API在K8S中工作,因此我们决定将fluentd的容器添加到相同的子类型,以将日志写入到现有的开放tcp端口fluentd。
我们还研究了如果他与Elasticsearch没有联系,流利的行为会如何。 在两天内,请求被连续发送到网关,日志被发送到fluentd,但是IP Elastic被禁止使用。 重新连接后,流利的文件绝对超过了Elastic中的所有日志。
结论
选择的实施方法使我们能够在短短2.5个月内向作战环境交付真正有效的产品。
如果您碰巧这样做了,我们建议您首先清楚地了解您要解决的问题以及已经拥有的资源。 注意与现有API管理系统集成的复杂性。
亲自了解您将要开发什么—仅请求处理的业务逻辑,或者整个代理(在本例中就是这种情况)。 请记住,您自己所做的一切都应在之后进行彻底测试。