Nginx

 

1   Nginx

1.1 为什么需要Nginx?

单个tomcat支持最高并发

 

 

怎么解决高并发问题,解决单个服务器过载问题?

 

 

 

 

 

 

 

1.2 Nginx概述

 

1.2.1     Nginx介绍

1、 Nginx ("engine x") 是一个高性能的 静态HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。

2、 第一个公开版本0.1.0发布于2004年10月4日。

3、 其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名

4、 官方测试nginx能够支撑5万并发链接,并且cpu、内存等资源消耗却非常低,运行非常稳定

5、 2011年6月1日,nginx 1.0.4发布。apache httpd

6、 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。由俄罗斯的程序设计师Igor Sysoev所开发,其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:新浪、网易、腾讯等。

 

1.2.2     Nginx和apache的优缺点

1.2.2.1 nginx相对于apache的优点:

1、 轻量级,同样起web 服务,比apache 占用更少的内存及资源

2、 抗并发,nginx 处理请求是异步非阻塞(可参考文章https://zhuanlan.zhihu.com/p/82935440)的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能  NIO netty NIO

3、 高度模块化的设计,编写模块相对简单

4、 社区活跃,各种高性能模块出品迅速

1.2.2.2 apache 相对于nginx 的优点:

1、 rewrite ,比nginx 的rewrite 强大

2、 模块超多,基本想到的都可以找到

3、 少bug ,nginx 的bug 相对较多

4、 Nginx 配置简洁, Apache 复杂

5、 最核心的区别在于apache是同步多进程模型,一个连接对应一个进程中线程;nginx是异步的,多个连接(万级别)可以对应一个进程

 

 

1.2.3     Tengine介绍

tengine网址:http://tengine.taobao.org/

Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验。它的最终目标是打造一个高效、稳定、安全、易用的Web平台。

 

从2011年12月开始,Tengine成为一个开源项目,Tengine团队在积极地开发和维护着它。Tengine团队的核心成员来自于淘宝、搜狗等互联网企业。Tengine是社区合作的成果,我们欢迎大家参与其中,贡献自己的力量。

 

tengine和nginx性能测试:http://tengine.taobao.org/document_cn/benchmark_cn.html

  1. Tengine相比Nginx默认配置,提升200%的处理能力。
  2. Tengine相比Nginx优化配置,提升60%的处理能力。

tengine更详细参数解释详见:

http://tengine.taobao.org/nginx_docs/cn/docs/

由于Nginx使用更为普及,我们主要讲解Nginx

 

1.3 Nginx下载与安装

1.3.1     Nginx下载

官网下载Nginx软件http://nginx.org

Nginx 官方提供了三个类型的版本:

  Mainline Version:主线版,是最新版,但未经过过多的生产测试。

  Stable Version:稳定版,生产环境使用版本。

  Legacy Version:老版本。

我们需要下载的是 Stable Version。其中又分为两种版本:Linux 版与 Windows 版。开发时这两个版本我们都下载。Linux 版用于生产环境,而 Windows 版用于开发测试。

 

 

 

 

1.3.2     Nginx  的源码安装

1.3.2.1 安装前准备工作

克隆一个没有安装其它软件的纯净主机。完成以下配置:

 1.修改主机名:vim /etc/sysconfig/network

[root@nginx1 ~]# hostname nginx1

[root@nginx1 ~]# vim /etc/sysconfig/network

HOSTNAME  nginx1

 2.修改网络配置:vim /etc/sysconfig/network-scripts/ifcfg-eth0

   192.168.20.95

1.3.2.2 安装 Nginx

1.安装源码编译以及Nginx依赖的库

yum install gcc gcc-c++ pcre  pcre-devel openssl openssl-devel zlib  zlib-devel -y

2.创建存放源文件的文件夹

首先在目录/opt下创建apps目录,用于存放源文件以及解压后的文件

mkdir /opt/apps

3.上传Nginx到2创建的目录/opt/apps下

4.解压 Nginx

[root@nginx1 apps]# pwd

/opt/apps

[root@nginx1 apps]# ls

nginx-1.16.1.tar.gz

[root@nginx1 apps]# tar -zxvf nginx-1.16.1.tar.gz

进入到/opt/apps 目录中的 Nginx 解压包目录,查看 Nginx 的目录。

其中各个目录中存放的文件作用为:

  auto:存放 Nginx 自动安装的相关文件

  conf:存放 Nginx 服务器配置文件

  configure:命令,用于对即将安装的软件的配置,完成 Makefile 编译文件的生成

  contrib:存放由其他机构贡献的文档材料

  html:存放 Nginx 欢迎页面

  man:manual,手册,存放 Nginx 帮助文档

  src:存放 Nginx 源码

 

5.生成 makefile

在 Nginx 解压目录下运行 make 命令,用于完成编译。但此时会给出提示:没有指定目标,并且没有发现编译文件 Makefile。编译命令 make 需要根据编译文件 makefile 进行编译,所以在编译之前需要先生成编译文件 makefile。使用 configure 命令可以生成该文件。那么,configure 命令需要配置些什么参数呢?使用--help 可以查看到可以使用的参数说明。这些参数可以分为三类:

第一类:基本信息的配置。

第二类:默认没有安装,可以指定安装的模块,使用--with 开头。Nginx 的高扩展性就

体现在这里。

第三类:默认已经安装,可以指定卸载的模块,使用--without 开头。

下面是简单配置的命令执行。命令中每一行的最后添加了反斜杠\表示当前命令并未结束,回车不会执行该命令。执行成功后,会给出配置报告。下面以安装对 https 访问协议支持的模块 http_ssl_module 为例。

  --prefix:用于指定 nginx 的安装目录。注意,安装目录与解压目录不一样。

  --http_ssl_module:https 访问协议需要安装 Http 安全连接协议模块 SSL(Secure SocketsLayer,安全套接层)。注意,在执行过 configure 命令后并不会立即生成/usr/local/nginx 目录,也不会马上开始安装指定的模块,而仅仅是将命令中指定的参数及默认配置写入到即将要生成的 Makefile文件中。

配置报告以两部分构成:第一部分给出了配置的系统库;第二部分给出了系统配置信息。

  path prefix:Nginx 安装目录

  binary file:Nginx 命令文件

  modules path:Nginx 模块存放路径

  configuration prefix:Nginx 配置文件存放路径

  configuration file:Nginx 配置文件名

  pid file:Nginx 的进程 id 文件

  error log file:错误日志文件

  http access log file:http 访问日志文件;

  http xxx:其它 http 请求相关的文件。

配置成功后,再次查看 Nginx 解压目录,发现其中多出了一个文件 Makefile。后面的编译就是依靠该文件进行的。

[root@nginx1 nginx-1.16.1]# mkdir –p /var/tmp/nginx/client

[root@nginx1 nginx-1.16.1]# pwd

/opt/apps/nginx-1.16.1

[root@nginx1 nginx-1.16.1]# ./configure \

  --prefix=/opt/nginx \

  --sbin-path=/usr/sbin/nginx\

  --conf-path=/etc/nginx/nginx.conf \

  --error-log-path=/var/log/nginx/error.log \

  --http-log-path=/var/log/nginx/access.log \

  --pid-path=/var/run/nginx/nginx.pid \

  --lock-path=/var/lock/nginx.lock \

  --user=nginx \

  --group=nginx \

  --with-http_ssl_module \

  --with-http_flv_module \

  --with-http_stub_status_module \

  --with-http_gzip_static_module \

  --http-client-body-temp-path=/var/tmp/nginx/client/ \

  --http-proxy-temp-path=/var/tmp/nginx/proxy/ \

  --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \

  --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \

  --http-scgi-temp-path=/var/tmp/nginx/scgi \

  --with-pcre 

注意: /var/tmp/nginx/client目录需要手动创建

 

也可以使用简单安装,指定安装目录,https访问,压缩支持:

./configure --prefix=/opt/nginx --with-http_ssl_module --with-http_gzip_static_module --error-log-path=/var/log/nginx/nginx.log --pid-path=/var/log/nginx/pid

5.编译安装

这是两个命令,make 为编译命令,make install 为安装命令,可以分别执行。这里使用&&将两个命令连接执行,会在前面命令执行成功的前提下才会执行第二个命令。

make && make install

 

6.nginx  命令随处可用

在 Nginx 的安装目录/opt/nginx 中有一个 sbin 目录,其中存放着 nginx 的命令程序nginx。默认情况下,若要使用 nginx 命令,则必须要在/opt/nginx/sbin 目录中,或指定命令路径,使用起来很不方便。为了能够在任意目录下均可直接执行 nginx 命令,可通过以下两种方式完成。

方式一 、  修改/etc/profile  文件

在/etc/profile 文件最后添加以下内容,将安装目录下的 sbin 目录添加到 PATH 系统环境变量中。然后再重新加载该文件即可。

#修改环境变量

export PATH=$PATH:/usr/sbin/nginx  #注意执行文件的位置

useradd nginx  #添加用户(默认添加nginx用户组)

nginx  #启动nginx

方式二 、  添加安装的nginx到服务列表:将如下内容添加到/etc/init.d/nginx脚本中,nginx需要具有可执行权限。

[root@nginx1 nginx-1.16.1]# vim /etc/init.d/nginx

 

少参数版本

#!/bin/sh

#

# 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/nginx/nginx.conf

# config:      /etc/sysconfig/nginx

# pidfile:     /var/run/nginx.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/nginx/sbin/nginx"

prog=$(basename $nginx)

 

NGINX_CONF_FILE="/opt/nginx/conf/nginx.conf"

 

[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx

 

lockfile=/var/lock/subsys/nginx

 

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_CONF_FILE ] || exit 6

    make_dirs

    echo -n $"Starting $prog: "

    daemon $nginx -c $NGINX_CONF_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_CONF_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

 

修改nginx文件的执行权限

   chmod +x nginx

添加该文件到系统服务中去

   chkconfig --add nginx

查看是否添加成功

   chkconfig [--list]|grep nginx

启动,停止,重新装载

service nginx start|stop|reload

 

 

设置开机启动

[root@nginx1 conf]# chkconfig nginx on

[root@nginx1 conf]# chkconfig

nginx             0:off  1:off  2:on   3:on   4:on   5:on   6:off

 

 

1.4 Nginx的工作模型

 

1.4.1     Master-Worker模式

1、Nginx 在启动后,会有一个 master 进程和多个相互独立的 worker 进程。

2、Master接收来自外界的信号,向各worker进程发送信号,每个进程都有可能来处理这个连接。

3、Master进程能监控Worker进程的运行状态,当 worker 进程退出后(异常情况下),会自动启动新的 worker 进程。

 

如果master进程被杀死或退出,再次遇到worker进行退出后,将无法让worker进程重新启动。

1.4.2     accept_mutex

由于所有子进程都继承了父进程的sockfd,那么当连接进来时,所有子进程都将收到通知并“争着”与它建立连接,这就叫“惊群现象”。大量的进程被激活又挂起,只有一个进程可以accept() 到这个连接,这当然会消耗系统资源。Nginx 提供了一个accept_mutex加在accept上的一把共享锁。即每个worker进程在执行accept之前都需要先获取锁,获取不到就放弃执行accept()。有了这把锁之后,同一时刻,就只会有一个进程去accpet(),这样就不会有惊群问题了。

 

当一个worker进程在accept()这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,完成一个完整的请求。一个请求,完全由worker进程来处理,而且只能在一个worker进程中处理。

 

1.4.3     为什么使用进程不使用线程?

1、节省锁带来的开销。每个worker进程都是独立的进程,不共享资源,不需要加锁。同时在编程以及问题查上时,也会方便很多。

2、独立进程,减少风险。采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master进程则很快重新启动新的worker进程。当然,worker进程的也能发生意外退出。

 

1.4.4     如何处理并发请求?

每进来一个request,会有一个worker进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发request,并等待请求返回。那么,这个处理的worker不会这么傻等着,他会在发送完请求后,注册一个事件:“如果upstream返回了,告诉我一声,我再接着干”。于是他就休息去了。此时,如果再有request 进来,他就可以很快再按这种方式处理。而一旦上游服务器返回了,就会触发这个事件,worker才会来接手,这个request才会接着往下走。由于web server的工作性质决定了每个request的大部份生命都是在网络传输中,实际上花费在server机器上的时间片不多,这就是几个进程就能解决高并发的秘密所在。

 

 

1.5 Nginx参数详解

1.5.1   nginx.conf配置文件全览

#号是注释。

#---全局块开始----
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;
#----全局块结束----

#====events块开始====
events {
    worker_connections  1024;
}
#====events块结束====
#****http块开始****
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块结束****

nginx由三部分组成:

1.5.1.1         第一部分:全局块

从配置文件开始到events之间的内容,主要会设置一些影响nginx服务器整体运行的配置参数。主要包括配置运行Nginx服务器的用户(组)、允许生成的worker process数,进程PID存放路径、日志存放路径和类型以及配置文件的引入等。

#user  nobody;
worker_processes  1;
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;

worker_processes是Nginx服务器并发处理服务的关键配置,值越大,可以支持的并发处理量也越多,但是会受到硬件、软件等设备的制约。

error_log配置nginx日志文件的全路径名

pid配置进程PID存放路径

1.5.1.2 第二部分:events块
events {
    worker_connections  1024;
}

events块涉及的参数主要影响Nginx服务器与用户的网络连接,常用的设置包括是否开启对多work process下的网络连接进行序列化,是否允许同时接受多个网络连接,选取哪种事件驱动模型来处理连接请求,每个work process可以同时支持的最大连接数等。

上述的例子表示每个work process支持的最大连接数为1024。这部分的配置对Nginx的性能影响比较大,在实际中应该灵活配置。

1.5.1.3         第三部分:http块{ }

这是Nginx服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里。需要注意的是:http块也可以包括http全局块、server块。

1.5.1.3.1  http全局块

http全局块配置的指令包括文件引入、MIME-TYPE定义、连接超时时间、单链接请求数上限等。

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 {
    ......
    }
  }

