规模化Rails开发实战:用Ruby编写业务逻辑,Go/C/Rust构建高性能组件

本文深入探讨如何在保持Ruby on Rails业务逻辑优势的同时,通过集成Go、C和Rust等高性能语言来优化系统性能,详细介绍imgproxy集成和云端文件处理等实际架构方案。

规模化Rails开发实战:用Ruby编写业务逻辑,Go/C/Rust构建高性能组件

Ruby on Rails擅长业务逻辑、开发者体验和快速迭代。但当性能瓶颈出现时,常见的建议是"用Go重写"、“拆分为微服务"或"解构单体应用”。但有一个更好的方法:通过优化工具链来扩展Rails应用,而不是改变核心架构。

TL;DR:保持你的蛋糕配方(业务逻辑)使用Ruby,但升级厨房工具为Go、C、Rust等高性能语言来实现快速混合和烘焙。永远不要为了速度而牺牲风味!

精准提取的艺术

Rails应用就像一个拥有活配方的面包店,你的业务逻辑通过反馈和创新不断优化。然而当订单涌入时,厨房开始不堪重负。解决方案不是用"更快"的语言重写配方,而是升级你的搅拌机和烤箱:识别重复的、CPU密集型的任务,并将它们委托给用Go、C、Rust等构建的专业工具。

实例一:imgproxy - 当厨房需要更快的图像变体处理

想象你的面包店突然被图片订单淹没:图库显示缓慢,图片上传超时,后台任务堆积。手动搅拌机(如Active Storage的.variant调用)无法跟上,消耗大量资源并阻塞其他厨房工作。

这时,你发现了imgproxy - 用Go构建的最快图像变体处理工具。但如何在不重新培训面包师或重写配方的情况下集成它?

架构洞察:在这种情况下,Rails已经有一个将工作委托给"更好搅拌机"的系统。当你调用image.variant(resize_to_limit: [300, 300])时,Rails创建一个变体对象,生成指向ActiveStorage::Representations::*Controller的签名URL。该控制器充当调度器:检查变体是否存在,根据需要处理它(使用配置的处理器),并提供或重定向到结果。

Rails使用libvips作为默认变体处理器,也可以使用ImageMagick。但整个流程(URL生成、处理委托和结果服务)都设计为适配器模式。我们只需要将这些转换指令路由到imgproxy,而不是进程内处理器。

关键点:配方保持纯净(检查权限、验证尺寸、做出业务决策),搅拌机从不查看用户上下文或业务规则 - 它只是遵循转换指令。

实际方案:我们已经构建了这个厨房升级!imgproxy-rails gem可以轻松替换你的搅拌机;安装它,通过配置连接,每个配方都会自动使用新设备。

1
2
3
4
5
6
7
8
# Gemfile
gem 'imgproxy-rails'

# config/production.rb(或任何你想要激活imgproxy的地方)
config.active_storage.resolve_model_to_route = :imgproxy_active_storage

# config/development.rb(在本地继续使用Rails处理)
config.active_storage.resolve_model_to_route = :rails_storage_proxy

你的面包师永远不会知道区别:相同的成分,相同的技术,只是结果更快。相同的视图代码现在将繁重的图像处理从Ruby委托给Go。

三个有效的增强原则

规则一:原生扩展Rails

优秀的性能助手应该插入Rails现有的抽象,而不是与之对抗。Rails提供了数十个扩展点,Active Record数据库适配器自然是最常用的,但还有更多。Active Storage、Cache Store、Session Store、Action Cable、Action Mailer、Active Job都是很好的例子。

规则二:工具不需要了解配方

性能助手应该是纯执行引擎,完全不了解你的业务配方。当它们开始对用户、权限、UX或领域逻辑做出决策时,你就从性能助手跨入了微服务领域。我们的高性能助手遵循指令;它们从不编写菜单。

规则三:规划维护

用外语构建和维护性能助手需要你可能不具备的团队专业知识。因此,尽可能依赖经过实战检验的开源解决方案来处理操作复杂性。自定义解决方案应该是你的最后手段,而不是第一本能。

TL;DR:最好的性能升级对你的团队来说是隐形的,对Rails来说是原生的。它们扩展机械工作而不触及使产品独特的创意过程。

实例二:Playbook的云端处理

自2021年以来,Evil Martians与Playbook合作,为全球70万+用户构建新一代创意云平台。在处理Playbook.com的项目时,我们没有在Rails内部处理文件(占用Web工作进程/内存),而是将计算密集型任务委托给Google Cloud服务:元数据解析、生成缩略图、AI识别,可靠性和可扩展性由GCP基础设施处理。

处理流程

  • 文件上传到Google Cloud Storage后,无服务器函数启动异步处理管道
  • 云函数启动Cloud Tasks作业,包含重试机制
  • 每个步骤独立处理:图像识别、调整大小、NSFW检查等
  • 结果写入Cloud Firestore
  • Rails通过计划的Sidekiq工作进程获取处理后的数据

模式:保持Rails用于面向用户的业务逻辑服务。所有繁重的工作都在进程外进行,由Google的无服务器骨干提供支持。

结果:可扩展、快速、有弹性的文件管道,不会拖累你的Ruby Web工作进程。

厨房升级示例

以下是一些遵循此模式的厨房升级示例:

Web服务器:Puma实现Rack::Handler,Falcon是基于Fiber的异步服务器,支持HTTP/2

存储适配器:ActiveStorage提供一系列适配器,包括ActiveStorage::Service,允许我们扩展经典的S3、GCP和Azure,以及Hetzner、Cloudflare R2、DigitalOcean Spaces等替代方案

文件处理:ActiveStorage::Variant可用于按需文件转换工具,如前述的imgproxy。ActiveStorage::Analyzer用于元数据提取(Poppler)。ActiveStorage::Previewer用于生成自定义预览(MuPDF)

后台作业:ActiveJob::QueueAdapters::*Adapter允许我们与替代作业队列集成,包括Sidekiq、GoodJob、Karafka、Temporal、Faktory等

邮件传递:ActionMailer::Base.delivery_method允许我们扩展经典的SMTP、sendmail、文件,以及Postmark、SendGrid、Amazon SES、Mailgun等替代方案

WebSocket适配器:我们正在领导Action Cable服务器适配器化的工作,以支持更多平台和技术

Rails面包店的优势

经过精心设计的现代Rails面包店可以为大量人群提供服务而不失其灵魂。Ruby引擎通过YJIT改进变得更快,而异步功能和Ruby Fiber Scheduler保持烤箱并行运行而不会使员工精疲力尽。性能优先的厨房助手解决瓶颈,智能缓存防止浪费,你的配方在Ruby中继续发展 - 敏捷、活跃、响应客户反馈。

配方驱动创新,同时,专业工具(用C、Go、Rust等编写)扩展机械工作。它们共同创造了快速、可维护且无限创意的面包店!

通过升级设备来扩展你的Rails面包店,永远不要通过重写配方。保持业务逻辑在Ruby中新鲜,用工业级工具升级你的厨房来完成繁重的工作。将它们结合起来,祝你好胃口!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计