Google Cloud Storage + Node.js 多图片读取请求加载缓慢问题排查求助
Hey there, let's break down what might be causing those slow image requests and walk through actionable fixes to get your service running smoothly for global users.
First: Address the Crashed ServiceImage Process
Looking at your PM2 process table, the ServiceImage process has restarted 13 times and only has 0 seconds of uptime. This is a critical red flag—every time the process crashes and restarts, Node.js has to reinitialize the app and re-establish connections to Google Cloud Storage (GCS). Requests hitting the service right after a restart will face massive delays while the app spins back up.
Start by checking the logs for this process to identify why it's crashing:
pm2 logs ServiceImage
Fixing these crashes should immediately resolve a large portion of your slow request issues.
Optimize Stream Handling in Your Code
Your current route code manually listens for data events to write to the response, which can lead to backpressure issues (when the response can't keep up with the GCS stream). This causes bottlenecks, especially under load. Instead, use Node.js's built-in pipe() method—it automatically handles backpressure and is far more efficient.
Update your route code:
router.get('/image/:imageId', async function (req, res) { try { const imageId = req.param('imageId'); const imageStream = cloudController.getImageFromBucket('fotos_perfil', imageId); // Set proper headers res.setHeader('Content-Type', 'image/jpg'); res.setHeader('Cache-Control', 'public, max-age=86400'); // Cache images for 1 day // Use pipe to handle stream efficiently imageStream.pipe(res); imageStream.on('error', (error) => { if (!res.headersSent) { res.status(400).send('Erro lendo a imagem'); } console.error('Image stream error:', error); }); } catch (err) { res.status(500).send('Internal Server Error'); console.error('Route error:', err); } })
You can also simplify your controller code—createReadStream() returns a stream immediately, so you don't need to wrap it in a Promise:
function getImageFromBucket(bucket, imageId) { return storage.bucket(bucket).file(imageId).createReadStream(); }
Leverage fast-crc32c for GCS Performance
Google mentions fast-crc32c because GCS uses CRC32C checksums for data integrity. The GCS Node.js SDK will automatically use this faster native implementation if it's installed, replacing the slower pure-JS version. Just install it as a dependency:
npm install fast-crc32c
No extra configuration needed—once installed, the SDK will use it to speed up stream operations to/from GCS.
Add Caching & CDN for Global Users
Since images rarely change, adding proper cache headers (like the Cache-Control header in the updated code above) lets browsers and intermediate caches store images locally, reducing repeat requests to your server and GCS.
For global users, consider adding Google Cloud CDN in front of your GCS bucket or Node.js server. CDNs cache content in edge locations worldwide, cutting down load times for users far from your server.
Final Action Plan
- Fix
ServiceImagecrashes using PM2 logs to identify root causes. - Refactor stream handling to use
pipe()instead of manualdatalisteners. - Install
fast-crc32cto optimize GCS stream performance. - Add cache headers to reduce unnecessary requests.
- Set up a CDN to serve images closer to global users.
内容的提问来源于stack exchange,提问作者Fernando Garcia

