在單體架構(gòu)下,我們是如何做到每天處理超過(guò)2.5億筆交易的?
此标题通俗易懂,并符合中文的口语表达习惯。它直接点出了文章的核心内容,即Probo公司如何在单体架构下实现每天处理大量交易的目标。
你可能已经读过很多关于单体架构和微服务的文章。其中大多数建议在没有充分理由的情况下,尽量远离微服务。
但是微服务非常有吸引力,你可以在开发周期的早期就发现这些有说服力的理由,或者根本得不到。有很多文章讨论如何把单体应用拆分成微服务,不过很少有文章教你如何正确地构建单体应用。下面就是尝试解决这个问题。
背景:在 Probo,我们是一个由大约 30 名工程师组成的小团队,我们坚持一个基本原则:保持简单。因此,从第一天开始,我们就运行一个单体式的后端,并且现在已经进入了第四个年头。我们从 零笔交易到超过每天 2.5 亿笔交易 的旅程。
在这一旅程中帮助我们的事
代码的结构- 我们的代码结构受到领域驱动设计的启发。从一开始我们就确定了三个主要的限界领域,并围绕它们开发了代码。
- 简单来说——用户领域(比如个人资料、推荐等),交易领域(比如订单管理、交易管理等)和支付领域(用户余额、交易历史等)领域。
我们尽量让这些上下文之间的交互保持松散,大多数的数据库连接查询都在各自的上下文中进行。
我们用的代码架构非常基础,分层结构是这样的:表示层 -> 领域逻辑 -> 数据管理。
左侧:边界上下文 | 右侧:分层架构
- 单体架构中的服务概念,重点关注防止领域泄露。
- 如果需要的话,每个独立的服务通过服务间的方法与其他服务进行通信,而不会通过数据层进行通信。
左:Probo 应用拆分(分割)| 右:服务器和多个工人的横向扩展
- 在旅程的早期阶段,当所有任务都在一个进程中处理时,我们就遇到了性能问题。为解决这个问题,我们首先将后台任务从服务器进程中分离出来。
- 这样,服务器进程和各种工作进程现在可以独立扩展,并且不会相互影响。
我们使用 AWS 提供的 MySQL RDS 数据库实例
- 正如 Kailash Nadh 所提到的,“97.42% 的扩展瓶颈都源于数据库”。这确实是真的,正如 Kailash Nadh 所说。
- 专注基本功。“没有索引”或“索引错误”是我面试时最常得到的答案,当我问到停机原因时。
- 利用 RDS 的性能洞察帮助我们解决了多个长时间运行或昂贵的查询,并深入了解了事务。
- 投入时间学习数据库参数帮助我们更好地扩展数据库并避免停机。即使在使用 AWS RDS 这样的托管数据库时,这也是必不可少的。
- 我们积极做的一件事是在 Slack 上发布慢查询——这使我们保持警惕!
Slack上的慢速查询
- 使用数据库代理帮助我们横向扩展了应用程序,不再担忧连接限制。
- 在团队层面,每个人都可以访问数据库监控仪表盘,这确实帮助大家共同理解问题。我见过很多组织仅限DevOps或DBA团队访问数据库,我认为这限制了开发人员理解并编写优化代码。
- 最初,我们开始在缓存层进行投资,以减少数据库的压力并提高API响应时间。
- 这套系统很快扩展,解决了其他用例,例如排行榜、登录认证、实时通信、推送通知、图表等。
- 将我们的高吞吐量端点隔离,帮助我们实现水平扩展,并让我们更快地找到并解决这些问题。
- 举例来说,当我们将通知发送给用户时,我们会更新我们端的已读/查看状态。这是一种突然增加的流量,会对其他 API 的性能产生影响。识别这些 API 并将其重定向到自己的专用服务器中,有助于我们避免性能下降。
路由器向它们自己的自动伸缩组发送一系列 API 调用。
语言特定的调整- 无论你选择哪种语言,总有一些基本的配置调整是需要做的,以便能够高效扩展。
- 我们主要使用Node.js,并且由于它是单线程的,我们使用PM2以集群模式运行它,以高效利用计算资源。
- 还有一些其他的配置,例如
**--max-old-space-size=SIZE**
(来设置V8旧内存空间的最大大小),**--prof**
(用于性能分析)也被设置。
告警
- 我们花了相当长的时间来正确设置我们的可观测性。虽然不能说是完美的,但确实完成了任务。
- 我们的日志基础设施是自托管的,使用的是简单的Elasticsearch集群和Kibana。虽然看起来很简单,但扩展Elasticsearch一直是一个挑战。
- 最初,我们基础设施的规模是由于自身原因造成的,因为很多日志并没有实际用途。为了解决这个问题,我们创建了一个简单的库——请参阅这篇文章。
- 对于指标,我们使用了last9。它帮助我们节省了安装、维护和扩展的时间,专注于创建有效的仪表板。
- Last9的警报管理器帮助我们实现了各团队间的警报民主化。尽管有单体架构的限制,每个团队都拥有自己的指标和警报。
部署时间显著增加了
- CI 管道测试及检查
- 滚动部署,因此部署时间与实例数量成正比,所需时间与实例数量成正比。
总是担心一次变更可能导致整个服务宕掉。因此在部署频率或高流量时段等方面的担忧增加。
多个依赖关系和资源调整- 数据库和缓存等依赖随着应用场景的增多而不断增加,增加了系统停机的风险。横向扩展也会给数据库连接等资源带来压力,导致资源配置不恰当。这时,代理和池化技术可以来帮忙!
总之,采取正确的实践和考虑后,单体架构也可以扩展以处理高交易量,是可能的。
我们一直努力保持代码库简洁且结构化,尽管遇到了一些挑战,我们还是找到了适合我们的解决方案。这段旅程既充实又有教育意义,我们希望我们的经验能为面临类似扩展挑战的其他人提供有用的参考。随着规模的扩大,我们在 Probo 经常有机会重新评估我们的架构。敬请期待!
如果你喜欢这类内容,那你就是我们中的一分子——一个喜欢解决技术难题的人。我们每天都在构建高性能的系统并每天都面对复杂的工程问题。
如果你对此感兴趣,我们可以聊一聊。请通过 adityachowdhry@probo.in 给我发邮件哦。 — 我很希望能和你联系一下。
共同學(xué)習(xí),寫下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章