1.5.1.3.2     server块。

这块和虚拟主机有密切关系,虚拟主机从用户角度看,和一台独立的硬件主机是完全一样的,该技术的产生是为了节省互联网服务器硬件成本。

每个http块可以包括多个server块,而每个server块就相当于一个虚拟主机。而每个server块也分为全局server块,以及可以同时包含多个location块。

#gzip  on;

    server {

        listen       80;#监听的端口号

        server_name  localhost;#监听的域名

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / { #路径中包含 /

            root   html;

            index  index.html index.htm;

        }

​        #error_page  404              /404.html;

​        # redirect server error pages to the static page /50x.html

        error_page   500 502 503 504  /50x.html;

        location = /50x.html {

            root   html;

        }

​        # proxy the PHP scripts to Apache listening on 127.0.0.1:80

        #location ~ \.php$ {

        #    proxy_pass   http://127.0.0.1;

        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

        #

        #location ~ \.php$ {

        #    root           html;

        #    fastcgi_pass   127.0.0.1:9000;

        #    fastcgi_index  index.php;

        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;

        #    include        fastcgi_params;

        #}

        # deny access to .htaccess files, if Apache's document root

        # concurs with nginx's one

        #

        #location ~ /$.ht {

        #    deny  all;

        #}

    }

全局server

