本文要点

  • AppsFlyer每天处理超过700亿个HTTP请求,并且是使用微服务架构风格构建。系统的入口点是一个被称为API网关的关键任务(非微型)服务,它封装了所有前端服务。

  • 原先的API网关使用了AppsFlyer的默认语言Clojure,其技术债务开始增加。

  • 为获得有关新设计的API网关服务的建议,他们选择使用Golang作为对Clojure进行基准测试的语言。

  • 作为一个选项,基准测试是在NGINX (经过Lua增强)上进行的,同时使用了Golang和Clojure。Go比Clojure提供了更好的吞吐量,因此被选为首选的实现语言。

  • API网关现在是用类型化语言构建的,借助Golang的库支持和社区的力量,我们可以更轻松地添加各种功能及引入新技术。

  • 新部署的解决方案能够支持的流量比现在多得多——随着流量和请求以10倍的规模增长,从前瞻性的角度来看,这是非常重要的。

AppsFlyer是一个领先的移动归因和市场分析平台,每天处理近700亿次HTTP请求(大约每分钟处理5000万次请求),使用微服务架构风格构建。系统的入口点是一个被称为API网关的关键任务(非微型)服务,它封装了所有前端服务。本质上,就是将客户流量通过单点路由到我们的后端服务,这极大地简化了客户端的身份验证和授权,但同时也可能导致单点故障。

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

API网关的技术债务日益增加

我们之前已经讨论过技术债务是如何产生的,并且这种情况经常发生,就像我们的API网关服务那样。

最初,AppsFlyer的服务是一个Python单体,它需要一个身份验证和授权解决方案作为这个单体的一部分。随着时间的推移,流量和复杂性不断增加,我们迁移到了微服务架构。因此,我们需要创建一个统一的API网关解决方案,它将作为我们的身份验证和授权提供者。

我们稍微做了下准备,就开始用Clojure编写,跳过了设计阶段,以概念验证模式构建服务。我们公司是EMEA最大的Clojure工场之一,因此,在很多情况下,Clojure都是默认选择的语言,无需考虑手头的具体项目。虽然这有利于提高速度,符合“把事情做完”的心态,但对于项目的长期维护来说就不那么理想了。随着流量的增长,我们很快意识到——新推出的API网关的代码太复杂了,需要不断地重构以支持所需的吞吐量。

我们最终到了需要做出抉择的时候,服务太不稳定了,我们意识到,我们需要完全重写这个项目——要么用Clojure(但是要有更好的设计),要么探索其他语言选项。通过这次迭代,我们决定抛弃我们的认知偏见,不再回到我们的Clojure舒适区,而是做恰当的设计工作来构建我们需要的服务,而不仅仅是重新构建我们已经拥有的服务。

我们最终选择了Golang作为这个Clojure API网关服务的比较基准,这也额外带来了语言多样性的好处,而且,掌握额外的语法有益于我们的代码技艺心态。

我们理解了向堆栈中添加另一种编程语言的另一面。我们是CI/CD思想的忠实信徒,我们引入了一种新的语言,它不是基于JVM(与Clojure完全不同)的,这有其运维成本,但是我们能够在短时间内解决这个问题。

当然,掌握一门新语言也有学习曲线,而且需要确保代码长期来看足够优秀健壮,在使用特定的语言实际编写第一个项目,并看一下它在生产中的表现之前,这很难知道。

我将简要说明下为什么我们选择Go来提供这个特定的服务(仅供参考)。作为一种过程式异步语言,Go为我们提供了函数式编程(我们已经在内部使用)、面向对象功能和更好的扩展。它是一种类型化语言,使得维护变得更加容易,而且不需要重新发明轮子。尤其是对于我们打算重写的服务,它有一个得天独厚的优势,就是内置了一个经过实践证明的反向代理。作为同步语言,Clojure非常适合多线程和并发,而事实证明,这个特定的服务有很大的I/O开销。

评估我们的选项

