共计 9095 个字符,预计需要花费 23 分钟才能阅读完成。
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
然后打开浏览器访问:
如果能够正常播放,说明点播服务配置成功。
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 直播服务发布成功。