从 PHP 到 Go:我们是如何将 API 速度提升 8 倍

从PHP到Go:我们是如何将API速度提升8倍

最近,我们把人脸识别API架构所使用的PHP升级到了Go语言。在本文中,我们分享了我们做出此举的原因,它如何从根本上改善了性能以及在此过程中获得的一些教训。

预备,开始!

2017年,在我们的#DiversityRecognition演示成为一种互联网现象之后,我们的流量有了巨大的持续增长,突如其来的增长量提醒我们注意迄今为止,从未考虑过的性能问题,以及随后支持超高使用率应用的成本。我们开始想做些改变,而这次活动将这一改变推到了我们榜首的位置。

在研究了各种选项(包括迁移到PHP 7,Java和其他语言)之后,我们决定迁移到纯Go和C解决方案-这对我们来说是最有效率的。考虑到我们最初的面部识别API体系结构是用PHP,Python和C组合编写的,所以简化代码库是一个主要目标。

根据我们的敏捷哲学,我们从小规模开始,用Go重写了PHP部分应用,而不是从完整的API开始。这种方法意味着我们可以在进行过程中应对任何意外的挑战,保持我们的代码高效,并收集用户的快速反馈。以我的经验,选择编程语言是一回事。最终,如何编写程序同样重要(甚至更重要)。

“随着多线程之间Go效率的提高,部署规模的减小,内存占用的减少以及运行更少的Docker容器的整体使用,我们能够将Kubernetes集群中的主机数量减少50%以上。”  -Kairos首席技术官Cole Calistra。

升级到Go的好处

正如我上面提到的,虽然桌上还有其他选择,但是,就以下关键方面而言,Go代表了最大的净赢球(最优解):

速度

通过我们的PHP构建,客户需要花费1-2秒的API时间。在迁移到Go时,相同的请求现在不超过300毫秒。值得注意的是,一些较低级别的功能将在100ms以下。总体来说,自从使用Go以来,我们平均将速度提高了8倍。

易于部署

使用PHP,我们必须部署承载完整Linux发行版,Nginx安装和PHP-FPM的容器。容器还必须包括许多库和驱动程序,以将PHP连接到其他服务,例如MySQL和Redis。

使用Go,我们可以部署精简版的Linux安装程序Debian-Slim,并且只需要分发生成的单个Go可执行文件即可。我们在Go中有一个528MB的Docker PHP映像,一次能减少到267MB,另一个366MB的Docker映像也减少到了170MB。实质上将我们的内存占用量减少了近50%

对于单个容器来说,这似乎不是一个巨大的变化,因为许多机器可以处理运行中的数千兆字节的容器而不会出现问题。但是,在规模化并且试图将尽可能多的容器打包到每个主机中时,大小成为整体容量和拆分新容器的速度的一个因素。

多线程

我们利用Go的许多内置功能(包括通道和go例程)来更有效地充分利用每个容器的CPU和内存容量。

成本

随着多线程之间Go效率的提高,部署规模的减小,内存占用的减少以及运行更少的Docker容器的整体使用,我们能够将Kubernetes集群中的主机数量减少50%以上。

以前,我们运行了200多个PHP API容器来处理基准流量,这些流量在高峰负载时将达到600-700个容器。使用我们新的Go部署,我们运行了不到20个容器,这些容器在40-50个峰值左右达到最高负载,比我们在PHP API上看到的要高。

专业发展

我们的工程师都没有Go的开发经验,但是是其他多种语言的专家,包括PHP,Python,Ruby,Java,C ++和C。这为我们提供了学习如何在现实世界中进行开发的机会。对公司和我们的客户有重大影响的项目。它也有助于完成一项熟悉而重要的任务来推动我们的学习。这比创建简单的“ Hello,World!”更为可取。应用程序类型。我们需要在生产中动手。

持续学习是Kairos这里文化的核心宗旨。使我们的工程师对外界更具市场吸引力,这使Kairos成为一个更好的工作场所。如果我们将团队扎根于过时的技能,则对任何人都无所提升。更不用说,Go是一种非常适合销售的技能-根据PayScale的这项研究,它是需求量排名第二的语言。

Golang的广泛吸引力吸引了许多流行的开源项目,例如Docker和Kubernetes。在基础架构中实施了此类工具并从其编码最佳实践中学习后,我们继续从其他顶级工程师那里提高我们的技能和策略。

从PHP到Go:我们是如何将API速度提升8倍

我们沿途学到的东西

对于任何新的软件开发任务,希望您都在项目的整个生命周期中学习新的东西。以下是从我们离开PHP到Go中获得的一些经验教训:

使用Cgo和Go进行内存泄漏和垃圾回收

不要以为内存正在泄漏或哪些对象可能正在泄漏内存。对您的代码进行性能分析将为您提供答案。垃圾收集器负责除去周围闲置的任何未使用的对象,只要没有对垃圾收集器检查的对象的引用即可。如果确实存在对对象的引用,则垃圾无法触及它们。这包括在代码其他部分中引用的Cgo对象。

从Redis封送JSON对象的问题

这里的挑战是找到一种方法来捕获非常大的Redis结果,并方便地将其格式化为数据结构。我们的主要目标是效率(速度),并且正如我们发现的那样,标准JSON包对于我们的特定用例不可行。解决此问题的另一种方法是处理Redis发送给我们的RAW数据,以避免不必要的JSON处理。
在优化代码之前收集数据。使用Go的分析工具,可以节省时间

在优化代码之前收集数据,使用Go的分析工具,可以节省时间

如上所述,在没有适当上下文的情况下进行优化会浪费时间。您必须找出可能导致问题的代码瓶颈。剖析代码的作用,您将能够看到问题的根源或对此有一些了解。此过程会非常节省时间。

如果您是新的Go开发人员,建议经常分析您的代码。您做的越多,就越熟悉,您未来的Go项目将得到改善。

Go如何在内部管理内存

Go是一种垃圾回收语言,因此它具有一个运行时来为您管理内存分配。但是请注意,垃圾收集器不能替代编写良好且高效的代码。

转义分析(Go确定内存分配位置的方式)

Go具有称为“转义分析”的功能。它负责在编译时确定运行时将在哪里分配内存,即分配给堆栈还是堆。这很容易克服,特别是在处理大量数据分配的大型应用程序中。并发应用程序(例如基于REST的API)就是您最能体会到这种情况的示例。
重用对象,而不是重新分配一遍又一遍使用的对象。为了提高效率,请寻找机会重用先前分配的数据结构。如果没有,这些数据结构或对象将被重新分配,垃圾收集器将做更多的工作。

走向未来

如您所见,Kairos从PHP迁移到Go的经历并非没有挑战。但是,我们的集体学习以及项目的成功,意味着我们可以继续使用Go来为客户服务。

原文: PHP to Go: How we boosted API performance by 8X

本作品采用《CC 协议》,转载必须注明作者和本文链接
Reflection.
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!