ubuntu下Nginx详解及点播直播服务器搭建
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直播服务发布成功。