我们知道,为了能够恰当地评估不同语言的适应性,我们需要进行几个方面的检查——每种语言的性能以及对于手头的具体任务所带来的特定好处。要测量性能,我们知道,我们需要在尽可能接近生产的环境中对Clojure和Go进行适当的基准测试。

为此,我们开始做压力测试,我们把NGINX(经过Lua增强)作为一个选项,同时使用了Golang和Clojure。Go比Clojure提供了更高的吞吐量。

以下是有关测试的基本统计数据:

  • 我们使用WRK作为我们的基准测试工具

  • 3分钟的突发流量

  • 64线程

  • 1000个连接池

  • 2分钟请求超时

  • 每个请求返回一个500kb的静态文件

  • 为了降低使用c4 xlarge实例的网络噪声,所有的流量都从同一个AZ发起

代理方案 每秒请求数 每秒事务量 总请求数 总事务量 错误请求 平均延迟
直连 190 72 MB 34500 12.8 GB ~ 400 (drop:200) 4.41 Sec
NGINX 185 73 MB 33486 12.7 GB ~ 300 (drop:37) 7.95 Sec
Clojure(基本Http-Kit 实现) 190 72 MB 34412 12.8 GB ~ 100 (drop:600) 8.48 Sec
Golang(原生反向代理\u0026amp;http层) 185 73 MB 33443 12.7 GB ~ 200 (drop: 0) 5.42 Sec

除了上述这些结果之外,从语法的角度来看,Go也提供了额外的好处,作为一种类型化语言,它容易更新和迭代,而且更容易通过现有的包和库进行扩展(许多东西不用从头开始写),从功能的角度来看,其内核内置的反向代理组件是一个重要的好处。

我们放弃使用Clojure重写服务,避免走捷径去复制过时的代码,践行类型化思维方式。

在设计阶段,首先列出服务需要提供的功能,在确定好基本概念之后,我们研究了在将我们的生产用户迁移到新服务时的向后兼容性问题和潜在陷阱。一旦我们能够确定我们已经覆盖了所有的主要部分,我们就开始为项目分配架构师和开发人员,并开始相关工作。

从概念到交付

我们很惊讶,这么快就完成了项目的一部分编码,大约只需要两个月的工作。因为这是我们第一次将Go引入内部,我们在进行项目这部分的编码时很小心。对于每个功能,我们做了两次迭代,以确保我们做对了,而且,我们还做了大量的代码评审。这是因为我们知道这段代码必须精雕细琢,干净利落,因为这将作为后续其他Go项目的一个基础。

尽管这是我们引入的第一个Go项目,但我们有机会好好掌握这门语言及其核心功能,因为我们必须弥补Clojure在与栈中其他部分如Redis(用户登录计数器的持久状态,预防DDoS和bot病毒)和Kafka(我们管理着一个域事件CQRS,其中一个是成功或不成功的登录)进行通信时所使用的库的不足,这需要我们在Go中创建类似的库。Clojure这种语言需要我们的应用程序编译为Java字节码,并且,作为一个先决条件,需要在我们作为部署目标的机器上运行JVM,而Go是一种静态编译语言,不需要在一台机器上运行一个VM。

为了实现开箱即用的功能,如CPU和内存使用情况指标、业务逻辑计数器等等——我们基本上需要从头开始编写整个栈,这使我们可以更快地深入到Golang的错综复杂之处。

在大约两个月之后,我们做好了基本的迁移准备,覆盖了基本的功能和测试。我们开始在父节点组(域组)中以可控的方式将服务迭代地迁移到新的API网关,基本上是一次金丝雀发布。

在这个过程的最初几个周里,我们决定以可控的方式推出第一批的几个服务,这样,我们就可以发现生产中的错误和缺陷,并且有时间在推出我们所有的服务之前修复它们。在从最初的API解决方案迁移时,由于速度过快而导致了错误,并最终导致了低质量交付,我们希望吸取教训。

