热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

为什么放弃clojure_将API网关服务从Clojure重写为Golang:AppsFlyer体验报告

为什么放弃clojure重要要点AppsFlyer每天使用微服务架构样式构建,处理近70亿个HTTP请求。包装所有前端服务的系统的入口点是称为API网关的关键任务&#

为什么放弃 clojure

重要要点

  • AppsFlyer每天使用微服务架构样式构建,处理近70亿个HTTP请求。 包装所有前端服务的系统的入口点是称为API网关的关键任务(非微型)服务。
  • 用AppsFlyer默认语言Clojure编写的原始API网关开始积累技术债务。
  • 对于新设计的API网关服务的建议,选择Golang作为与Clojure进行基准比较的语言。
  • 基准测试是通过NGINX(由Lua增强)以及Golang和Clojure进行的。 与Clojure相比,Go的吞吐量有所提高,因此被选择为实现的语言。
  • API网关现在已经以一种类型化的语言构建,这一事实使Golang的库支持和社区能够插入各种功能并更轻松地引入新技术。
  • 与以前相比,新部署的解决方案能够支持的流量成倍增长,而且随着流量和请求以10倍的规模增长,从前瞻性角度来看,这是很重要的。

AppsFlyer是领先的移动归因和营销分析平台,每天处理近70+亿个HTTP请求(每分钟约5000万个请求),并采用微服务架构样式构建。 包装所有前端服务的系统的入口点是称为API网关的关键任务(非微型)服务。 从本质上讲,这是将流量从客户路由到我们的后端服务的单点,从而为我们的客户实现了指数级的身份验证和授权,但同时也存在潜在的单点故障。

本文探讨了工程团队为何以及如何从基于Clojure的API网关实现迁移到基于Go的实现。

在API网关中积累技术债务

就像我们的API网关服务一样,我们之前已经讨论过技术债务是如何产生的,并且发生了很多次。

最初,AppsFlyer的服务是Python整体,它需要一个完整的身份验证和授权解决方案作为整体的一部分。 随着时间的流逝,流量和复杂性都在增加,我们迁移到了微服务架构。 因此,我们需要创建一个统一的API网关解决方案,以用作我们的身份验证和授权提供程序。

我们从卷起袖子并用Clojure编写代码开始,跳过设计阶段,并在很大程度上以概念验证模式构建服务。 我们公司是欧洲,中东和非洲地区生产中最大的Clojure商店之一,因此,默认情况下,Clojure在很多情况下都是选择的语言,而无需考虑更多具体项目。 尽管这对于提高速度和“完成工作”的心态是有益的,但对于项目的长期维护而言,它并不是理想的选择。 随着流量的增长,我们很快意识到-新推出的API网关的代码过于复杂,需要不断进行重构以实现所需的吞吐量。

最终,我们走到了一个十字路口,那里的服务太不稳定了,我们意识到我们需要完全重写项目-使用Clojure(但设计更好),或者也探索其他语言选择。 通过这次迭代,我们决定不接受认知偏见并恢复到Clojure舒适区,而是进行构建所需服务所需的适当设计工作,而不仅仅是重新设计已有的服务。

我们最终选择了Golang作为该API网关服务的Clojure基准测试语言,该语言还带来了语言多样性的额外好处,并通过掌握更多语法提高了我们的代码制作技巧。

我们了解了在堆栈中添加另一种编程语言的另一面。 我们坚信CI / CD的心态,并引入了一种新的语言,这种语言不是基于JVM的(与Clojure相对),但它的运行成本很高,但是我们能够在短时间内解决。

当然,还需要学习掌握新语言的曲线,并且需要确保代码在长期内足够出色和健壮,这在实际用特定语言编写第一个项目之前很难知道并查看其在生产中的表现。

我将简要介绍为什么我们选择Go来进行这项特定服务-仅出于某些情况。 Go对构建网络服务,特别是对带有内置反向代理的类代理服务的支持非常强。 与其他解决方案(例如我们在Clojure中使用的http-kit)相比,它的最大优势是能够通过代理流式传输数据,而不是将其存储在内存中,并仅在收到最后一个字节后才将其返回给客户端。从服务器。 此功能与对有效I / O的支持一起,而无需付出我们必须在JVM等其他平台上编写的过于复杂的异步代码的代价,这使得Go的选择非常引人注目。 当我们开始实现服务时,一个明显的优势就是,静态类型的语言使重构代码及其原因变得容易得多,因为类型是自记录代码的绝佳方法。

评估我们的选择

我们知道,要能够正确评估不同语言的适用性,我们需要检查几个方面-每种语言在执行特定任务时的性能以及特定优势。 为了衡量性能,我们知道我们需要在尽可能接近生产模拟的情况下适当地对Clojure vs. Go进行基准测试。

为此,我们首先进行了压力测试,并选择了NGINX(由Lua增强),Golang和Clojure。 与Clojure相比,Go可以提高吞吐量。

