每周学习一门新技术》第一周——Nginx。

1. Nginx是什么

Nginx(发音为“engine x”)是一个高性能、轻量级的web服务器,反向代理服务器,邮件服务器。我们也可以用它作为代理服务器,实现负载均衡(LB,Load Balance)和高可用性(HA,High Availability)。它最初由俄罗斯人Igor Sysoev开发,发布于BSD许可证下。现在也有商业版Nginx Plus。

2. Nginx安装及控制

Nginx的安装参考官方文档:http://nginx.org/en/docs/install.html。不同OS,不同的Linux发行版中nginx的配置文件位置、控制等会有些许差异,下面的所有介绍都基于Ubuntu 14.04 LTS:

sudo apt-get install nginx

安装好后有以下几个重要的目录:

  • /etc/nginx:所有nginx配置相关的东西都在这个目录下,最常用的配置文件是/etc/nginx/nginx.conf
  • /var/log/nginx:nginx的所有日志都在这个目录下

安装好之后我们以root身份在命令行执行nginx遍可以启动nginx服务了(如果你的电脑上80端口被占用,可能会启动失败,请关闭使用80端口的进程后再启动):

root@Ubuntu:~# nginx
root@Ubuntu:~# ps -ef | grep nginx | grep -v grep
root      3656     1  0 10:54 ?        00:00:00 nginx: master process nginx
www-data  3657  3656  0 10:54 ?        00:00:00 nginx: worker process
www-data  3658  3656  0 10:54 ?        00:00:00 nginx: worker process
www-data  3659  3656  0 10:54 ?        00:00:00 nginx: worker process
www-data  3660  3656  0 10:54 ?        00:00:00 nginx: worker process

可以看到,nginx由一个master进程和若干个worker进程组成。这时我们访问http://localhost发现已经可以看到正常的nginx欢迎网页了,说明nginx已经在正常服务了。我们没有做任何配置但nginx就已经很好的在工作了,这是因为nginx默认已经配置好了一个简单的静态文件服务器,这个配置文件是/etc/nginx/sites-enabled/default至于它是如何工作的,我们下一节再介绍。至此安装完毕。

nginx提供了一些简单的命令行,我们可以通过这些命令行对nginx进行控制,下面我们介绍一下。

  • -?,-h:显示帮助
  • -v:显示版本号
  • -V:显示版本号,并显示一些nginx编译时的配置信息
  • -t:测试配置文件是否有错误
  • -q:关闭测试配置文件时的非错误信息的打印
  • -p prefix:set prefix path (default: /usr/share/nginx/)
  • -c filename:set configuration file (default: /etc/nginx/nginx.conf)
  • -g directives:set global directives out of configuration file
  • -s signal:向nginx进程发送信号,目前支持四个信号:

    • stop:快速停止
    • quit:让进程完成现在的工作,然后停止
    • reopen: 重新加载配置文件
    • reload:重新打开日志文件

一般我们最常用的就是-s这个命令行了。

3. nginx配置

我们先看一下配置目录的结构:

root@Ubuntu:~# ls -l /etc/nginx/
total 64
drwxr-xr-x 2 root root 4096 Oct 18 18:30 conf.d
-rw-r--r-- 1 root root  911 Mar  5  2014 fastcgi_params
-rw-r--r-- 1 root root 2258 Mar  5  2014 koi-utf
-rw-r--r-- 1 root root 1805 Mar  5  2014 koi-win
-rw-r--r-- 1 root root 2085 Mar  5  2014 mime.types
-rw-r--r-- 1 root root  222 Mar  5  2014 naxsi-ui.conf.1.4.1
-rw-r--r-- 1 root root  287 Mar  5  2014 naxsi.rules
-rw-r--r-- 1 root root 5287 Mar  5  2014 naxsi_core.rules
-rw-r--r-- 1 root root 1601 Mar  5  2014 nginx.conf
-rw-r--r-- 1 root root  180 Mar  5  2014 proxy_params
-rw-r--r-- 1 root root  465 Mar  5  2014 scgi_params
drwxr-xr-x 2 root root 4096 Oct 30 11:36 sites-available
drwxr-xr-x 2 root root 4096 Oct 27 17:57 sites-enabled
-rw-r--r-- 1 root root  532 Mar  5  2014 uwsgi_params
-rw-r--r-- 1 root root 3071 Mar  5  2014 win-utf