最常见的配置是本虚拟主机的监听配置和本虚拟主机的名称或IP配置。

location

一个server块可以配置多个location块。www.123.com/xx?k1=v1&k2=v2

这块的主要作用是基于Nginx服务器接受到的请求字符串(例如 server_name/uri-string),对虚拟主机名称(也可以是IP别名)之外的字符串(列如 前面的/uri-string)进行匹配,对特定的请求进行处理。地址定向、数据缓存和应答控制等功能,还有许多第三方模块的配置也在这里进行。

 

1.5.2     工作模式与连接数上限

#user  nobody;

worker_processes  1;

events {

use epoll;

worker_connections 1024;

}

  1. 用户与工作进程

#user  nobody;

worker_processes  1;

[root@nginx1 conf]# ps aux |grep nginx

root       1170  0.0  0.0  22568   680 ?        Ss   09:14   0:00 nginx: master process /opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf

nobody     1171  0.0  0.1  23020  1288 ?        S    09:14   0:00 nginx: worker process                             

root       1174  0.0  0.0 103264   876 pts/0    S+   09:14   0:00 grep nginx

[root@nginx1 conf]# ps aux |grep nginx

root       1170  0.0  0.0  22568   680 ?        Ss   09:14   0:00 nginx: master process /opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf

nobody     1171  0.0  0.1  23020  1288 ?        S    09:14   0:00 nginx: worker process                              

 [root@nginx1 conf]# id nobody

