【魄爺】 发表于 2018-11-9 08:36:07

nginx高效原理及源码编译安装(nginx-1.6.2)

  目录:
  1、nginx为何如此高效
  1.1、进程模型(master-worker)
  1.2、事件处理模型(异步非阻塞的事件处理机制)
  1.3、支持sendfile,提升文件传输性能
  1.4、支持AIO
  1.5、支持mmap
  1.6、小结:
  2、源码编译安装nginx
  1、nginx为何如此高效
  nginx特点:nginx是一个高度模块化,基于事件驱动,异步,单线程,非阻塞架构的软件。nginx为何如此高效?为何在相同的硬件资源下能承受比http大得多并发连接?现总结以下几点,本人技术有限,错误再所难免,若有错误请指正。
  1.1、进程模型(master-worker,单线程模型)
  nginx对http请求的处理方式和apache对请求的处理方式截然不同,nginx采用单线程、异步非阻塞的模型,nginx启用后,会有一个master进程和多个worker进程,master进程的主要功能是用来管理worker进程,包括接收外界的信息,向worker进程发送信号,监管worker进程的运行状态等,而worker进程则是真实用来处理网络事件的,多个worker进程间是相互对等的,对客户端的响应是拥有相同的响应等级,各个worker竞争对请求的响应,各个进程之间相互独立,互不影响,一个进程有异常退出后不会影响其他的进程运行,服务不会因此而中断。
  nginx在提供服务时,其内部发了什么呢?当提供一个80端口的http服务时,一个连接请求过来,每个worker进程都对这个请求拥有平等的处理权限,那是怎么挑选出一个worker进程来响应的呢?首先在master进程里向内核注册需要listen的socket(listenfd),再fork出各个worker进程,worker进程继承了master的所有属性(当然也包括已建立好的socket,注意这不是同一个socket,只是每个进程的这个socket会监听在同一个ip地址与端口,这个在网络协议里面是允许的),各个worker进程在注册listenfd读事件前抢accept_mutex(可以理解成加在accept上的一个互斥锁,只有得到这把锁后,worker进程注册listenfd读事件,在读事件里调用accept),抢到互斥锁后,worker就开始读取请求,解析请求,处理请求,得到数据后,再返回给客户端,最后才断开连接,这样一个完整的请求结束。可见,一个请求完全由worker进程来处理,且只是在一个worker进程里完成。
  nginx采用这种master与多个worker的进程模型的优点:
  1、节省锁带来的开销。每个worker进程相互独立,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题排查上也会方便很多;
  2、独立进程工作,减少风险。worker进程间互相不会影响,一个进程退出后,其他进程还在工作,服务不会中断,master进程则很快重新启动新的worker进程。若因程序bug导致worker异常退出,会导致worker上的所有请求失败,但不会影响到所有的请求,所以降低了风险。
  master进程的主要工作:
  1、读取和校验配置文件
  2、创建、绑定、关闭套接字
  3、启动、终止、维护所配置数目的worker进程
  4、不中断服务刷新配置文件
  5、重新打开日志文件
  6、编译嵌入Perl脚本
  worker进程主要完成的任务包括:
  1、接收、传入并处理来自客户端的连接
  2、提供反向代理及过虑功能
  3、nginx任何能完成的其它任务
  1.2、事件处理模型(异步非阻塞的事件处理机制)
  在进程模型中我们知道,nginx是一个master和多个worker进程模型,一个worker只有一个主线程,有多少个worker进程不就是只能处理多少个并发吗?这何来高并发呢?那增加worker进程不就可以增加并发了,如果真是这样做了,那不是像apache一样了(每个请求独占一个工作线程,当并发上千时,同时也有上千的工作线程处理请求,这对操作系统来说是一个挑战,对内存的占用非常大,线程的上下文切换带来的cpu开销也很大,自然性能就上不去,而这些开销完全是没有意义的)。nginx没有采用apache一样的多线程模式处理事件,而是采用了异步非阻塞的方式来处理请求。
  何为异步非阻塞?一个完整的请求过程是这样的,客户端的请求过来,要建立连接,然后接收数据,再处理请求(生成请求数据),再回送数据。从系统底层来看,就是读写事件,当读写事件没有准备好时,如果不是非阻塞的方式来调用,那就是阻塞方式来调用,这样,事件没有准备好,那只有等待,等事件准备妥当,再继续。阻塞调用会进入内核等待,cpu会让出给其他进程用,这对单线程的worker来说,阻塞调用显然不合适,当网络事件越多,大家都被阻塞,等待事件的完成,大量cpu资源空闲下来,cpu利用率上不去,高并发是不能实现的。那非阻塞调用呢?当请求过来,读写事件没有完成,但马上给你返回一个信号,告诉你,你请求的事件还没有准备好,别着急,你过一会再过来看看。这样你就过一会儿来看看事件的状态(忙等模式),直到事件准备好了为止,在这期间,你就可以去做其它的事情,然后抽个时间来看看事件准备好没有,这种调用方式虽然没有阻塞,但你得不时地回来检查一下事件的状态,这样你可以做更多的事,但带来的开销也是挺大的,这种这是同步非阻塞的调用方式。为了节省开销,提高性能,所以有了异步非阻塞的调用机制,具体到系统调用就是像select/poll/epoll/kqueue这样的系统调用。这种机制允许你可以同时监控多个事件,拿epoll来举例,如果事件没有准备好,那就放到epoll里面,事件准备好了,我们就去读写,当读写返回EAGAIN时,我们再次将它加入到epoll里面。这样,只要有事件准备好了,我们就去处理它,当所有的事件都没有准备好时,就在epoll里面等着,通过这种方式我们就可以并发处理大量的并发请求了,注意这里所说的并发请求是指未处理完的请求,然尔,线程只有一个,所以同时能处理的请求也只有一个,只是在请求间不断地切换而已,切换也是因为异步事件未准备好而主动让出的。这里的切换是没有任何代价的,你可以理解为循环处理多个准备好的事件。与多线程相比,这种事件处理方式有很大的优势,不需要创建线程,每个请求占用的内存也很少,没有上下文切换,事件处理非常的轻量。并发数再多也不会导致无谓的资源浪费(因为是单线程,没有上下文切换),只是会占用更多的内存而已。有人对连接数进行过测试,在24G内存的服务器上,处理的并发请求数达到过200万。
  1.3、支持sendfile,提升文件传输性能
  web服务器从存储取得一个资源来作为请求端的响应,这个过程一般是这样的:
  1、调用read函数,把数据从存储设备copy到内核空间;
  2、把数据从内核空间copy到用户空间;
  3、调用write函数,把数据从用户空间copy到内核中与socket相关的缓冲空间;
  4、从socket缓冲地区copy数据到相关的协议栈引擎;
  5、最后把数据封装成数据帧发向网卡设备。
  而sendfile系统调用是这样来传输数据的:
  1、sendfile系统调用把数据从存储设备copy到内核空间;
  2、从内核空间直接把数据copy到socket相关的缓冲空间;
  3、从socket缓冲区copy数据到相关的协议栈引擎;
  4、最后把数据封装成数据帧发向网卡设备。
  sendfile系统调用是在2.1内核才引用的,这种机制省去了数据从内核空间copy到用户空间的过程,文件的传输性能得到了提高,而在2.4的内核中,又对这种机制进行了进一步的优化,在把数据从内核空间copy到socket相关的缓冲区这一操作中在2.4内核中不再是copy数据本身,而是把数据在内核空间的地址及数据长度发送到socket相关缓冲区中,再由DMA(Direct Memory Access直接内存访问)模块让相关的协议引擎直接访问数据,这样又减少了一次数据的copy。
  1.4、支持AIO
  AIO是指异步时间非阻塞IO,用户向内核发起IO请求后,不用等待IO事件的真正发生,而是直接返回给IO请求后,断续做其他的事情,等IO操作完成后,内核通过回调函数或信号机制通知用户进程它之前的IO请求已完成,这能提高系统的吞吐量。
  1.5、支持mmap
  mmap(Memory Map,内存映射)的最终目的是将设备或文件映射到用户进程的虚拟地址空间,实现用户进程对文件的直接读写,避免read()、write()的系统调用,这样减少了数据在内核空间与用户空间的数据copy及相应的上下文切换,降低了因磁盘IO发生的开销,提升了web服务的响应速度。当有数据需要从内核空间复制数据到用户空间时,采用mmap这种机制,特别是在高并发的环境下,对性能是有提升的。
  1.6、小结:
  基于单个master与多个worker进程的进程模型(且是单线程工作,一个worker只有一个主线程)、基于事件驱动的事件处理机制、支持linux内核的sendfile、AIO、mmap等特性的nginx必将是一个高效可靠的服务。
  2、源码编译安装nginx
  2.1、系统环境