对于Linux比较熟悉的人一看conf.d就大概知道它是干嘛的了——没错,nginx启动的时候会加载这个目录下所有以.conf结尾的配置文件。

sites-availablesites-enabled这两个目录是用来配置你的网站的。一般sites-available目录下可能会有很多个配置,我们将要使用的配置文件做一个软链接到sites-enabled目录下,使其生效。安装好以后,我们看到有一个default配置文件。

目录下其他文件除nginx.conf以外的文件都是为一些特殊的进程指定的配置文件或是一些可选的配置文件,有兴趣或者需要的时候可以去官网了解。这部分剩下的内容我们主要介绍一下nginx.conf这个配置文件。先看一下默认配置(删掉了默认注释掉的配置):

root@Ubuntu:~# cat /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /run/nginx.pid;

events {
    worker_connections 768;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

nginx的配置是由一条条指令(directive)组成,每条指令以分号结尾。指令有一条一条写的,也有组成一个块(block)的。上面的例子中前面三条指令就是单独存在的,而后面就是两个块了。

  • user指明nginx工作(worker进程)的用户,在Ubuntu上面,默认为www-data用户。
  • worker_processes指定了worker进程的个数。
  • pid指明了存放master进程pid的文件。
  • event模块一般指定nginx的工作模式和连接数上限。
  • http模块是nginx配置中一个非常重要的块,在这个里面设定的所有指令对所有的server都有效。这里最后面有两个include,很多时候如果我们的配置不是很复杂的话,不需要使用include,可以直接将被include进来的文件里面的配置写在http块里面就好。

http块里面会有一个server块,用于设置每个server(虚拟机),这里面可以对每个server进行不同的配置。而server里面会有一个location块,主要是对资源的路由设置。我们看看默认的配置 /etc/nginx/sites-enabled/default(为了节省空间,删掉了注释):

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /usr/share/nginx/html;
    index index.html index.htm;

    # Make site accessible from http://localhost/
    server_name localhost;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
        # Uncomment to enable naxsi on this location
        # include /etc/nginx/naxsi.rules
    }
}
  • listen指定监听端口。
  • root指定网站的根目录。
  • index指定网站的首页——如果请求中没有指定访问哪个网页,就会寻找index.html这个页面,如果没找到,就寻找index.htm。
  • server_name指定域名,这里可以指定任意多个,中间用空格隔开。这里可以使用*作为通配符,比如*.example.com可以匹配任何以example.com结尾的域名。匹配url时,先精确匹配,如果匹配不到就使用了类似于贪心算法那样匹配。
  • location块用于指定处理资源请求的规则,里面支持正则表达式。匹配规则类似于server_name的匹配,但是更加复杂一下。详细的规则这里不再赘述,有兴趣的可以看一下官方文档,也可参考《nginx location匹配规则》。

这里我们简单介绍一个location里面的try_files指令。这个指令非常的有用,对于一个资源请求,我们可以用这个指令指定一系列的尝试。比如:

try_files $uri $uri/ /index.html;

这个指定的意思是当我们收到一个请求后,首先把这个请求的uri作为文件去解析;如果这个文件不存在,就把这个uri解析为一个目录,并且去这个目录下找index指定的默认文件;如果这个文件也不存在,就去网站根目录下(root指定的目录)找index指定的文件。

上面配置中的try_files $uri $uri/ =404;的解析与上面的例子相同,不过如果资源经过前两步的解析依旧找不到的话,并不会返回网站根目录下的index.html,而是会返回404错误。

综上,我们看到nginx的配置主要是httpserverlocation这三个指令块(directive block)的配置。