一旦我们觉得自己已经准备好并修复了所有的缺陷,我们就开始了所有服务的迁移计划。这包括每个服务的迁移指南PDF,其中包括迁移到新服务所需的具体步骤,这一举措的好处,以及根据具体的栈和依赖的不同确定执行迁移的最优方法。

为了以循序渐进的方式推出一个新的反向代理,我们使用了一个应用程序负载均衡器(ALB)基于一组预定义的URL来进行流量路由,这些URL是我们想要通过新旧API网关暴露的服务。

这为我们如何以最小的努力和风险路由流量提供了一种非常可控的方法。我们把我们的时间用在了测试每个要迁移的服务以及与其他负责用户服务的团队的协作上。我们花了六个月的时间,但我们设法使大约40个微服务在零停机的情况下迁移到了新的API网关。

结果

最终的结果是,我们从25个运行Clojure代码的实例(c4 xlarge)——能够处理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\t:get                           :url\t(service/service-uri service-spec uri-match)                           :headers\t(dissoc (into {} (:headers req)) “content-length”)                           :body\t(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\t:stream}))

Golang中:

func NewProxy(spec *serviceSpec.ServiceSpec, director func(*http.Request), respDirector func(*http.Response) error, dialTimeout, dialKAlive, transTLSHTimeout, transRHTimeout time.Duration) *MultiReverseProxy {\treturn \u0026amp;MultiReverseProxy{\t\tproxy: \u0026amp;httputil.ReverseProxy{\t\t\tDirector:       director, //Request director function\t\t\tModifyResponse: respDirector,\t\t\tTransport: \u0026amp;http.Transport{\t\t\t\tDial: (\u0026amp;net.Dialer{\t\t\t\t\tTimeout:   dialTimeout, //limits the time spent establishing a TCP connection (if a new one is needed).\t\t\t\t\tKeepAlive: dialKAlive,  //limits idle keep a live connection.\t\t\t\t}).Dial,\t\t\t\tTLSHandshakeTimeout:   transTLSHTimeout, //limits the time spent performing the TLS handshake.\t\t\t\tResponseHeaderTimeout: transRHTimeout,   //limits the time spent reading the headers of the response.\t\t\t},\t\t},

请注意,Golang的许多特性都是为了更好地管理连接池以及将反向代理功能集成到其核心类中。

总结

事实是,使用类型化语言构建使我们能够借助Golang的库支持和社区的力量更轻松地插入各种功能并引入新技术。对我们来说,从性能和吞吐量的角度来看,最重要的部分是改善我们的客户体验。新部署的解决方案能够支持比现在多得多的流量——随着我们的流量和请求以10倍的规模增长,从前瞻性的角度来看,这是非常重要的。

关于作者

Asaf Yonay是AppsFlyer研发组经理,非常乐于接受管理和技术挑战,在其中加入人的元素,把它们变成成功的故事。Asaf坚信,可以通过定义流程帮助研发团队成长和发展,而又没有速度损失,他始终在迎接全栈的挑战——相信这可以让经理进化为领导者。他一直在初创企业中承担不同的角色,包括技术支持、QA和各种研发角色,使用Clojure、Golang、Node.js和Python构建可伸缩的、可靠的系统来支撑React和Angular服务,并使用Kafka、Aerospike和Neo4J处理大规模或复杂的业务逻辑状态。

查看英文原文:Rewriting an API Gateway Service from Clojure to Golang: AppsFlyer Experience Report

AppsFlyer将API网关服务从Clojure迁移到Golang相关推荐

  1. 美团技术:百亿规模API网关服务Shepherd的设计与实现

    在微服务架构下,服务拆分会让API的规模成倍增长,使用API网关来管理API逐渐成为一种趋势.美团统一API网关服务Shepherd就是在这种背景下应运而生,适用于美团业务且完全自研,用于替换传统的W ...

  2. 百亿规模API网关服务Shepherd的设计与实现

    在微服务架构下,服务拆分会让API的规模成倍增长,使用API网关来管理API逐渐成为一种趋势.美团统一API网关服务Shepherd就是在这种背景下应运而生,适用于美团业务且完全自研,用于替换传统的W ...

  3. SpringCloudZuul之API网关服务

    Spring Cloud 系列 eureka之服务治理完整过程搭建 eureka之高可用的注册中心 eureka之详解 ribbon之客户端负载均衡 ribbon之配置详解 Hystrix之服务容错保 ...

  4. Spring Cloud应用开发(五:API网关服务)

    1.使用Zuul构建API网关服务: 注:本服务涉及到3个工程,起作用分别如下: ms-spring-eureka-server工程:服务注册中心,端口为8761. ms-spring-eureka- ...

  5. spring cloud 入门系列六:使用Zuul 实现API网关服务

    通过前面几次的分享,我们了解了微服务架构的几个核心设施,通过这些组件我们可以搭建简单的微服务架构系统.比如通过Spring Cloud Eureka搭建高可用的服务注册中心并实现服务的注册和发现: 通 ...

  6. Spring Cloud Zuul API 网关服务

    API 网关是一个更为智能的应用服务器,它的定义类似于面向对象设计模式中的 Facade 模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤.它除了要实 ...

  7. Spring Cloud Netfilx Zuul : API网关服务

    Spring Cloud Zuul 是Spring Cloud Netflix 子项目的核心组件之一,可以作为微服务架构中的API网关使用,支持动态路由与过滤功能,本文将对其用法进行详细介绍. 1.Z ...

  8. API 网关服务:Spring Cloud Zuul(二):路由详解、Cookie 与头信息

    实践出于<Spring Cloud 微服务实战> - 瞿永超 著 路由详解 传统路由配置   传统路由配置就是在不依赖于服务发现机制的情况下,通过在配置文件中具体指定每个路由表达式与服务实 ...

  9. Spring Cloud——API网关服务:Spring Cloud Zuul

    API网关像是整个微服务框架系统的门面一样,所有的客户端访问都需要经过它来进行调度和过滤.它实现了请求路由.负载均衡.校验过滤等功能.zuul包含了hystrix.ribbon.acturator等重 ...

最新文章

  1. jQuery / jQuery mvc plugin
  2. 27道高频Spring面试题,你能答对几个?
  3. ((sizeof(n)+sizeof(int)-1)~(sizeof(int)-1))
  4. Linux启动多个Oracle实例
  5. Ubuntu 安装简单samba
  6. 五、开始学习Excel函数,效率快速提高
  7. Redis学习第三课:Redis Hash类型及操作
  8. java 移动其他窗口_移动窗口平均值不等
  9. linux系统下安装两个或多个tomcat
  10. mysql group by cube_SQL Server 之 GROUP BY、GROUPING SETS、ROLLUP、CUBE
  11. Oracle相同的一个SQL执行计划截然不同的解决方法
  12. 【22】基于java的电影院售票管理系统
  13. html重置怎么使用图片,路由器重置后怎么设置_路由器重置设置【图文教程】-太平洋IT百科...
  14. 网络营销十大要点秒懂
  15. 盛世五洲flash进入页 动画设计制作
  16. 武松大战蒋门神(35)
  17. AWS-Lambda 从传入的 S3 Event 里获取信息
  18. 用手机怎么在线生成一份简历?
  19. 一个星期快速自学java编程高级语言干货笔记 -实用类
  20. \033[0;30m 之超级终端的字体背景和颜色显示等

热门文章

  1. DT技术助力企业创新转型
  2. Jenkins进阶系列之——04Publish Over FTP Plugin插件
  3. [每日一题] 11gOCP 1z0-053 :2013-09-29 Flashback Data Archive ...................................6...
  4. ASP.NET 调味品:AJAX
  5. LINQ中的延迟查询特性
  6. 基于WebGL架构的3D可视化平台—三维设备管理(ThingJS实现楼宇设备管理3D可视化)...
  7. 细数我们身边的人工智能之最
  8. Springboot引用外部配置文件
  9. 怎么提高大表和小表的连接查询效率?
  10. leach协议matlab仿真代码