uid=99(nobody) gid=99(nobody) groups=99(nobody)

[root@nginx1 conf]# cat /etc/passwd

root:x:0:0:root:/root:/bin/bash

……

nobody:x:99:99:Nobody:/:/sbin/nologin

  1.  use epoll;

参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。

  1.  worker_connections  1024;

单个后台worker process进程的最大并发链接数。

并发总数是 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  设置可以打开的文件数量

 

 

1.5.3     开启零拷贝

 

sendfile实际上是 Linux2.0+以后的推出的一个系统调用,web服务器可以通过调整自身的配置来决定是否利用 sendfile这个系统调用。先来看一下不用sendfile的传统网络传输过程:

read(file,tmp_buf, len);

write(socket,tmp_buf, len);

硬盘 >> kernel buffer >> user buffer>> kernel socket buffer >>协议栈

 

 

一个基于socket的服务,首先读硬盘数据,然后写数据到socket 来完成网络传输的。上面2行用代码解释了这一点,不过上面2行简单的代码掩盖了底层的很多操作。来看看底层是怎么执行上面2行代码的:

 

1、系统调用 read()产生一个上下文切换:从 user mode 切换到 kernel mode,然后 DMA 执行拷贝,把文件数据从硬盘读到一个 kernel buffer 里。

 

2、数据从kernel buffer拷贝到user buffer,然后系统调用 read() 返回,这时又产生一个上下文切换:从kernel mode 切换到 user mode。

 

3、 系统调用write()产生一个上下文切换:从 user mode切换到 kernel mode,然后把步骤2读到 user buffer的数据拷贝到 kernel buffer(数据第2次拷贝到 kernel buffer),不过这次是个不同的 kernel buffer,这个 buffer和 socket相关联。

 

4、系统调用 write()返回,产生一个上下文切换:从 kernel mode 切换到 user mode ,然后 DMA 从 kernel buffer拷贝数据到协议栈。

 