最后我们引用一下nginx.cn的一个例子(注:这个例子中的一些设置可能在新版本的nginx中已经不适用了,所以仅供参考。

#运行用户
user nobody;
#启动进程,通常设置成和cpu的数量相等
worker_processes  1;

#全局错误日志及PID文件
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

#工作模式及连接数上限
events {
    #epoll是多路复用IO(I/O Multiplexing)中的一种方式,
    #仅用于linux2.6以上内核,可以大大提高nginx的性能
    use   epoll; 

    #单个后台worker process进程的最大并发链接数    
    worker_connections  1024;

    # 并发总数是 worker_processes 和 worker_connections 的乘积
    # 即 max_clients = worker_processes * worker_connections
    # 在设置了反向代理的情况下,max_clients = worker_processes * worker_connections / 4  为什么
    # 为什么上面反向代理要除以4,应该说是一个经验值
    # 根据以上条件,正常情况下的Nginx Server可以应付的最大连接数为:4 * 8000 = 32000
    # worker_connections 值的设置跟物理内存大小有关
    # 因为并发受IO约束,max_clients的值须小于系统可以打开的最大文件数
    # 而系统可以打开的最大文件数和内存大小成正比,一般1GB内存的机器上可以打开的文件数大约是10万左右
    # 我们来看看360M内存的VPS可以打开的文件句柄数是多少:
    # $ cat /proc/sys/fs/file-max
    # 输出 34336
    # 32000 < 34336,即并发连接总数小于系统可以打开的文件句柄总数,这样就在操作系统可以承受的范围之内
    # 所以,worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置
    # 使得并发总数小于操作系统可以打开的最大文件数目
    # 其实质也就是根据主机的物理CPU和内存进行配置
    # 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需要消耗系统资源。
    # ulimit -SHn 65535

}


http {
    #设定mime类型,类型由mime.type文件定义
    include    mime.types;
    default_type  application/octet-stream;
    #设定日志格式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/access.log  main;

    #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,
    #对于普通应用,必须设为 on,
    #如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,
    #以平衡磁盘与网络I/O处理速度,降低系统的uptime.
    sendfile     on;
    #tcp_nopush     on;

    #连接超时时间
    #keepalive_timeout  0;
    keepalive_timeout  65;
    tcp_nodelay     on;

    #开启gzip压缩
    gzip  on;
    gzip_disable "MSIE [1-6].";

    #设定请求缓冲
    client_header_buffer_size    128k;
    large_client_header_buffers  4 128k;


    #设定虚拟主机配置
    server {
        #侦听80端口
        listen    80;
        #定义使用 www.nginx.cn访问
        server_name  www.nginx.cn;

        #定义服务器的默认网站根目录位置
        root html;

        #设定本虚拟主机的访问日志
        access_log  logs/nginx.access.log  main;

        #默认请求
        location / {
            
            #定义首页索引文件的名称
            index index.php index.html index.htm;   

        }

        # 定义错误提示页面
        error_page   500 502 503 504 /50x.html;
        location = /50x.html {
        }

        #静态文件,nginx自己处理
        location ~ ^/(images|javascript|js|css|flash|media|static)/ {
            
            #过期30天,静态文件不怎么更新,过期可以设大一点,
            #如果频繁更新,则可以设置得小一点。
            expires 30d;
        }

        #PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置.
        location ~ .php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include fastcgi_params;
        }

        #禁止访问 .htxxx 文件
            location ~ /.ht {
            deny all;
        }

    }
}

4. Nginx实现负载均衡

Nginx除了可以作为Web服务器,反向代理服务器、邮件服务器外,还可以实现http/https的负载均衡(LB),使用和配置也非常的简单。这里举一个简单的例子:

http {
    upstream myapp1 {
        # round_robin;/least_conn;/ip_hash;
        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}

这里假设srv1~srv3三台Server运行着相同的应用程序,当外部请求到达myapp1时,它会根据一定的算法将这个请求分发给srv1~srv3中的某一台服务器。目前nginx支持三种算法:

  • round-robin:轮询算法。这个是默认使用的算法,简单的轮询分发。参数值为:round_robin
  • least-connected:最少连接算法。新的请求将分发给目前活动连接数最少的服务器。参数值为:least_conn;
  • ip-hash:IP哈希算法。根据客户端的IP计算这个请求该分发给哪一台服务器。这个算法可以保证同一个客户端的请求被分发给同一台服务器,除非这台服务器已经不可用。参数值为:ip_hash;

1.7.10版本里面又增加了一个新算法:

  • least_time:这个与least_conn;类似,不过它还会把平均响应延迟作为一个考量。

除了指明这些算法外,我们也可以为服务器指定权重,算法也会将这些权重值考虑在内。

    upstream myapp1 {
        server srv1.example.com weight=3;
        server srv2.example.com;
        server srv3.example.com;
    }

更多参数信息请参考:http://nginx.org/en/docs/http/ngx_http_upstream_module.html