# pwd  
/root/software
  
# cat /etc/issue
  
CentOS release 6.4 (Final)
  
Kernel \r on an \m
  

  
# uname -r
  
2.6.32-358.el6.x86_64
  2.2、处理依赖关系及建立运行nginx的用户
# yum -y install pcre-devel  
# useradd -r -s /sbin/nologin -M nginx
  2.3、编译、配置、安装
  软件包这里获取:nginx-1.6.2.tar.gz
# pwd  
/root/software/nginx
  
# ls
  
nginx-1.6.2.tar.gz
  
# tar xf nginx-1.6.2.tar.gz
  
# cd nginx-1.6.2
  
# ./configure \
  
--prefix=/opt/lemp/nginx16 \
  
--sbin-path=/opt/lemp/nginx16/sbin/nginx \
  
--conf-path=/etc/nginx16/nginx.conf \
  
--error-log-path=/var/log/nginx16/error.log\
  
--http-log-path=/var/log/nginx16/access.log \
  
--pid-path=/var/run/nginx16.pid \
  
--lock-path=/var/lock/subsys/nginx16 \
  
--user=nginx \
  
--group=nginx \
  
--with-file-aio \
  
--with-http_ssl_module \
  
--with-http_flv_module \
  
--with-http_mp4_module \
  