上面4个步骤有4次上下文切换,有4次拷贝,我们发现如果能减少切换次数和拷贝次数将会有效提升性能。在kernel2.0+ 版本中,系统调用 sendfile() 就是用来简化上面步骤提升性能的。sendfile() 不但能减少切换次数而且还能减少拷贝次数。

 

再来看一下用 sendfile()来进行网络传输的过程:

 

sendfile(socket,file, len);

 

硬盘 >> kernel buffer (快速拷贝到kernelsocket buffer) >>协议栈

 

1、 系统调用sendfile()通过 DMA把硬盘数据拷贝到 kernel buffer,然后数据被 kernel直接拷贝到另外一个与 socket相关的 kernel buffer。这里没有 user mode和 kernel mode之间的切换,在 kernel中直接完成了从一个 buffer到另一个 buffer的拷贝。

 

2、DMA 把数据从 kernelbuffer 直接拷贝给协议栈,没有切换,也不需要数据从 user mode 拷贝到 kernel mode,因为数据就在 kernel 里。

 

简单说,sendfile是个比 read 和 write 更高性能的系统接口, 不过需要注意的是,sendfile 是将 in_fd 的内容发送到 out_fd 。而 in_fd 不能是 socket , 也就是只能文件句柄。 所以当 Nginx 是一个静态文件服务器的时候,开启 SENDFILE 配置项能大大提高 Nginx 的性能。 但是当 Nginx 是作为一个反向代理来使用的时候,SENDFILE 则没什么用了,因为 Nginx 是反向代理的时候。 in_fd 就不是文件句柄而是 socket,此时就不符合 sendfile 函数的参数要求了。

 

1.5.4      keepalive_timeout

 

测试时改为0,便于看出负载切换的效果,部署到生产前进行优化来提高效率。

1.5.5      是否启用压缩

 

压缩可以有效减少文件的大小,有利于网络传输。

 

1.5.6     autoindex

autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。

1.5.7     nginx虚拟主机演示

 

 