测试的基本统计数据:

  • 我们使用WRK作为基准测试工具
  • 3分钟连拍
  • 64个线程
  • 1000个连接池
  • 2分钟的请求超时
  • 每个请求都返回一个重为500kb的静态文件
  • 使用c4 xlarge实例从同一可用区触发所有流量以减轻网络噪声

代理解决方案

要求/秒

反/秒

要求总数

总交易规模

错误要求

平均 潜伏

直接

190

72兆字节

34500

12.8 GB

〜400(掉落:200)

4.41秒

NGINX

185

73兆字节

33486

12.7 GB

〜300(下降:37)

7.95秒

Clojure(基本Http-Kit实现)

190

72兆字节

34412

12.8 GB

〜100(掉落:600)

8.48秒

Golang(本地反向代理和http层)

185

73兆字节

33443

12.7 GB

〜200(下降:0)

5.42秒

我们不再使用Clojure重写服务,这不仅是因为Go表现出更好的性能,而且还因为我们想挑战自己,并暴露于不同的语言和不同的思维方式。

设计阶段从概述我们要求服务具有的功能开始,并在指定了基本概念之后,我们在向生产用户群迁移到新服务的过程中研究了向后兼容性考虑因素和潜在的陷阱。 一旦我们确保涵盖了所有基础,便开始为项目分配建筑师和开发人员,开始工作。

从概念到交付

我们对项目的编码部分完成了如此Swift的工作感到惊讶,只需大约两个月的工作。 因为这是我们内部首次引入Go,所以我们在项目的编码部分非常小心。 我们对每个函数进行了两次迭代以确保我们做得正确,并进行了许多代码审查。 这是因为我们知道该代码必须精巧和简洁,因为它将作为其他Go项目的源代码。

尽管这是Go中引入的第一个项目,我们还是有机会真正掌握了该语言及其核心功能,因为我们不得不补偿Clojure中用于与堆栈的其他部分(包括Redis)进行通信的库。用户登录计数器的状态,以防止DDoS和僵尸程序)和Kafka(我们管理域事件的CQRS,其中之一是成功或失败的登录),这需要在Go中创建类似的库。

为了匹配Clojure中的生态系统,我们需要集成各种库,例如指标收集库,日志记录库,JWT库等,我们很高兴能找到所有成熟的库级别,这强烈表明社区对Go语言的采用程度-这是在决定迁移到新语言时的重要考虑因素。 它的社区可持续性和成熟度在这一决定中起着重要作用。

大约两个月后,我们就为基本迁移做好了准备,并涵盖并测试了基本功能。 我们开始以受控方式在父组(我们的域组)内迭代地迁移服务到新的API网关,该API网关基本上是一个Canary版本。

我们决定在最初的几周内对前几项服务进行有控制的部署,以便我们能够发现生产中的错误和缺陷,并有时间在推出我们的所有服务之前进行适当的修复。 我们想从原始API解决方案过快迁移的错误中汲取教训,最终导致交付质量低下。

一旦我们准备好并修复了所有缺陷,便开始了所有服务的迁移计划。 其中包括每个服务的迁移指南PDF,其中包括转移到新服务所需的确切步骤,此举所包含的好处以及根据其特定堆栈和依赖关系执行迁移的最佳方法。

为了逐步推出新的反向代理,我们使用了应用程序负载平衡器(ALB)根据一组预定义的URL路由流量,这些URL指示我们希望通过新的API网关与旧的API网关公开的服务之一。

这为如何以最小的工作量和最小的风险路由流量提供了一种非常可控的方法。 我们花了时间,测试了每个迁移的服务,并与负责其面向用户服务的所有其他团队携手合作。 我们花了六个月的时间,但我们设法迁移了约40个微服务,以使用零停机时间的新API网关。

结果

最终结果使我们能够将运行Clojure代码的25个实例(c4xlarge)(能够处理60个并发请求)减少到运行Go代码的两个实例(c3.2xlarge),每分钟能够支持约5000个并发请求,这是一个巨大的进步。 新的体系结构设计还为我们的下一阶段发展提供了足够强大的解决方案,它为我们提供了既可以承受大规模的强大服务,又可以凭借其过程方法轻松地实现业务复杂性的增长,而且还为我们的工具箱增加了新的语言当处理高规模时。

让我们以Clojure和Go中的反向代理解决方案为例。

Clojure:

;; Creating a connection manager
(let [cm (clj-http.conn-mgr/make-reusable-conn-manager {:timeout 1 :threads 20 :default-per-route 10})])
;; Creating a proxy server using cm (connection manager)
(client/request {:method :get
:url (service/service-uri service-spec uri-match)
:headers (dissoc (into {} (:headers req)) “content-length”)
:body (when-let [len (get-in req [:headers “content-length”])]
(bs/to-byte-array (:body req)))
:follow-redirects false
:throw-exceptions false
:connection-manager cm
:as :stream}))

在Golang中:

