开发笔记

技术探索与开发经验分享

← 返回首页

使用 Rust 重写核心模块的性能优化之旅

公司的一项图片处理服务遇到了性能瓶颈——Node.js 实现的图片处理模块在高并发场景下 CPU 占用率持续 95%+,响应时间不断攀升。我们决定用 Rust 重写这个核心模块,看看能带来多大的提升。

背景

这个服务的核心功能是接收用户上传的图片,进行缩放、裁剪、水印、格式转换等一系列操作,最终输出到 CDN。流量高峰时每秒需要处理约 200 张图片,Node.js 版本的 P99 延迟到了 8 秒以上。

技术选型

为什么选择 Rust?

架构设计

我们采用 N-API(现用 napi-rs)将 Rust 编译为 Node.js 原生插件,主服务保持 Node.js 不变,仅替换图片处理核心:

// Rust 侧 - 图片处理核心
use napi_derive::napi;
use image::{DynamicImage, GenericImageView, ImageFormat};

#[napi]
pub fn process_image(input: Vec<u8>, options: String) -> Vec<u8> {
    let img = image::load_from_memory(&input).unwrap();
    let opts: Options = serde_json::from_str(&options).unwrap();

    let result = apply_operations(img, &opts);
    let mut output = Vec::new();
    result.write_to(&mut output, ImageFormat::WebP).unwrap();
    output
}

fn apply_operations(img: DynamicImage, opts: &Options) -> DynamicImage {
    let mut img = img;
    if let Some(w) = opts.width {
        img = img.resize_exact(w, opts.height.unwrap_or(w),
            image::imageops::FilterType::Lanczos3);
    }
    // ... 更多操作
    img
}

性能对比

经过一周的开发和测试,我们得到了令人振奋的结果:

指标              Node.js 版本    Rust 版本    提升倍数
----------------------------------------------
吞吐量 (ops/s)     180            2,160       12x
P99 延迟 (ms)     8,200           320         25x
CPU 占用率         95%             35%         2.7x
内存占用 (MB)      850             250         3.4x

遇到的挑战

当然,这个过程并非一帆风顺。我们遇到了几个主要问题:

经验总结

以下是我们从这次迁移中学到的几点经验:

  1. 先 profiling 再优化:用 perf / flamegraph 找出真正的热点,不要凭感觉优化
  2. 渐进式替换:通过特性开关逐步灰度,降低风险
  3. 充分的基准测试:在真实数据上做 benchmark,避免优化了 A 却拖慢了 B
  4. 不要过度优化:80% 的收益来自 20% 的代码,优先优化关键路径
Rust 的 slogan 是「性能、可靠性、生产力」。经过这次实践,我认为它在性能密集型任务中确实名副其实。如果你的 Node.js 服务也遇到了性能瓶颈,不妨试试用 Rust 作为加速器。