虚拟主机,就是将一台物理服务器虚拟为多个服务器来使用,从而实现在一台服务器上配置多个站点,即可以在一台物理主机上配置多个域名。Nginx 中,一个 server 标签就是一台虚拟主机,配置多个 server 标签就虚拟出了多台主机。Nginx 虚拟主机的实现方式有两种:域名虚拟方式与端口虚拟方式。域名虚拟方式是指不同的虚拟机使用不同的域名,通过不同的域名虚拟出不同的主机;端口虚拟方式是指不同的虚拟机使用相同的域名不同的端口号,通过不同的端口号虚拟出不同的主机。基于端口的虚拟方式不常用。

 

 

 

  1. 修改nginx.conf文件

    gzip  on;

    server {

       listen  80;

       server_name www.123.com;

       location / {

           root /mnt;

           autoindex on;

       }

    }

    server {

        listen       80;

        server_name www.123.com;

 

        #charset koi8-r;

 

        #access_log  logs/host.access.log  main;

 

        location / {

            root   html;

            index  index.html index.htm;

        }

......

重新加载nginx

[root@nginx1 conf]# service nginx reload

 

  1. 修改本机hosts文件(C:\Windows\System32\drivers\etc)

192.168.20.11 nginx1 www.123.com  www.123.com

 

  1. 访问测试

 

 

自动提供了一个欢迎页面,由于/mnt下什么也没有挂载,所以列表中什么也没有。

  1. 为/mnt挂载,并重新测试

[root@nginx1 conf]# mount /dev/cdrom /mnt

 

对比下图

 

1.5.8     日志配置

Nginx还可以作为日志服务器

[root@nginx1 logs]# pwd

/opt/nginx/logs

 [root@nginx1 logs]# tail -f access.log

本地浏览器访问:http://www.123.com/2019-12-03maxwd19

先不管404的问题,查看日志多了一天记录

 

修改一下http://www.123.com/2019-12-04maxwd20,日志又记录一条

 

当然日志格式我们也可以自定义

 

access_log配置http下,多server公用,配置http->某server下,仅对该server使用。

 

http://www.123.com/2019-12-04maxwd20myfmt.log日志记录多一条

http://www.123.com/2019-12-04maxwd20日志记录access.log多一条(并没有使用myfmt.log)

 

 

 

1.5.9     Location(重点)

参考:

http://tengine.taobao.org/nginx_docs/cn/docs/http/ngx_http_core_module.html

语法

location [ = | ~ | ~* | ^~ ] uri { ... }

location @name { ... }

默认值

-

上下文

server, location

 

让我们用一个例子解释上面的说法:

location = / {

    [ configuration A ]

}

location / {

    [ configuration B ]

}

 

location /documents/ {

    [ configuration C ]

}

 

location ^~ /images/ {

    [ configuration D ]

}

 

location ~* \.(gif|jpg|jpeg)$ {

    [ configuration E ]

}

请求“/”匹配配置A,

请求“/index.html”匹配配置B,

请求“/documents/document.html”匹配配置C,

请求“/images/1.gif”匹配配置D,

请求“/documents/1.jpg”匹配配置E。

 

 

 

 

  1. 修改nginx.conf配置文件

    server {

       listen  80;

       server_name www.123.com;

       access_log   logs/myfmt.log  myfmt;

       location / {

           root /mnt;

           autoindex on;

       }

       location /aabb {

          proxy_pass http://192.168.20.102/;#带上/访问该url对应的首页,

#不带/ 访问http://192.168.20.102/aabb

       }

    }

  1. 重新加载nginx

[root@nginx1 conf]# !ser

 

  1. 访问测试

http://www.123.com/ooxx

 

  1. 修改nginx.conf

       location /ooxx {

          proxy_pass http://www.baidu.com/;

       }

  1. 重启nginx
  2. [root@nginx1 conf]# !ser

如果重启没有问题,直接跳步骤7.

如果出现下图所示的错误:

 

找不到域名,也就是访问不到域名解析服务器。

解决办法:

 

 

  1. 访问测试http://www.123.com/ooxx

 

虽然访问到了百度,但是确实通过重定向的方式,以后发生的事情和我们的服务器就没有半毛钱关系了。优化配置nginx.conf:

#尽量在服务器端跳转,不要在客户端跳转

proxy_pass https://www.baidu.com/;

重启nginx,再次测试,地址栏没有重定向,但是当我们查询(比如:ssd)时出现

 

修改nginx.conf

       location /ooxx {

          proxy_pass http://www.baidu.com/;

       }    

location ~* /s.* {

          proxy_pass https://www.baidu.com;

       }

 

1.5.10   Bug https protocol requires SSL support in

[root@nginx1 conf]# service nginx reload

nginx: [emerg] https protocol requires SSL support in /opt/nginx/conf/nginx.conf:45

nginx: configuration file /opt/nginx/conf/nginx.conf test failed

当初编译的时候没有启用SSL支持,在配置反向代理到 https的网站时,编辑配置文件报错,无法启动nginx。

解决办法:先将nginx.conf备份/root/目录下,删除/opt/nginx和/opt/apps/

nginx-1.16.1,然后在解压一份,最后编译安装。

[root@nginx1 nginx-1.16.1]# ./configure --prefix=/opt/nginx --with-http_ssl_module

[root@nginx1 nginx-1.16.1]# make && make install

[root@nginx1 nginx-1.16.1]# cd /opt/nginx/conf/

[root@nginx1 conf]# cp /root/nginx.conf  ./

cp: overwrite `./nginx.conf'? yes

[root@nginx1 conf]# service nginx reload

nginx: the configuration file /opt/nginx/conf/nginx.conf syntax is ok

nginx: configuration file /opt/nginx/conf/nginx.conf test is successful

Reloading nginx:    

然后再访问http://www.123.com/ooxx

 

 

 

 

 

 

 

 

1.6 反向代理

代理服务器根据其代理对象的不同,可以分为正向代理服务器与反向代理服务器。这里

的“正”与“反”均是站在客户端角度来说的。

1.6.1     正向代理服务器

正向代理是对客户端的代理。客户端 C 想要从服务端 S 获取资源,但由于某些原因不能,直接访问服务端,而是通过另外一台主机 P 向服务端发送请求。当服务端处理完毕请求后,将响应发送给主机 P,主机 P 在接收到来自服务端的响应后,将响应又转给了客户端 C。此时的主机 P,就称为客户端 C 的正向代理服务器。

客户端在使用正向代理服务器时是知道其要访问的目标服务器的地址等信息的。正向代理服务器是为服务用户(客户端)而架设的主机,与服务端无关,对服务器端透明。

 

 

 

1.6.2     反向代理服务器

反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

 

 

 

1.6.3     反向代理之负载均衡

 

负载均衡(Load Balancing):就是将对请求的处理分摊到多个操作单元上进行。这个均衡是指在大批量访问前提下的一种基本均衡,并非是绝对的平均。对于 Web 工程中的负载均衡,就是将相同的 Web 应用部署到多个不同的 Web 服务器上,形成多个 Web 应用服务器。当请求到来时,由负载均衡服务器负责将请求按照事先设定好的比例向 Web 应用服务器进行分发,从而增加系统的整体吞吐量。

负载均衡可以通过负载均衡软件实现,也可通过硬件负载均衡器实现。

(1 ) 硬件负载均衡

硬件负载均衡器的性能稳定,且有生产厂商作为专业的服务团队。但其成本很高,一台硬件负载均衡器的价格一般都在十几万到几十万,甚至上百万。知名的负载均衡器有 F5、Array、深信服、梭子鱼等。

 

(2 ) 软件负载均衡

软件负载均衡成本几乎为零,基本都是开源软件。例如:LVS、HAProxy、Nginx 等。

 

该机群包含一台 Nginx 服务器,两台 Web服务器(node2和node3)。

在node2和node3上安装httpd服务

yum install httpd -y

在/var/www/html目录下创建一个index.html

添加内容:node2 192.168.20.82     或  node3 192.168.20.83

 

修改nginx.conf文件

   upstream rss{

        server 192.168.20.102;

        server 192.168.20.103;

    }

    server {

       listen  80;

       server_name www.123.com;

       access_log   logs/myfmt.log  myfmt;

       location / {

           root /mnt;

           autoindex on;

       }

       location /toms {

           proxy_pass http://rss/;

       }

重启nginx,请求测试:http://www.123.com/toms,发现已经实现了负载均衡。(中小企业一般使用该方式)

也可以通过在Nginx服务器上配置hosts本地域名解析:

[root@nginx1 conf]# vim /etc/hosts

 

192.168.20.101 node1

192.168.20.102 node2  xxx

192.168.20.103 node3  xxx

192.168.20.104 node4

修改nginx.conf配置文件,添加如下配置

       location /cats {

           proxy_pass http://xxx/;

       }

重启nginx,测试:http://www.123.com/cats

一样能够实现负载均衡的目的(大型企业使用该方式,一般还会搭建一个DNS域名解析服务器,只需要修改DNS域名解析服务器就ok了)。

Nginx会用到域名解析,如果一个域名解析出多个ip地址,会在这些ip地址之间做负载均衡。

 

1.7 Nginx调优

1.7.1     worker_processes  的设置

打开 nginx.conf 配置文件,可以看到 worker_processes 的默认值为 1。

 

worker_processes,工作进程,用于指定 Nginx 的工作进程数量。该值应该设置为多少合适呢?其数值一般设置为 CPU 内核数量,或内核数量的整数倍。注意,现代的 CPU 一般都是多核的,即一块 CPU 中包含多个内核。若当前系统具有 2 块 CPU,而每块 CPU 中包含 2 个内核,那么,worker_processes 的值一般可以设置为 4 或 8。当然,也可以设置为 2。

不过需要注意,该值不仅仅取决于 CPU 内核数量,还与硬盘数量及负载均衡模式相关。

在不确定时可以指定其值为 auto。

1.7.2     worker_cpu_affinity  的设置

为了进一步提高系统性能,我们会将 worker 进程与具体的内核进行绑定。该绑定操作是通过 worker_cpu_affinity 属性进行设置的。affinity,密切关系。不过,若指定 worker_processes 的值为 auto,则无法设置 worker_cpu_affinity。

该设置是通过二进制进行的。每个内核使用一个二进制位表示,0 代表内核关闭,1 代

表内核开启。也就是说,有几个内核,就需要使用几个二进制位。下面通过几个例子来增进对 worker_processes 与 worker_cpu_affinity 的理解。

 

1.8 session共享

 

http协议是无状态的,即你连续访问某个网页100次和访问1次对服务器来说是没有区别对待的,因为它记不住你。那么,在一些场合,确实需要服务器记住当前用户怎么办?比如用户登录邮箱后,接下来要收邮件、写邮件,总不能每次操作都让用户输入用户名和密码吧,为了解决这个问题,session的方案就被提了出来,事实上它并不是什么新技术,而且也不能脱离http协议以及任何现有的web技术

session的常见实现形式是会话cookie(session cookie),即未设置过期时间的cookie,这个cookie的默认生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。实现机制是当用户发起一个请求的时候,服务器会检查该请求中是否包含sessionid,如果未包含,则系统会创造一个名为JSESSIONID的输出 cookie返回给浏览器(只放入内存,并不存在硬盘中),并将其以HashTable的形式写到服务器的内存里面;当已经包含sessionid是,服务端会检查找到与该session相匹配的信息,如果存在则直接使用该sessionid,若不存在则重新生成新的 session。这里需要注意的是session始终是有服务端创建的,并非浏览器自己生成的。 但是浏览器的cookie被禁止后session就需要用get方法的URL重写的机制或使用POST方法提交隐藏表单的形式来实现

首先我们应该明白,为什么要实现共享,如果你的网站是存放在一个机器上,那么是不存在这个问题的,因为会话数据就在这台机器,但是如果你使用了负载均衡把请求分发到不同的机器呢?这个时候会话id在客户端是没有问题的,但是如果用户的两次请求到了两台不同的机器,而它的session数据可能存在其中一台机器,这个时候就会出现取不到session数据的情况,于是session的共享就成了一个问题。

 

 

1.8.1     session一致性解决方案

1、session复制

   tomcat 本身带有复制session的功能。(基本不用)

2、共享session

   需要专门管理session的软件,

   memcached 缓存服务,可以和tomcat整合,帮助tomcat共享管理session。

 

 

1.8.2     安装memcached

 

1.8.2.1 node2和node3上安装jdk和tomcat

[root@node2 apps]# rpm -ivh jdk-7u80-linux-x64.rpm

[root@node2 apps]# find / -name '*java*'

#可以看出/usr/java/jdk1.7.0_80/

配置环境变量

[root@node2 jdk1.7.0_80]# vim /etc/profile

加入以下两行代码:

export JAVA_HOME=/usr/java/jdk1.7.0_80

export PATH=$PATH:$JAVA_HOME/bin

让文件生效:

[root@node2 apps]# source /etc/profile

[root@node2 apps]# jps

13894 Jps  #说明jdk安装配置成功

 

解压apache-tomcat-7.0.69.tar.gz

修改ROOT/index.jsp,dG全删后,添加:

from 192.168.20.102 session=<%=session.getId()%>

另外一台node3同样的安装配置操作。

然后分别访问node2和node3都可以正常,刷新session也不会变。

修改nginx.conf

    upstream rss {

        server 192.168.20.102:8080;

        server 192.168.20.103:8080;

}

访问测试http://www.123.com/toms,刷新session一直改变。

 

 

1.8.2.2 nginx1上安装memcached

1、 安装libevent

a)  yum install libevent -y

2、 安装memcached

a)  yum install memcached  -y

3、启动memcached

memcached -d -m 128m -p 11211 -l 192.168.20.85 -u root -P /var/run/memcached/memcached.pid

   -d:后台启动服务

   -m:缓存大小

   -p:端口

   -l:IP

   -P:服务器启动后的系统进程ID,存储的文件

   -u:服务器启动是以哪个用户名作为管理用户

 

 

 

 

1.8.3     配置session共享(node2和node3)

1、拷贝jar到tomcat的lib下,jar包见附件

2、配置tomcat,每个tomcat里面的context.xml中加入

 

<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"

   memcachedNodes="n1:192.168.20.85:11211"

    sticky="false"

    lockingMode="auto"

    sessionBackupAsync="false"

   requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"

    sessionBackupTimeout="1000" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"/>

 

tomcat添加jar包和配置信息之后需要重启

 

1.9 反向代理之动静分离

1.9.1     动静分离需求分析

 

Nginx动静分离简单来说就是把动态和静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。严格意义上说应该是动态请求和静态请求分开,可以理解成使用Nginx处理静态请求,Tomcat处理动态请求。

动静分离从目前实现方式大致分为两种:

一是纯粹的把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案。

二是动态和静态文件混合在一起发布,通过nginx分开。通过location指定不同的后缀名实现不同的请求转发。

1.9.2     动静分离具体实现

  1. index.jsp修改

node2

<link rel="stylesheet" type="text/css" href="/css/index.css">

<img src="/image/logo.jpg" ><br/>

<font class="myfont">

from 192.168.20.102 <br/>

 session=<%=session.getId()%></font>

node3

<link rel="stylesheet" type="text/css" href="/css/index.css">

<img src="/image/logo.jpg" ><br/>

<font class="myfont">

from 192.168.20.103 <br/>

session=<%=session.getId()%></font>

 

  1. 从nginx1克隆nginx2,修改/etc/udev/ rules.d/70-persistent-net.rules

[root@nginx2 rules.d]# pwd

/etc/udev/rules.d

[root@nginx2 rules.d]# vim 70-persistent-net.rules

将原来的eth0一行删掉(dd),并将eth0,改为eth1

  1. 在nginx2服务器上创建目录 /data/image和/data/css,然后将logo.jpg和index.css上传到对应的目录

[root@nginx2 rules.d]# mkdir -p /data/image  /data/css

  1. 并修改nginx2服务器上的nginx.conf配置文件

 [root@nginx2 rules.d]# vim /opt/nginx/conf/nginx.conf

    server {

       listen  80;

       server_name 192.168.20.86;

       location / {

           root /mnt;

           autoindex on;

       }

       location /image {

           root /data;

       }

       location /css {

           root /data;

       }

}

  1. 启动memcached

memcached -d -m 128m -p 11211 -l 192.168.20.85 -u root -P /var/run/memcached/memcached.pid

 

  1. 修改nginx1服务器上的nginx.conf配置文件

    server {

       listen  80;

       server_name www.123.com;

       location / {

           proxy_pass http://rss/;

       }

       location /image/ {

           proxy_pass http://192.168.20.96;

       }

       location /css/ {

           proxy_pass http://192.168.20.96;

       }

    }

然后重新加载nginx

  1. 浏览器测试http://www.123.com

 

posted @ 2021-03-15 19:06  LesterLin  阅读(48)  评论(0)    收藏  举报