--with-http_gzip_static_module \
  
--with-http_stub_status_module \
  
--http-client-body-temp-path=/var/tmp/nginx16/client \
  
--http-proxy-temp-path=/var/tmp/nginx16/proxy \
  
--http-fastcgi-temp-path=/var/tmp/nginx16/fastcgi \
  
--http-uwsgi-temp-path=/var/tmp/nginx16/uwsgi \
  
--http-scgi-temp-path=/var/tmp/nginx16/scgi \
  
--with-pcre
  

  
# make && make install
  2.4、为nginx提供启动脚本
# vim /etc/rc.d/init.d/nginx16  
#!/bin/bash
  
##
  
#nginx - this script starts and stops the nginx daemon
  
#
  
# chkconfig:   - 85 15
  
# description:Nginx is an HTTP(S) server, HTTP(S) reverse \
  
#               proxy and IMAP/POP3 proxy server
  
# processname: nginx
  
# config:      /etc/nginx16/nginx.conf
  
# pidfile:   /var/run/nginx16.pid
  

  
# Source function library.
  
. /etc/rc.d/init.d/functions
  

  
# Source networking configuration.
  
. /etc/sysconfig/network
  

  
# Check that networking is up.
  
[ "$NETWORKING" = "no" ] && exit 0
  

  
nginx="/opt/lemp/nginx16/sbin/nginx"
  
prog=$(basename $nginx)
  
nginx_config_file="/etc/nginx16/nginx.conf"
  
lockfile=/var/lock/subsys/nginx16
  

  
make_dirs() {
  
   # make required directories
  
   user=`$nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -`
  
   options=`$nginx -V 2>&1 | grep 'configure arguments:'`
  
   for opt in $options; do
  
       if [ `echo $opt | grep '.*-temp-path'` ]; then
  
         value=`echo $opt | cut -d "=" -f 2`
  
         if [ ! -d "$value" ]; then
  
               # echo "creating" $value
  
               mkdir -p $value && chown -R $user $value
  
         fi
  
       fi
  
   done
  
}
  

  
start() {
  
    [ -x $nginx ] || exit 5
  
    [ -f $nginx_config_file ] || exit 6
  
    make_dirs
  
    echo -n $"Starting $prog: "
  
    daemon $nginx -c $nginx_config_file
  
    retval=$?
  
    echo
  
    [ $retval -eq 0 ] && touch $lockfile
  
    return $retval
  
}
  

  
stop() {
  
    echo -n $"Stopping $prog: "
  
    killproc $prog -QUIT
  
    retval=$?
  
    echo
  
    [ $retval -eq 0 ] && rm -f $lockfile
  
    return $retval
  
}
  

  
restart() {
  
    configtest || return $?
  
    stop
  
    sleep 1
  
    start
  
}
  

  
reload() {
  
    configtest || return $?
  
    echo -n $"Reloading $prog: "
  
    killproc $nginx -HUP
  
    RETVAL=$?
  
    echo
  
}
  

  
force_reload() {
  
    restart
  
}
  

  
configtest() {
  
$nginx -t -c $nginx_config_file
  
}
  

  
rh_status() {
  
    status $prog
  
}
  

  
rh_status_q() {
  
    rh_status >/dev/null 2>&1
  
}
  
case "$1" in
  
    start)
  
      rh_status_q && exit 0
  
      $1
  
      ;;
  
    stop)
  
      rh_status_q || exit 0
  
      $1
  
      ;;
  
    restart|configtest)
  
      $1
  
      ;;
  
    reload)
  
      rh_status_q || exit 7
  
      $1
  
      ;;
  
    force-reload)
  
      force_reload
  
      ;;
  
    status)
  
      rh_status
  
      ;;
  
    condrestart|try-restart)
  
      rh_status_q || exit 0
  
            ;;
  
    *)
  
      echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
  
      exit 2
  
esac
  

  
# chmod +x /etc/rc.d/init.d/nginx16
  
# service nginx16 start
  
# chkconfig --add nginx16
  
# chkconfig nginx16 on
  
# ps aux | grep nginx


页: [1]
查看完整版本: nginx高效原理及源码编译安装(nginx-1.6.2)