10

1 Nginx简介

  Nginx(engine x)是一个高性能的HTTP服务器,也是一款轻量级的Web服务器,反向代理服务器及电子邮件IMAP/POP3/SMTP代理服务器。Nginx是由伊戈尔·赛索耶夫为站点Rambler.ru开发的。第一个公开版本发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示列配置文件和低系统资源的消耗而闻名。其特点是占用内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现比较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。

2 Nginx的特点

2.1 动静分离

  Nginx是一种轻量级,高性能,多进程的Web服务器,非常适合作为静态资源服务器使用,而动态的访问操作可以是用稳定的Apache,Tomcat及IIS等来实现。这里就以Nginx作为代理服务器的同时,也使用其作为静态资源服务器。
静态资源服务器通过绝对路径去访问,放在nginx服务器当中。
动态资源通过url拼接字符串的方式去访问例如tomcat服务器

2.2 均衡负载

2.2.1 Nginx的upstream目前支持以下几种方式的分配

  1).轮询(默认)
    每个请求按实际顺序逐一分配到不同的后端服务器,如果后端服务器dump掉,能自动剔除。
  2) weight(权重)
    为后台服务器指定轮询几率,weight和访问量成正比,让性能高的服务器承担更多的访问量。
  3).ip_hash
    每个请求按访问ip的hash结构分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
  4).fair(第三方)
    按后端服务器的响应时间来分配请求,响应时间快短的优先分配。
  5).url_hash(第三方)
    按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

2.2.2 Session问题

  当我们确定一系列负载的服务器后,如果你先访问到A服务器,下一次请求突然转到B服务器。这时候与A服务器建立的Session,传到B服务器肯定是无法响应的。下面我们来看一下解决方案:
  1).Session或凭据缓存到独立的服务器上
    将seesion保存到独立的服务器,缓存效率会比较高,但如果同时访问的服务器过多的话,可能导致session服务器无法负荷而宕机。
  2).Session或凭据保存数据库中
    保存到数据中,除了要控制Session的有效期,同时也加重了数据库的负担,所以最终转变为SQL Server负载均衡,涉及读,写,过期同步,处理起来会很复杂。
  3).nginx ip_hash保持同一IP的请求都是指定到固定的一台服务器
    通过nginx ip_hash负载保持对同一服务器的会话,这种方式最方便,最轻量。

2.2.3 文件的上传下载

  如果实现了均衡负载,除了Session问题,我们还会碰到文件的上传下载问题。文件不可能上传不同的服务器上,这样会导致下载不到对应文件的问题。下面来看一下解决方案:
  1).独立文件服务器
  2).文件压缩数据库
  两种方案都是常用的,我们来说一下文件压缩数据库,以前的方式都是将文件二进制压缩至关系型数据库,而现在NOSQL的流行,加上MongoDB处理文件又比较方便,所以文件压库又多了一种选择。毕竟文件服务器的效率和管理以及安全都不及数据库。

2.3 反向代理

  反向代理(Reverse Proxy)方式是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器;并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
  反向代理服务器:
  通常的代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定代理服务器,并将本来要直接发送到Web服务器上的http请求发送到代理服务器中。当一个代理服务器能够代理外部网络上的主机,访问内部网络时,这种代理服务的方式称为反向代理服务器。

2.4 单点故障

  某台节点服务器挂了,但是Nginx仍然会可能选中这个出故障的机器,然后就一直连接着是因为超时时间很长,具体多长不清楚,所以为了避免一直连接着,我们需要设置超时时间。
  用Keepalived搭建双Nginx server集群,防止单点故障

2.5.优化

2.5.1 高层的配置(nginx.conf)

worker_processes auto;  
worker_rlimit_nofile 100000;
worker_cpu_affinity 0001 0010 0100 1000 0001 0010 0100 1000;

worker_processes:
  定义了nginx对外提供web服务时的worker进程数。最优值取决于许多因素,包括(但不限于)CPU核的数量、存储数据的硬盘数量及负载模式。不能确定的时候,将其设置为可用的CPU内核数将是一个好的开始(设置为“auto”将尝试自动检测它)。

