Klarna致力于帮助开发人员创建高质量和安全的服务。 供开发人员使用的工具之一是用于执行A / B测试的平台。 该系统最重要的组成部分是众多过程,对于每个传入请求,它们决定将请求发送至哪种测试类型(A或B)。 反过来,这确定了显示按钮的颜色,显示给用户的布局,甚至使用了哪个第三方程序包。 这些决定直接影响用户体验。

Klarna使用Node.js平台 该文章(我们今天发布的翻译)专门介绍了公司专家从优化服务性能的经验中学到的那些课程。
第1课:性能测试可以使您确信系统的速度不会随着每个发行版而降低
每个过程的性能都发挥着重要作用,因为这些过程在Klarna生态系统的关键决策路径中同步使用。 对于此类任务的通常性能要求是,对于99.9%的请求,必须延迟做出决定,其时间用一位数字表示。 为了确保系统不偏离这些要求,该公司开发了一种用于压力测试服务的输送机。
第2课:独立“整理”负载,您甚至可以在生产之前就确定问题
尽管在该平台投入生产的两年中,我们几乎没有发现任何性能问题,但测试清楚地表明了一些问题。 在测试的几分钟内,在中等稳定的接收请求水平下,处理请求的持续时间从正常值急剧增加到几秒钟。
有关处理请求所需时间的信息。 检测到某种问题我们决定,尽管这尚未在生产中发生,但这只是时间问题。 如果实际负载达到一定水平,我们可能会遇到类似的情况。 因此,决定应该对此问题进行调查。
第3课:长期的压力测试可以识别各种问题。 如果一切看起来不错,请尝试增加测试的持续时间。
另一点需要注意的是,在负载下工作2-3分钟后,我们的系统出现了问题。 首先,我们只运行了2分钟的测试。 并且只有在测试执行时间增加到10分钟时才看到问题。
第4课:不要忘记考虑域名请求的DNS解析名称所需的时间。 不要忽略缓存条目的生存期-这会严重破坏应用程序
通常,我们使用以下指标监控服务:每秒传入请求的数量,传入请求的处理持续时间,错误级别。 这为我们提供了很好的系统状态指示器,指示其中是否存在任何问题。
但是,这些指标在服务故障期间无法提供有价值的信息。 当出现问题时,您需要知道系统瓶颈在哪里。 在这种情况下,您需要监视Node.js运行时使用的资源。 显然,指示器的组成(在出现问题时会对其状态进行监视)包括处理器和内存的使用。 但是有时系统的速度并不取决于它们。 例如,在我们的案例中,处理器利用率水平很低。 关于内存消耗的水平也可以这样说。
决定Node.js项目性能的另一个资源是事件循环。 正如了解进程使用多少内存很重要一样,我们需要知道处理事件循环需要多少“任务”。 Node.js事件循环是在C ++ libuv库中实现的(
这是一个很好的视频)。 “任务”在本文中称为“活动请求”。 另一个重要指标是由Node.js进程使用的打开文件描述符或套接字表示的“活动句柄”数。 可以在libuv
文档中找到描述符类型的完整列表。 结果,如果测试使用30个连接,则可以预期系统将具有30个活动描述符。 表征活动请求数的指示器指示排队等待特定描述符的操作数。 这些操作是什么? 例如,读/写操作。 在
这里可以找到它们的完整列表。
在分析服务指标之后,我们意识到这里有些问题。 尽管活动描述符的数量符合我们的预期(在此测试中约为30),但活动请求的数量却过高,成千上万。
活动描述符和活动请求是的,我们还不知道队列中有什么类型的请求。 在将活动查询划分为类型后,情况有所改善。 也就是说,
UV_GETADDRINFO
查询非常引人注目。 它们是在Node.js尝试解析DNS名称时生成的。
为什么系统会生成这么多DNS名称解析请求? 原来,
我们正在尝试为每个传出消息解析主机名的
StatsD客户端。 应当注意,该客户端提供了缓存DNS查询结果的可能性,但是此处未考虑相应DNS记录的TTL。 结果被无限期地缓存。 结果,如果在客户端已经解析了相应的名称之后更新了记录,则他将永远不会知道它。 由于可以使用其他IP地址重新部署StatsD负载平衡器,并且我们不能强制重新启动服务以更新DNS缓存,因此这种不限时间使用缓存的方法不适合我们。
我们想到的解决方案是使用客户端外部的一种方法来缓存DNS查询。 通过运行DNS模块的“猴子补丁”,可以轻松做到这一点。 现在结果看起来比以前好多了。
有关处理请求所需时间的信息。 使用外部DNS缓存的结果第5课:以批处理模式执行I / O操作。 这样的操作,甚至是异步的,都是严重的资源消耗者。
解决上述问题后,我们打开了先前禁用的服务的某些功能,然后再次对其进行了测试。 特别是,我们包含了一个代码,该代码针对每个传入请求将消息发送到Kafka主题。 该测试再次显示了在较大时间间隔内观察到的响应时间(我们所说的是秒)的测量结果中的明显峰值。
有关处理请求所需时间的信息。 测试表明,形成答案所需的时间急剧增加这些结果恰好在我们测试之前包含的功能中指出了一个明显的问题。 特别是,我们面临这样一个事实,即向Kafka发送消息会花费太多时间。
有关为Kafka生成消息所需时间的信息我们决定在这里使用最简单的改进-将传出消息放入内存中的队列,并以批处理模式每秒传输这些消息。 通过再次运行测试,我们发现服务形成响应所需的时间有了明显的改善。
有关处理请求所需时间的信息。 组织批消息处理后的改进第6课:尝试对系统进行任何改进之前,请准备测试,其结果值得信赖
没有允许您获得可重现和统一结果的测试运行机制,就不可能进行上述优化服务性能的工作。 我们的测试系统的第一个版本没有给出统一的结果,因此我们不能依靠它来做出重要的决定。 通过投资创建可靠的测试系统,我们能够以不同的模式测试项目,并进行更正试验。 新的测试系统在很大程度上使我们充满信心,所获得的测试结果是真实的,而不是无处可寻的数字。
我们来谈谈用于组织测试的特定工具。
负载是由内部工具生成的,该工具可以轻松地在分布式模式下运行Locust。 通常,所有这些都归结为单个命令的执行,然后启动了负载生成器,将测试脚本传送给了它们,并收集了由Grafana控制面板可视化的结果。 相应的结果显示在深色背景图上的材料中。 这就是系统从客户端角度看待测试的方式。
被测服务在Datalog中提供测量信息。 此信息在此处以明亮背景的图表显示。
亲爱的读者们! 您使用哪些Node.js服务测试系统?