func NewProxy(spec *serviceSpec.ServiceSpec, director func(*http.Request), respDirector func(*http.Response) error, dialTimeout, dialKAlive, transTLSHTimeout, transRHTimeout time.Duration) *MultiReverseProxy {
return &MultiReverseProxy{
proxy: &httputil.ReverseProxy{
Director: director, //Request director function
ModifyResponse: respDirector,
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: dialTimeout, //limits the time spent establishing a TCP connection (if a new one is needed).
KeepAlive: dialKAlive, //limits idle keep a live connection.
}).Dial,
TLSHandshakeTimeout: transTLSHTimeout, //limits the time spent performing the TLS handshake.
ResponseHeaderTimeout: transRHTimeout, //limits the time spent reading the headers of the response.
},
},

请注意,Golang如何具有许多旨在更好地管理连接池和归入其核心类的反向代理功能的功能。

综上所述

选择在Go中编写新版本的API网关已被证明是一个非常好的决定。 Go的最小学习曲线使其成为在进行真正的生产服务时“实时”学习的出色语言。 它对诸如反向代理之类的低级网络结构的支持以及对性能的一般观念,使得最终结果既是真正可衡量的改进,也是更可靠的解决方案。 由于之前的代码而导致的所有生产问题现在都已过时,向网关添加新功能要容易得多,而且我们现在可以支持的增加的流量使我们所有人在晚上都能睡得更好。

本文已于2019年2月15日更新,以澄清意见讨论中提出的几点要点。

翻译自: https://www.infoq.com/articles/api-gateway-clojure-golang/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

为什么放弃 clojure



推荐阅读
  • 我的读书清单(持续更新)201705311.《一千零一夜》2006(四五年级)2.《中华上下五千年》2008(初一)3.《鲁滨孙漂流记》2008(初二)4.《钢铁是怎样炼成的》20 ... [详细]
  • This article explores the process of integrating Promises into Ext Ajax calls for a more functional programming approach, along with detailed steps on testing these asynchronous operations. ... [详细]
  • 优雅地记录API调用时长
    本文旨在探讨如何高效且优雅地记录API接口的调用时长,通过实际案例和代码示例,帮助开发者理解并实施这一技术,提高系统的可观测性和调试效率。 ... [详细]
  • 本文介绍了进程的基本概念及其在操作系统中的重要性,探讨了进程与程序的区别,以及如何通过多进程实现并发和并行。文章还详细讲解了Python中的multiprocessing模块,包括Process类的使用方法、进程间的同步与异步调用、阻塞与非阻塞操作,并通过实例演示了进程池的应用。 ... [详细]
  • 我在尝试将组合框转换为具有自动完成功能时遇到了一个问题,即页面上的列表框也被转换成了自动完成下拉框,而不是保持原有的多选列表框形式。 ... [详细]
  • 2023年1月28日网络安全热点
    涵盖最新的网络安全动态,包括OpenSSH和WordPress的安全更新、VirtualBox提权漏洞、以及谷歌推出的新证书验证机制等内容。 ... [详细]
  • 在CentOS 7中部署Nginx并配置SSL证书
    本文详细介绍了如何在CentOS 7操作系统上安装Nginx服务器,并配置SSL证书以增强网站的安全性。适合初学者和中级用户参考。 ... [详细]
  • 本文探讨了当通过Nginx访问网站时出现504 Gateway Timeout错误的解决方案,特别是当请求处理时间超过30秒时的情况。文章提供了调整PHP-FPM配置的具体步骤,以延长请求超时时间。 ... [详细]
  • 告别jQuery:Bootstrap5即将全面脱离jQuery依赖及其他前端热点
    本文精选了2019年2月「前端大全」平台上的15篇热门文章,涵盖技术分享与资源推荐。关注前端大全,获取更多前沿信息,提升您的前端技能。 ... [详细]
  • 本文探讨了使用Python实现监控信息收集的方法,涵盖从基础的日志记录到复杂的系统运维解决方案,旨在帮助开发者和运维人员提升工作效率。 ... [详细]
  • 本文详细介绍了在Windows系统中如何配置Nginx以实现高效的缓存加速功能,包括关键的配置文件设置和示例代码。 ... [详细]
  • CRZ.im:一款极简的网址缩短服务及其安装指南
    本文介绍了一款名为CRZ.im的极简网址缩短服务,该服务采用PHP和SQLite开发,体积小巧,约10KB。本文还提供了详细的安装步骤,包括环境配置、域名解析及Nginx伪静态设置。 ... [详细]
  • 在Android应用开发中,当在MenuItem中通过app:actionLayout属性使用Switch控件时,可能会遇到空指针异常的问题。本文将探讨该问题的原因及解决方案。 ... [详细]
  • Mysqlcheck作为MySQL提供的一个实用工具,主要用于数据库表的维护工作,包括检查、分析、修复及优化等操作。本文将详细介绍如何使用Mysqlcheck工具,并提供一些实践建议。 ... [详细]
  • 一键LNMP配置SSL证书实现全站HTTPS访问
    许多网站搭建者选择了便捷的一键LNMP安装包,但在网站部署完成后,配置SSL证书以支持HTTPS访问是一个不可或缺的步骤。本文将详细介绍如何通过简单的步骤完成这一过程。 ... [详细]
author-avatar
手机用户2602919547
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有