阿里云主机折上折
  • 微信号
Current Site:Index > Production environment configuration and tuning

Production environment configuration and tuning

Author:Chuan Chen 阅读数:7850人阅读 分类: Node.js

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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.