Production environment configuration and tuning
Production Environment Configuration and Optimization
Express, as one of the most popular web frameworks for Node.js, requires special attention to performance, security, and stability in production environments. Proper configuration and optimization can significantly improve application throughput, reduce response latency, and ensure system security.
Environment Variable Management
Production environments must use environment variables to manage sensitive configurations. The dotenv
package can conveniently load .env
files, but production environments should inject variables directly through system environment variables:
// config.js
const config = {
port: process.env.PORT || 3000,
dbUrl: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET,
nodeEnv: process.env.NODE_ENV || 'development'
}
Never hardcode sensitive information in the code or commit it to version control. For complex configurations, libraries like convict
can be used for validation:
const convict = require('convict');
const config = convict({
env: {
doc: "Application environment",
format: ["production", "development", "test"],
default: "development",
env: "NODE_ENV"
},
port: {
doc: "Service port",
format: "port",
default: 3000,
env: "PORT"
}
});
Performance Optimization
Enable Compression
Using the compression
middleware can significantly reduce response size:
const compression = require('compression');
app.use(compression({
level: 6, // Compression level 1-9
threshold: '1kb', // Only compress if larger than 1kb
filter: (req) => !req.headers['x-no-compression']
}));
Cluster Mode
Leverage multi-core CPUs by starting a cluster:
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
const app = require('./app');
app.listen(3000);
}
Caching Strategy
Set appropriate HTTP cache headers:
app.use((req, res, next) => {
if (req.path.match(/\.(jpg|png|css|js)$/)) {
res.set('Cache-Control', 'public, max-age=31536000');
}
next();
});
For API responses, use ETag:
app.set('etag', 'strong'); // Use strong ETag validation
Security Configuration
Helmet Middleware
Helmet provides various security-related HTTP headers:
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "trusted.cdn.com"]
}
},
hsts: {
maxAge: 63072000,
includeSubDomains: true,
preload: true
}
}));
Request Limiting
Prevent brute-force and DDoS attacks:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit 100 requests per IP
message: 'Too many requests, please try again later'
});
app.use('/api/', limiter);
Logging
Production environments require comprehensive access and error logs:
const morgan = require('morgan');
const fs = require('fs');
const path = require('path');
// Access logs
const accessLogStream = fs.createWriteStream(
path.join(__dirname, 'access.log'),
{ flags: 'a' }
);
app.use(morgan('combined', { stream: accessLogStream }));
// Error logs
process.on('uncaughtException', (err) => {
fs.appendFileSync('error.log', `${new Date().toISOString()} - ${err.stack}\n`);
process.exit(1);
});
For distributed systems, consider using more powerful logging libraries like Winston or Bunyan and integrate with log collection systems like ELK.
Database Optimization
Connection Pool Configuration
Proper database connection pool configuration is critical for performance:
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // Maximum connections
idleTimeoutMillis: 30000, // Idle connection timeout
connectionTimeoutMillis: 2000 // Connection timeout
});
Query Optimization
Be mindful of N+1 query issues when using ORMs:
// Bad practice - results in N+1 queries
const users = await User.findAll();
const userWithPosts = await Promise.all(users.map(user =>
user.getPosts()
));
// Good practice - eager load associations
const users = await User.findAll({
include: [{ model: Post }]
});
Process Management
Use process managers like PM2 to ensure automatic restarts on crashes:
pm2 start app.js -i max --name "api-server" --update-env
PM2 configuration example:
module.exports = {
apps: [{
name: 'api',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
max_memory_restart: '1G',
env: {
NODE_ENV: 'production'
}
}]
}
Monitoring and Alerts
Integrate health check endpoints:
app.get('/health', (req, res) => {
res.json({
status: 'UP',
uptime: process.uptime(),
memoryUsage: process.memoryUsage(),
dbStatus: checkDbConnection()
});
});
Use Prometheus for metrics collection:
const client = require('prom-client');
const collectDefaultMetrics = client.collectDefaultMetrics;
collectDefaultMetrics({ timeout: 5000 });
app.get('/metrics', async (req, res) => {
res.set('Content-Type', client.register.contentType);
res.end(await client.register.metrics());
});
Static Resource Optimization
Use CDN for static resource distribution:
app.use('/static', express.static('public', {
maxAge: '1y',
setHeaders: (res, path) => {
if (path.endsWith('.br')) {
res.set('Content-Encoding', 'br');
} else if (path.endsWith('.gz')) {
res.set('Content-Encoding', 'gzip');
}
}
}));
Pre-compress static files:
# Pre-compress with Brotli and Gzip
find public -type f -exec brotli -k {} \;
find public -type f -exec gzip -k {} \;
Error Handling
Production environments require graceful error handling:
// 404 handling
app.use((req, res, next) => {
res.status(404).json({
error: 'Not Found',
path: req.path
});
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: process.env.NODE_ENV === 'development' ?
err.message : 'Internal Server Error'
});
});
For async errors, use express-async-errors
:
require('express-async-errors');
app.get('/async', async (req, res) => {
throw new Error('Async error');
// No try/catch needed; middleware will catch it automatically
});
Deployment Optimization
Optimize Docker images for deployment:
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
USER node
EXPOSE 3000
CMD ["node", "app.js"]
Build optimization:
# Multi-stage build to reduce image size
docker build -t myapp .
# Use .dockerignore to exclude unnecessary files
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn