/ code

生产环境下用nginx为Nodejs server减压

生产环境下,nodejs server随时需要应对访问量的突然变化,以及各种网络攻击,特别是如果你的网站需要服务端渲染,哪怕前端已经部署了cdn、slb,高达百倍的输出输入比,都会让你的服务器被陡增的访问量轻松击溃。

面对如此恶略的网络环境,有什么办法能够让nodejs server更加健壮呢?方法当然有很多,这里我先介绍一款必备的基本款,包您满意。

本文关注的主要问题是如何有效降低nodejs server的访问流量,也就是尽量把流量拦截在抵达nodejs server之前。让nodejs server更少地直接暴露给充满恶意的网络世界。

架构是在nodejs进程(express)之前加一层nginx。这里nginx可以处理绝大多数脏活儿累活儿:文件压缩、serve静态文件、caching、SSL(可选,根据架构)、负载均衡以及其他让客户端开心的事儿。这样就可以避免对nodejs进程不必要的访问,来让nodejs专心做好该做的事儿,别动不动就罢工。

这里贴一份我服务器的config代码做例子:

 http {
    proxy_cache_path  /var/cache/nginx levels=1:2 keys_zone=one:8m max_size=3000m inactive=600m;
    proxy_temp_path /var/tmp;
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    gzip on;
    gzip_comp_level 6;
    gzip_vary on;
    gzip_min_length  1000;
    gzip_proxied any;
    gzip_types text/plain text/html text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_buffers 16 8k;
 
    ssl_certificate /some/location/sillyfacesociety.com.bundle.crt;
    ssl_certificate_key /some/location/sillyfacesociety.com.key;
    ssl_protocols        SSLv3 TLSv1;
    ssl_ciphers HIGH:!aNULL:!MD5;

    upstream nodejs_upstream {
      server 127.0.0.1:61321;
      keepalive 64;
    }

    server {
      listen 80;
      listen 443 ssl;

      server_name yourdomain.com;
      return 301 $scheme://www.yourdomain.com$request_uri;
    }

    server {
        listen 80;
        listen 443 ssl;

        server_name www.yourdomain.com;

        error_page 502  /errors/502.html;

        location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) {
          root /usr/local/yourdomain/public;
          access_log off;
          expires max;
        }

        location /errors {
          internal;
          alias /usr/local/silly_face_society/node/public/errors;
        }

        location / {
          proxy_redirect off;
          proxy_set_header   X-Real-IP            $remote_addr;
          proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
          proxy_set_header   X-Forwarded-Proto $scheme;
          proxy_set_header   Host                   $http_host;
          proxy_set_header   X-NginX-Proxy    true;
          proxy_set_header   Connection "";
          proxy_http_version 1.1;
          proxy_cache one;
          proxy_cache_key sfs$request_uri$scheme;
          proxy_pass         http://nodejs_upstream;
        }
    }
}

这里upstream可以根据实际express开的端口来决定,可以有多个,因为我用了pm2,所以只暴露出一个端口。

Static file intercept

location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico)
{
  root /usr/local/yourdomain/public;
  access_log off;
  expires max;
}

这里将静态文件都通过nginx来serve,不需要将请求转发到nodejs进程。这里的expires参数根据具体需求场景来配置,比如设为2h。

Caching

nginx配置里面,proxy_cache_pathproxy_temp_path配置nginx自带的cache,nginx自带的cache是个十分简化版的cache策略:
也就是当nodejs server返回的http头中包含Cache-Control字段,nginx将据此做cache,在失效时间之前,nodejs将直接返回自己存储的数据,而不需要再去请求nodejs server。
当然有总比没有的好,应对大部分的场景可能也够了。如果你需要更高的吞吐性能,可能需要其他cache策略,如memcached/redis/varnish等等。

location配置中的字段proxy_cacheproxy_cache_key用来给nginx做md5,计算出来的key用于索引cache资源。

PS

当用express做后端的服务时,返回的Response中header是不包含Cache-Control字段的,这就需要我们加一个middleware:

const cacheMiddleware = (req, res, next) => {
    res.setHeader("Cache-Control", "public, max-age=#{seconds}");
    next();
}

PPS

不是所有的请求都能做缓存,例如多数post请求,server端不应该做缓存。所以,有条件地使用cacheMiddleware

GZIP

express当然是有能力做数据压缩的,不过这份差事nginx做起来会更加得心应手,最好还是留给nginx来做吧。

What's more

经过诸多整改措施之后,服务器感觉更加健壮了。然鹅,面对网络流氓的攻击,比如各大搜索引擎索要无度的爬啊爬的疯狂请求,并没有什么卵用,信不信可以分分钟给你爬瘫痪咯。关键你还不能说啥,也不好意思说啥,谁扑街谁丢脸啊。

那可咋办咧?解决办法也不少,最简单粗暴的就是加机器。
先配置好按需扩容,很方便也很便宜,算一下,没多少钱。
然后加一台防爆御用的机器,然后把所有的爬虫请求都转到这台,崩了就重启呗,至少不要晚上被一堆Warning叫起来加班。

生产环境下用nginx为Nodejs server减压
Share this