worker_rlimit_nofile:
  更改worker进程的最大打开文件数限制。如果没设置的话,这个值为操作系统的限制。设置后你的操作系统和Nginx可以处理比“ulimit -a”更多的文件,所以把这个值设高,这样nginx就不会有“too many open files”问题了。

worker_cpu_affinity:
  CPU亲和力配置,让不同的进程绑定不同的CPU,可以减少由于线程切换时CPU切换带来的缓存拷贝导致降低效率。

2.5.2 Events模块

  events模块中包含Nginx中所有处理连接的设置。

events {
worker_connections 2048;  
multi_accept on;  
use epoll;  
}

worker_connections:
  设置可由一个worker进程同时打开的最大连接数。如果设置了上面提到的worker_rlimit_nofile,我们可以将这个值设得很高。但是不能超过系统的可用socket连接数限制(~ 64K)。
multi_accept:
  告诉nginx收到一个新连接通知后接受尽可能多的连接。
use 设置用于复用客户端线程的轮询方法。如果你使用Linux 2.6+,你应该使用epoll。如果你使用FreeBSD,你应该使用kqueue。
(值得注意的是如果你不知道Nginx该使用哪种轮询方法的话,它会选择一个最适合你操作系统的)

2.5.3 HTTP 模块

  HTTP模块控制着Nginx http处理的所有核心特性。

http {  
server_tokens off;  
sendfile on;  
tcp_nopush on;  
tcp_nodelay on;  
...  
} 

server_tokens:
  并不会让nginx执行的速度更快,但它可以关闭在错误页面中的nginx版本数字,这样对于安全性是有好处的。
sendfile:
  可以让sendfile()发挥作用。sendfile()可以在磁盘和TCP socket之间互相拷贝数据(或任意两个文件描述符)。Pre-sendfile是传送数据之前在用户空间申请数据缓冲区。
之后用read()将数据从文件拷贝到这个缓冲区,write()将缓冲区数据写入网络。sendfile()是立即将数据从磁盘读到OS缓存。
因为这种拷贝是在内核完成的,sendfile()要比组合read()和write()以及打开关闭丢弃缓冲更加有效(更多有关于sendfile)。
tcp_nopush:
  告诉nginx在一个数据包里发送所有头文件,而不一个接一个的发送。
tcp_nodelay:
  告诉nginx不要缓存数据,而是一段一段的发送--当需要及时发送数据时,就应该给应用设置这个属性,这样发送一小块数据信息时就不能立即得到返回值。

access_log off;  
error_log /var/log/nginx/error.log crit; 

access_log:
  设置nginx是否将存储访问日志。关闭这个选项可以让读取磁盘IO操作更快(aka,YOLO)
error_log:
  告诉nginx只能记录严重的错误:

3 Nginx编译

  nginx依赖于pcre, openssl, zlib, nginx-rtmp-module。故在编译nginx之前必须下载先编译这些库。
  pcre是一个Perl库,包括perl兼容的正则表达式库。
  
  zlib提供数据压缩用的函式库。
  nginx-rtmp-module是nginx的rtmp流媒体服务拓展库,实现了rtmp,hls,dash的流媒体服务功能。下面是本人编写的nginx一键下载编译脚本,运行该脚步即可下载编译nginx:

#!/bin/bash
#indicate that this script execute by /bin/bash
# 本脚本用于自动下载nginx已经依赖库pcre, openssl, zlib, nginx-rtmp-module,并编译安装,安装时需要输入管理员账号权限
#zipexts=(".tar.gz" ".tar.bz2" ".tar.Z" ".tgz" ".tar" ".gz" ".bz2" ".Z" ".rar" ".zip")
#zipexts=(".tar.gz" ".tar.bz2" ".tar.Z" ".tgz" ".tar" ".gz" ".bz2" ".Z" ".rar" ".zip")
# 由于shell脚本的函数不能返回字符串,故设置全局变量以便记录dezip产生的返回值以及下downloazanddezip函数下载解压后的文件夹名称
extname=""
outputname=""

# 解压压缩包,并返回解压后的文件夹或文件名称
# param1:压缩包名称,必填项
# param2:解压到指定文件夹,可为空
# 返回值:无, 注,shell返回值只能为整形,不能为字符串
dezip(){
    outputname=$2
    extname=""
    exename="tar"
    unzipflag=""
    echo "param1:"$1
    # 检测压缩包类型,并使用对应的解压方式
    if expr match $1 ".*.tar.gz" != 0; then
        extname=".tar.gz"
        exename="tar"
        unzipflag="-zxvf"
        #echo $extname
    elif expr match $1 ".*.tar.bz2" != 0; then
        extname=".tar.bz2"
        exename="tar"
        unzipflag="-xjf"
        #echo $extname
    elif expr match $1 ".*.tar" != 0; then
        extname=".tar"
        exename="tar"
        unzipflag="-xvf"
    elif expr match $1 ".*.tgz" != 0; then
        extname=".tgz"
        exename="tar"
        unzipflag="-xvf"
    elif expr match $1 ".*.gz" != 0; then
        extname=".gz"
        exename="gzip" # 或gunzip
        unzipflag="-d"
    elif expr match $1 ".*.bz2" != 0; then
        extname=".bz2"
        exename="bzip2" # 或bunzip2
        unzipflag="-d"
    elif expr match $1 ".*.tar.Z" != 0; then
        extname=".tar.Z"
        exename="tar"
        unzipflag="-xZf"
    elif expr match $1 "*.Z" != 0; then
        extname=".Z"
        exename="uncompress"
        unzipflag=""
    elif expr match $1 ".*.rar" != 0; then
        extname=".rar"
        exename="unrar"
        unzipflag="e"
    elif expr match $1 ".*.zip" != 0; then
        extname=".zip"
        exename="unzip"
        unzipflag="-o"
    fi
    if [ ! -d $outputname ]; then
        outputname=${1%$extname}
        echo "outputname" $outputname
    fi
    #if [ ! -d $outputname ]; then
        #mkdir $outputname
        #echo $outputname
    #fi
    echo "$exename $unzipflag $1"
    # 解压文件
    $exename $unzipflag $1
    #return $extname
}
# 下载压缩包并解压
# param1:url
# param2:zip
# return: dezip filename
downloazanddezip()
{
    if [ -d $1 ]; then
       echo "Invalid url, please input url while you run downloazanddezip"
    fi
    downloadurl=$1
    # 截取?前面的字符串
    downloadurl=${downloadurl%\?*}
    echo $downloadurl
    # 截取最后一个/后面的字符串,即压缩包名称
    zipname=${downloadurl##*/}
    echo $zipname
    if [ ! -f $zipname ]; then
        curl -o $zipname $1
        echo 'curl -o $zipname $1'
    fi
    echo "dezip:"$zipname
    dezip $zipname
    #extname=$?
    echo $extname'=dezip' $zipname

    if expr match $zipname $extname == 0; then
        zipname=${zipname%$extname}
        echo "zipname:"$zipname
    fi
    outputname=$zipname
}

# 下载nginx源码,如果链接有误,直接替换链接即可
curdir=$PWD
downloazanddezip http://117.128.6.28/cache/nginx.org/download/nginx-1.14.2.tar.gz?ich_args2=468-23103716018527_5b6f2632bf91b3cdae35405cfb43338b_10001002_9c89612cdfcbf8d59538518939a83798_ff478ef610d500dd7f20fe13dd251d7b
# 记录nginx库源码文件夹目录名称
nginxname=$outputname
cd $outputname
if [ ! -d "./thirdpart/" ];then
    mkdir thirdpart
fi
cd ./thirdpart
# 下载pcre库
downloazanddezip https://ftp.pcre.org/pub/pcre/pcre-8.42.zip
# 记录pcre库源码文件夹目录名称
pcrename=$outputname
# 下载openssl
downloazanddezip https://www.openssl.org/source/openssl-1.0.2q.tar.gz
# 记录openssl库源码文件夹目录名称
opensslname=$outputname
# 下载zlib库
downloazanddezip http://117.128.6.17/cache/www.zlib.net/zlib-1.2.11.tar.gz?ich_args2=531-22232503018317_67f53fed3443a8995b0d31f74b74bce1_10001002_9c89612cdfc7f8d09639518939a83798_9b3b5eb8fa2f98e0b42e37f3838be97f
# 记录zlib库源码文件夹目录名称
zlibname=$outputname
# 从github下载nginx-rtmp-module源码
if [ ! -d "./nginx-rtmp-module/" ];then
    git clone https://github.com/arut/nginx-rtmp-module.git
fi
cd ../
make clean
./configure --prefix=$curdir/$nginxname/build  --with-openssl=$curdir/$nginxname/thirdpart/$opensslname --with-pcre=$curdir/$nginxname/thirdpart/$pcrename  --with-zlib=$curdir/$nginxname/thirdpart/$zlibname --add-module=$curdir/$nginxname/thirdpart/nginx-rtmp-module --with-http_ssl_module
make
make install

4 配置点播服务

4.1 修改nginx.conf

  打开build目录下conf文件夹下的nginx.conf文件:

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        # add stat page
        location /stat {
            rtmp_stat all;
        rtmp_stat_stylesheet stat.xsl;
        }
        location /stat.xsl {
            root $nginxpath/thirdpart/nginx-rtmp-module;
        }
        # add stat page end
        location / {
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    # 支持flv、mp4文件点播
    location ~* \.flv$ {#flv 支持
        root $flvpath;#flv点播文件目录
    }
    location ~* \.mp4$ {#MP4 支持
        root $mp4path;#mp4点播文件目录
    }
    }

4.2 测试点播服务

  运行nginx:

$sudo ./nginx -c $buildpath/conf/nginx.conf

  然后打开浏览器访问:

http://127.0.0.1/test.mp4
如果能够正常播放,说明点播服务配置成功。

5 配置直播服务

5.1 修改nginx.conf配置:

http {
    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        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        location /stat {
                rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }

        location /stat.xsl {
            root $nginxpath/third/nginx-rtmp-module;
        }
        location / {
            root   html;
            index  index.html index.htm;
        }
        location /live {  #这里也是需要添加的字段。
        types {  
            application/vnd.apple.mpegurl m3u8;  
            video/mp2t ts;  
        }
        alias /opt/video/hls;   
        expires -1;
        add_header Cache-Control no-cache;  
    }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    # 支持flv、mp4文件点播
    location ~* \.flv$ {#flv 支持
        root $flvpath;#flv点播文件目录
    }
    location ~* \.mp4$ {#MP4 支持
        root $mp4path;#mp4点播文件目录
    }
    }
}
# 增加rtmp服务
rtmp {
    server {
        listen 1935;
    chunk_size 4096;
    # rtmp点播
    application vod {
        play $rtmpvodpath;#rtmp点播文件存放路径
    }
    # rtmp直播
    application live {
    live on;
    hls on; #这个参数把直播服务器改造成实时回放服务器。
    wait_key on; #对视频切片进行保护,这样就不会产生马赛克了。
    hls_path $hls_save_path; #切片视频文件存放位置。
    hls_fragment 10s;     #每个视频切片的时长。
    hls_playlist_length 60s;  #总共可以回看的事件,这里设置的是1分钟。
    hls_continuous on; #连续模式。
    hls_cleanup on;    #对多余的切片进行删除。
    hls_nested on;     #嵌套模式。
    }
    }
}

5.2 测试rtmp直播服务

  安装ffmpeg,并使用ffmpeg进行rtmp推流

$ sudo apt-get install ffmpeg
$ ffmpeg -re -I $mp4path/test.mp4 -vcodec libx264 -acodec aac -strict -2 -f flv rtmp://localhost:1935/live
$ ffplay -I rtmp://localhost:1935/live

  若能正常播放rtmp流,则rtmp直播服务发布成功。


懒熊工作室
94 声望4 粉丝