Nginx是一个高性能的http服务器和反向代理服务器,是一个高度模块化的web服务器,和Apache的模块化不同,Nginx的模块不支持动态编译,Nginx要加入新的第三方模块的时候,必须先下载模块,然后重新编译Nginx,而Apache只需要将新加入的模块编译成so文件,然后配置文件指定是否加载即可,无需重新编译Apache。并且Nginx的rewrite模块会使用正则表示式进行匹配,因此需要pcre软件库的支持,另外ssl加密需要openssl-devel软件库的支持,gzip压缩传输需要zlib-devel软件库的支持。编译安装Nginx之前确定这三个软件库已经安装。
目录
- Nginx的配置文件结构
- Nginx的配置文件参数
- 虚拟主机
- LNMP架构
- 状态监控页面
- URL重写
- Nginx的负载均衡功能
- Nginx的缓存服务器应用
- Nginx的性能优化
- Nginx的I/O模型
1.Nginx的配置文件结构
通过epel源安装的Nginx的配置文件在/etc/nginx/目录下,对于编译安装的Nginx,其配置文件在安装目录下的conf目录下,Nginx配置文件以block块的形式组织,每个block以{...}为界限,block块中包含各种指令。整体结构分为main层、events块、http块,main是最高层,从上到下继承,位于main层的指令会向下继承至events和http,events块指令会继承至http。http往下分为多个server块,server块再往下分为location块,继承关系是从外到内。
一个完整的Nginx的配置结构如下,
... #全局块,包含main层指令,同时包含events块和http块 events { #events块,包含events指令 ... } http #http块,包含http指令和多个server块 { ... #http全局指令块 server #server块 { ... #server全局指令块 location [PATTERN] #location块 { ... } location [PATTERN] { ... } } server { ... } ... #http全局块 }
2.Nginx的配置文件参数
通过yum安装的Nginx,其配置文件类型如下图所示,当然了,Nginx都会默认为每种类型的配置文件提供一个.default结尾的文件用以参考。
主配置文件nginx.conf
对于rpm安装的Nginx而言,它的主配置文件是由/etc/sysconfig/nginx文件决定的,其中的NGINX_CONF_FILE参数定义主配置文件。
主配置文件的主要参数如下,关于main全局配置
user nginx nginx; 定义Nginx的worker进程的用户和用户组,默认是nobody
worker_processes auto; 定义开启worker进程的数量
error_log(日志错误级别debug|info|notice|warn|error|crit) 定义全局错误日志文件
pid 定义nginx的master进程ID的存储文件,防止程序启动多个实例
进程运行后会给pid文件加一个文件锁,只有获得该锁的进程才具有写权限,将自身的pid写入pid文件中,其他试图获得该锁的进程会退出。
关于events事件驱动配置
use epoll; 定义Nginx工作时的I/O模型,支持的I/O模型有select、poll、epoll等,其中select和poll是标准的方式,epoll是高效的工作方式,也是Nginx优先考虑的I/O模型。
work_connections 定义每个worker进程允许的最大的同时连接数,总之,Nginx能够同时打开的最大文件数=worker_processes*work_connections。
注意:Linux系统默认规定进程打开的最大文件数为1024,因此,如果work_connections的值大于1024的话,则无法生效。例如,设置worker_connnects 65535,Nginx在启动之前需要运行命令ulimit -n 65535将进程支持的最大文件数改为65535。
关于http块的配置
log_format 定义Nginx的日志输出格式,main为该日志的输出格式名称,下面的指令如果想要调用该日志格式的话,直接引用main关键词即可。
access_log /var/log/nginx/access.log main; 定义Nginx被访问的日志,同时引用log_format定义的以main为命名的日志格式。
日志格式中各变量的含义如下,
$remote_addr 客户端IP地址
$remote_user 客户端用户名称
$time_local 访问的时间与时区
$request 请求的URL
$status 请求状态(http的请求状态码)
$body_bytes_sent 发送给客户端的文件主体大小
$http_referer 记录从哪个页面链接过来的
$http_user_agent 客户端浏览器的信息
$http_x_forwarded_for 客户端的IP地址
注意:remote_addr和http_x_forwarded_for同样都是访问服务器的客户端IP,但是这两者还是有区别的,对于前端设置了反向代理的服务器,remote_addr是指反向代理服务器的IP地址,http_x_forwarded_for是指真实的客户端IP地址
charset utf-8 设置网页的编码格式,默认编码为utf-8,还有gb2312等
server_names_hash_bucket_size
服务器名字的hash表大小,nginx的多个server_name是存放在哈希表中的。这样可以提高nginx速度
client_max_body_size 客户端请求单个文件的最大字节数
当请求的文件数大于该值的时候,服务器会报出413(Request Entity Too Large)的错误
client_header_buffer_size 指定客户端请求头的buffer大小,一般情况下1K大小足以
large_client_header_buffers <number> <size>
请求头大小超过client_header_buffer_size的值时,会将请求头的大小重新设置为large_client_header_buffers的值。例如large_client_header_buffers 4 128K,最大缓存量为4个128K。
上诉两个请求头的buffer的关系,受到http请求时,先根据client_header_buffer_size分配一个buffer,如果buffer无法容纳请求头,那么会根据large_client_header_buffers重新分配buffer。
keepalived_timeout 客户端保持连接的超时时间
client_header_timeout 客户端请求头读取超时时间
client_body_timeout 客户端请求主体读取超时时间
上述请求头和请求主体的超时时间,如果超过时间客户端还没有发送数据,则Nginx返回408(Request time out)的错误。
send_timeout 服务端响应的超时时间,对于已经建立的连接,如果客户端在超时时间内没有任何动作,Nginx断开连接。
conf.d/和default.d/扩展配置文件目录 Nginx中引入扩展配置文件的目录是为了降低主配置文件nginx.conf的复杂度,Nginx的http块是由多个server块组成,每个server也是有多个location块组成的,这么多的server和location当然都可以放在一个主配置文件中,但还是server过多的情况下,会使得主配置文件变得十分庞大,而且也不便于管理与修改。因此引入了扩展配置文件目录。
conf.d/目录下存放server块的配置文件,default.d/目录下存放location块的配置文件,引用的时候使用include指令将相对应的server块配置文件和location配置文件加入各自的block块中即可。
http { ...<http全局指令>... include /etc/nginx/conf.d/*.conf; } 或者 server { ...<server全局指令>... include /etc/nginx/default.d/*.conf; }
fastcgi_params/fastcgi.conf
Nginx在fastcgi解析的时候,需要调用这两个文件中的服务器变量,内置的服务器变量如下,
这个文件的区别就是.conf比params多了一行fastcgi_param SCRIPT_FILENAME document_rootfastcgi_script_name;
uwsgi_params
和fastcgi_params一样,这里是调用关于uwsgi协议的一些服务器变量。
scgi_params
这里是调用关于scgi协议的一些服务器变量。
mime.types
web资源类型配置文件,定义web资源的类型,即文件扩展名和资源类型的映射表,nginx就是根据这个映射表设置http请求响应头的Content-Type值。
mime.types在http块的全局配置中被引用,下面的指令的意思是根据mime.types定义web资源类型,如果在映射表中找不到对应关系,则使用default_type定义的默认资源类型。
koi-win/koi-utf/win-utf
这三个文件都是与编码转换映射相关的文件,用于在输出内容到客户端时,将一种编码转换到另一种编码。
koi-win: charset_map koi8-r < -- > windows-1251
koi-utf: charset_map koi8-r < -- > utf-8
win-utf: charset_map windows-1251 < -- > utf-8
3.虚拟主机
对Nginx而言,一个server块就是一个虚拟主机,我这里将每个server块的配置单独作为一个文件放入conf.d/目录下。然后在主配置文件中include命令包含进来即可。
关于虚拟主机的指令如下,
server {...} 虚拟主机开始的关键字
listen address[:port] [default_server]
虚拟主机监听的IP和端口
default_server定义该server为默认,如果所有server段都没有使用该参数,则第一个server为默认
该参数通常在地址列没有输入server_name,而是输入IP地址的时候,Nginx会定位在default_server的虚拟主机。
例: listen 192.168.124.100:80 default_server
server_name 指定虚拟主机的域名,可以定义多个域名,之间用空格隔开,可以使用通配符和正则表达式
server_name的作用:Nginx收到一个http请求的时候,会取出首部的server字段,然后和所有server_name字段进行比较,匹配则定位到对应的server块;
server可以是多个,相对应的server_name也会有多个,多个server_name之间的优先顺序是:
精确匹配 www.linux.com
左侧通配符匹配 *.linux.com
这个左侧通配符匹配实际应用场景很广,www.linux.com、bbs.linux.com、blog.linux.com等都可以通过一个左侧通配符来完成
右侧通配符匹配 www.*
正则表达式匹配 ^.*.linux.com$
root 定义虚拟主机的网页根目录,可以是绝对路径,也可是相对路径
index 定义虚拟主机默认的首页文件
charset 定义网页的默认编码格式,既可以用于http全局,也可以用于server全局
access_log 同样既可以用于http全局,也可以用于server全局
location {...} 地址匹配开始的关键字,location块对URL地址匹配进行设置,支持正则表达式匹配
location和URL进行匹配,匹配成功则进行其中的处理。控制服务端的访问路径
= 精确匹配
~ 正则表达式匹配,区分大小写
~* 正则表达式匹配,不区分大小写
^~ URL前半部分匹配,忽略大小写
/ 通用匹配,因为所有的匹配都是以/为开始的
他们的优先级:
= -> ^~ -> ~或者~* -> /
例如:
location ^~ /bbs.txt { | location ^~ /bbs{ alias /var/www/html/1.txt; | alias /var/www/html/1.txt; } | } location = /bbs.txt { | location = /bbs{ alias /var/www/html/2.txt; | alias /var/www/html/2.txt; } | } 结果会优先返回优先级高的匹配URL | 结果会优先返回优先级高的匹配URL 即2.txt的页面内容 | 即2.txt的页面内容
alias 定义Nginx中别名功能,只能用在location中
tips:
root既可以写在server中,也可以写在location,当同时存在时,内层会替换外层。例如
root path1; location / { root path2; } location ~ .php$ { }
第一个location生效的是path2,第二个location生效的是path1.
root和alias的区别:
root指定的是根目录,alias指定的是当前目录,
location /html { root /data/web/;}# 如果URL请求的是/html/index.html,则nginx会在/data/web/html/目录查找index.html文件location /html { alias /data/web/; }# 如果URL请求的是/html/index.html,则nginx会在/data/web/目录下查找index.html文件location ~ ^/download/(.*) { alias /data/web/1;}# 如果URL请求的是/download/nginx.tar.gz,则nginx会在/data/web/目录下查找nginx.tar.gz文件
nginx中配置文件中的正则表达式依赖于pcre库,这也是安装nginx之前为什么必须先安装pcre的原因。
http_geoip_module模块可以处理来自不同地区的访问
include /usr/share/nginx/modules/mod-http-geoip.conf
Nginx -V # 查看Nginx编译过程中的参数
--sbin-path 定义开启Nginx的主进程的命令路径
--modules-path 定义Nginx的模块的存储路径
--conf-path 定义Nginx配置文件的路径
--error-log-path 定义错误日志的路径
--http-client-body-temp-path 客户端主体请求的临时文件的路径
--http-proxy-temp-pathNginx 代理(负载均衡)的临时文件的路径
--http-fastcgi-temp-path fastcgi的临时文件的路径
--with-file-aio 文件的异步IO
4.LNMP架构
现在大多数的电子商务网站都是基于LNMP(Linux Nginx MySQL PHP)架构设计的。LNMP的架构的简单的工作原理如下,
其中,Nginx作为web服务器,响应客户端发来的http请求,和Apache一样,本身只能处理静态请求,而对于动态请求,则需要Nginx与专门的语言解释器进行交互,例如PHP解释器,然后PHP解释器执行php脚本,其中会涉及到一些数据的操作,因此就需要到后端的数据库服务器中存取数据。最后php的执行结果交给nginx,nginx继而将结果返回给客户端。
fastcgi:
nginx收到的请求不能直接转发给php,因为php不认识,反过来,php处理后的结果也不能直接返回给nginx,因此需要在他们两者中间加入公共接口,即fastcgi,使得nginx和php都能够支持fastcgi,当nginx和php交互的时候,都需要通过fastcgi协议的处理。
PHP和php-fpm的关系:
php-fpm是php和fastcgi的管理器,它其实php的一个补丁,是为了将fastcgi的管理器整合到php中,这样一来,仅可以通过php-fpm服务,既可以管理fastcgi进程,也可以reload重新加载php(在修改php的配置文件php.ini后无需重新启动php)。
因此LNMP详细的工作流程是,Nginx收到浏览器的web请求后,Nginx 自身处理其中的静态请求,例如图片、视频等,对于动态请求,Nginx将其通过fastcgi处理后转发给php-fpm进程,php-fpm调用php解析器执行fastcgi处理后的php脚本,期间php再连接数据库进行数据的操作,php解析器处理完毕后,将结果转发给php-fpm进程,php-fpm将结果通过fastcgi交付给Nginx,最后Nginx将最终的结果返回给浏览器。
LNMP搭建过程:
① 安装Nginx,为了方便我这里使用yum安装,当然也可以使用源码编译安装,我使用的阿里云的epel源,直接在Linux中输入如下命令,就可以成功配置yum仓库。
[root@CentOS6 ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo [root@CentOS6 ~]# yum makecache [root@CentOS6 ~]# yum -y install nginx
我这里安装的nginx版本是1.10.2
② 安装数据库mysql,直接使用本地base源即可。
[root@CentOS6 ~]# yum -y install mysql-server
安装成功之后会发现也会为我们安装mysql客户端工具,用于连接mysql服务端。
然后创建用于存放数据的数据库wordpress,赋予用户wordpress使用该数据库的所有权限,并设置连接数据库的秘密centos,
[root@CentOS6 ~]# mysql mysql> create database wordpress; mysql> grant all on wordpress.* to wordpress@'%' identified by 'centos'; mysql> flush privileges;
③ 接下来就是php和及其扩展包的安装,同样使用base源即可
[root@CentOS6 ~]# yum -y install php php-mysql
④ 安装统一管理php解释器和fastcgi进程的工具php-fpm。
[root@CentOS6 ~]# yum -y install php-fpm
修改php-fpm的工作进程的用户和用户组为nobody.nobody(需要同Nginx工作进程的用户/用户组一致,我这里设置的是nobody.nobody),这样做的目的是限制php-fpm和Nginx的权限。php-fpm的配置文件是/etc/nginx/php-fpm.d/www.conf
修改后的内容如下,其他的指令保持默认。
⑤ 修改Nginx的配置,使其能将动态请求转发给php-fpm。
Nginx主配置文件的内容如下:
[root@CentOS6 nginx]# cat /etc/nginx/nginx.conf user nobody nobody; worker_processes auto; error_log /var/log/nginx/error.log; pid /var/run/nginx.pid; events { use epoll; worker_connections 1024; } http { 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 /var/log/nginx/access.log main; keepalive_timeout 65; charset utf-8; include /etc/nginx/mime.types; default_type application/octet-stream; include /etc/nginx/conf.d/*.conf; # 引用server块的扩展配置文件 }
Nginx的server块的扩展配置文件内容如下,
[root@CentOS6 conf.d]# cat /etc/nginx/conf.d/default.conf ## The default server#server { listen 80; server_name wordpress.linux.com; root /data/web; location / { index index.php; } location ~ .php { fastcgi_pass 127.0.0.1:9000; # 运行php-fpm进程的ip和端口 fastcgi_index index.php; # 定义网页的入口文件 fastcgi_param SCRIPT_FILENAME document_root
⑥ 测试LNMP架构是否成功
在nginx服务器的/data/web/(由root指令决定)目录下,创建index.php文件,写入连接数据库和php的测试代码
[root@CentOS6 ~]# cd /data/web[root@CentOS6 web]# cat index.php<?php
在windows的hosts文件中添加记录,
192.168.239.129 wordpress.linux.com
然后在浏览器中出现如下如所示的界面,表示LNMP架构已经搭建完成。
利用LNMP架构搭建博客,下载wordpress,将解压后的所有文件放在/data/web/目录下,需要保持这些文件的用户和用户组和nginx进程的保持一致,因此设置这些文件的用户和组为nobody。
[root@CentOS6 web]# chown -R nobody.nobody /data/web/*
然后修改其中的wp-config.php文件,该文件主要用于连接数据库配置,主要修改的内容如下,
/** The name of the database for WordPress */ define('DB_NAME', 'wordpress'); /** MySQL database username */ define('DB_USER', 'wordpress'); /** MySQL database password */ define('DB_PASSWORD', 'centos'); /** MySQL hostname */ define('DB_HOST', '192.168.239.129');
最后在浏览器中输入wordpress.linux.com,首次访问需要完成安装的步骤,按照指示一步步完成即可。最后再次访问该域名,出现如下界面表示博客搭建成功。这样LNMP的一个实际应用就已经实现了。
tips:
LNMP搭建网站的时候,首次都需要连接数据库的步骤,该步骤的完成通常都是修改源码中的config.php文件来完成的(例如该例中的wp-config.php),该文件中一般定义了要连接的数据库的名称,用户和用户口令等信息。
LNMP和LAMP的区别:
LAMP中的php是作为Apache的一个模块存在的,所以php和Apache共存亡;LNMP中的php是作为一个单独的服务存在的,通过php-fpm进行管理。
5.状态监控页面
Nginx内置了StubStatus的工作模块,该模块可以监控Nginx的工作状态。监控页面完成通过一个location就可以完成,location的配置如下,
location /status { stub_status on; # 开启状态监控的功能 access_log /var/log/nginx/status.log; # 定义状态页面的访问日志 # 采用auth_basic认证机制 auth_basic "NginxStatus"; # 定义状态页面的名称 auth_basic_user_file /etc/nignx/conf.d/htpasswd; # 指定密码文件 }
其实只需要stub_status on就可以开启Nginx的状态监控功能,和上边的区别就是没有日志记录文件,没有密码认证的功能。
另外auth_basic认证机制的密码文件需要Apache的htpasswd命令生成。
[root@CentOS6 default.d]# htpasswd -c /etc/nignx/conf.d/htpasswd admin # 允许登录的用户为admin,然后根据指示设置密码 # 因为nginx的工作进程为nobody,因此需要将密码文件对nobody用户有r--权限 [root@CentOS6 default.d]# chown nobody.nobody /etc/nignx/conf.d/htpasswd [root@CentOS6 conf.d]# chmod 400 htpasswd [root@CentOS6 conf.d]# ll htpasswd -r--------. 1 nobody nobody 20 Jul 19 03:03 htpasswd
然后在浏览器中输入wordpress.linux.com/status,输入正确的用户名和密码后,就可以看到状态页面。
Nginx的状态页面的参数含义:
Active connections 当前活动的客户端连接数
accepts 已经累计收到的客户端连接数量
handled 已经处理的客户端连接数量
requests 客户端的总的请求数量
accepts/handled/requests这三个参数的区别是:
客户端发起的总的请求数是requests,服务端可以不接受请求,也即实际接受的请求数是accepts,而服务器在接受的请求数中实际处理的请求数是handled
reading 正在读取的客户端请求数量
writing 正在发送响应报文的连接数量
waiting 等待发送请求的空闲连接数量
对于长连接,还需要等待用户发送数据等等的情况,因此会出现空闲连接。
6.URL重写
Nginx的配置文件支持类似shell编程的逻辑判断,对于不同的请求定义不同的规则。
URI/URN/URL的区别
URI 统一资源标识符,URI由URN和URL组成,mailto:John.Doe@example.com
URN 统一资源名称,代表资源的名称,John.Doe
URL 统一资源定位符,代表资源的路径,mailto:example.com
if判断指令
if (condition) {...},仅仅在server和location块中使用
condition是一个变量的时候,为空或者以0开头的字符串都将被判定为false
逻辑判断表达式:
=或!= 等于或不等于
正则表达式匹配:
~ 区分大小写的匹配
~* 不区分大小写的匹配
!~ 区分大小写的不匹配
!~* 不区分大小写的不匹配
文件/目录匹配:
-f/!-f 文件是否存在
-d/!-d 目录是否存在
-e/!-e 文件或者目录是否存在
-x/!-x 文件是否可以执行
Nginx内置的全局变量
$host 请求主机头字段,即server_name
$http_user_agent 客户端agent信息,即客户端使用的何种浏览器,这个值一定要根据日志access.log中显示的浏览器信息为依据,尤其是IE浏览器,之前我就以MSIE为IE的http_user_agent,结果实验老是失败。
$http_cookie 会话标识
$limit_rate 限制连接速率,例如网盘的限速
$request_meathod 客户端请求的动作
$remote_addr 客户端的IP地址
$request_filename 当前请求的文件路径
$scheme http方法(http或者https)
$server_protocol http协议版本
$server_addr 服务器IP地址
$server_name 服务器名称
$server_port 服务器的端口号
$request_uri 包含参数的URI,例如www.xxx.com/xxx.php?xxx
rewrite指令
只能用在server、location、if中,并且只能对URL中的非传递参数的字符串起作用。
rewrite和location都能实现地址跳转,但是两者还是有区别的,rewrite是在同一域名内改变资源路径,而location是对一类路径做访问控制,他们的执行顺序是server中rewrite->location-> location中rewrite。
rewrite重复执行超过10次还找不到资源就会返回500错误代码。
rewrite指令的格式:rewrite <regex> <替换后的URL> [flag]
重定向的标志位flag
last 表示完成rewrite,一般用在server和if中,浏览器地址栏的URL不变
break 通常使用在locating中,表示后续的rewrite不再执行,浏览器地址栏的URL不变
redirect 返回302临时重定向,浏览器地址栏显示跳转后的地址
permanent 返回301永久重定向,浏览器地址栏显示跳转后的地址
例子:
以上边LNMP搭建的博客为例,在浏览器中的域名后边随便输入一个字符串表示一个不存在的目录或者文件,然后浏览器默认会返回404的错误页面。
现在使用yum安装nginx时其默认提供的404错误页面,而不是使用浏览器提供的404错误页面。
cp /usr/share/nginx/html/404.html /data/web/
server { listen 80; server_name wordpress.linux.com;... if (!-e request_filename) { rewrite ^(.*) /404.html break; }... } 如果访问的文件或者目录不存在,则重定向到404.html页面
server { listen 80; server_name wordpress.linux.com; ... if ($http_user_agent ~ "Firefox") { rewrite ^(.*)$ http://www.baidu.com/ redirect; ... } 域名跳转
现在使用火狐浏览器访问,结果成功的跳转到百度的首页。
然后使用360浏览器,结果域名没有跳转。
7.Nginx的负载均衡功能
Nginx拥有基于7层交换的负载均衡功能,对于后端10台左右的web服务器的情况,Nginx的负载均衡性能最佳。因此Nginx适用于小型网站的负载均衡。Nginx通过upstream命令实现负载均衡功能。Nginx的负载均衡模块支持的常用负载均衡算法如下,
轮询 Nginx默认调度算法,按请求时间将请求逐一分配到后端服务器
加权轮询 指定后端服务器的权值,权值越大,请求被分配的概率越大, 轮询是权值都为1的加权轮询
ip_hash 根据客户端IP的哈希结果分配请求,这样同一客户端的请求就可以分配到固定的后端服务器,可以解决session共享的问题
fair 根据后端服务器的响应时间分配请求,响应时间短的优先分配。
url_hash 根据访问的URL的哈希结果分配请求,这样同一URL的请求就可以分配到固定的后端服务器。
Nginx的负载均衡调度算法中,轮询、加权轮询、ip_hash是Nginx自身提供的,而fair、url_hash分别由第三方模块upstream_fair模块和hash软件包提供。
Nginx负载均衡功能的配置格式:
upstream xxx { server ip1:port1 status; server ip2:port2 status; ...}server { ... location ...{ proxy_set_header Host host; proxy_set_header X-Forward-For remote_addr; proxy_pass http://xxx; ... }}# xxx为定义的后端服务器群的名称# ip:port为后端提供web服务的ip和端口号# status是后端服务器群各个节点的状态
proxy_set_header的作用:
该指令的意义在于修改http请求头的信息。这里修改的主要是请求头中的host字段和remote_addr字段。在设置了负载均衡之后,客户端的请求先到达负载均衡层,再到真实的web服务器,由此请求经过了一个中间层。Host的含义是表明请求的主机名,因为nginx作为反向代理使用,而如果后端真实的服务器设置根据http请求头中的host字段来进行路由或判断功能的话(即根据host字段判断请求发往哪个虚拟主机),如果反向代理层的nginx不重写请求头中的host字段,将会导致请求失败(默认反向代理服务器会向后端真实服务器发送请求,并且请求头中的host字段应为proxy_pass指令设置的服务器,也就是上边的xxx)。x-forward-for表示http请求由谁发起的,如果负载均衡层不重写该字段的ip,则后端真实服务器收到的http请求头中都是负载均衡服务器的ip地址,如果后端有防×××策略的话,那么负载均衡服务器就被真实服务器封掉了。
status的常用状态有
down 表示该web节点不参与负载均衡的调度
weight 定义该web节点的权值
backup 表示预留web节点,只有当所有的非backup的web节点出现故障的时候,backup节点才会接受请求
max_fails 表示请求该web节点的最大的失败次数
fail_timeout 表示该节点在经历max_fails次的失败请求之后,暂停服务的时间
实验环境:
192.168.239.130:80 前端负载均衡节点
192.168.239.129:80 后端提供web服务的节点1
192.168.239.132:80 后端提供web服务的节点2
负载均衡节点的nginx.conf内容如下:
user nobody nobody; worker_processes 2; error_log /usr/local/nginx/logs/error.log notice; pid /usr/local/nginx/logs/nginx.pid; events { use epoll; worker_connections 1024; } 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 /usr/local/nginx/logs/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 60; gzip on; upstream webserver { # web1 server 192.168.239.129:80 weight=4 max_fails=3 fail_timeout=20s; # web2 server 192.168.239.132:80 weight=3 max_fails=3 fail_timeout=20s; } server { listen 80; server_name blog.linux.com; root /data/html; index index.php index.html index.htm; location / { proxy_set_header Host $host; proxy_set_header X-Forward-For $remote_addr; proxy_pass http://webserver; }
节点1就是4.LNMP架构搭建的web服务,这里不再重写其nginx.conf文件的内容
节点2的nginx.conf的配置内容如下:
user nobody nobody;worker_processes auto;error_log /var/log/nginx/error.log;pid /run/nginx.pid; events { use epoll; worker_connections 1024;} http { log_format main 'remote_addr - remote_user [time_local] "status body_bytes_sent "http_referer" ' '"
然后在节点2的web根目录下创建index.html文件。
[root@CentOS7 ~]# echo "this is web2" > index.html
这样负载均衡服务器和后端服务器已经搭建完成,现在在浏览器中输入负载均衡服务器的ip,不停地刷新,结果会交替返回节点1和节点2的web页面。
8.Nginx的缓存服务器应用
这里使用的是Nginx自带的缓存模块,通过proxy_cache指令来实现。用于反向代理时对后端web服务器进行缓存。这里的实验以前一小节搭建负载均衡服务器为前提。在负载均衡服务器上设置缓存。nginx.conf文件中增加的内容如下:
http { ... proxy_cache_path /data/cache levels=1:2 keys_zone=cache_one:1m inactive=1h max_size=1g; proxy_temp_path /data/temp_cache; server { ... location / { proxy_cache cache_one; proxy_cache_valid 200 304 302 12h; proxy_cache_key hosturiis_argsargs; proxy_set_header Host host; proxy_set_header X-Forward-For remote_addr; proxy_pass http://webserver; expires 1d; } }}
proxy_cache_path
/data/cache 定义缓存文件的目录
levels=1:2 定义目录深度,并且第一层目录为1个字符,第二层目录为2个字符。
缓存文件是以proxy_cache_key指令定义的key值进行hash得到的结果,例如生成的缓存文件为f9e228c5ead32aa97fe65df97a5b8196,从文件名的最右边开始,第一层目录的目录名为6,第二层目录的目录名为19。
keys_zone cache_one为自定义的缓存区的名称,1m表示内存缓存空间大小为1M
inactive 定义自动清除没有第二次访问的缓存文件的时间
max_size 当内存缓冲空间不足时,需要将缓存文件放到硬盘中,这里的max_size为存放缓存文件的硬盘空间最大值
proxy_temp_path 定义临时缓存文件路径
proxy_cache 反向代理设置缓存的指令,格式为proxy_cache zone|off,关闭为off,开启直接写keys_zone指令定义缓存区名称
proxy_cache_valid 对不同http状态码的页面设置不同的缓存时间
proxy_cache_key 定义以什么样的key值得到文件名
这样反向代理缓存已经配置完成,重启nginx,然后在查看进程状态,当出现cache manager process进程时表明缓存已经建立成功。
并且生成了缓存文件目录。
最后在浏览器中输入负载均衡服务器的ip地址,当返回页面内容的时候,查看/data/cache/目录,结果生成了缓存文件。
查看该缓存文件正是刚才返回的页面的请求头信息和页面内容信息。
9.Nginx的性能优化
提高Nginx进程和CPU的亲缘性,减少进程上下文的切换
worker_process 开启worker进程数
worker_cpu_affinity 开启利用多核cpu,将worker进程绑定至CPU,提高CPU的性能
例如:两核CPU,两个进程
worker_process 2;
worker_cpu_affinity 01 10;
目的:
提高CPU的缓存命中率,设置worker进程数和CPU核心数一致,并且将一个worker进程绑定至一个CPU核心,这样就可以避免CPU缓存从一个核心切换到另一个核心,减少进程上下文的切换的时间。当然上诉情况仅用于CPU密集型,对于网络密集型和I/O密集型的情况,可以设置CPU核心数是worker进程数的1.5-2倍。
taskset命令
查看某个进程运行在哪个CPU核心上。可以将某个进程运行在指定的CPU核心上
taskset -pc PID 查看PID的进程运行在哪个CPU核心
taskset -c <CPU核心> <指定命令> 定义指定命令开启的进程运行在指定CPU核心
[root@CentOS6 ~]# ps -ef |grep ping root 13707 8104 0 11:14 pts/0 00:00:00 /bin/ping www.baidu.com root 13709 9069 0 11:14 pts/1 00:00:00 grep ping [root@CentOS6 ~]# taskset -pc 13707 pid 13707's current affinity list: 0,1 [root@CentOS6 ~]# ps -ef |grep ping root 13729 8104 0 11:18 pts/0 00:00:00 /bin/ping www.baidu.com root 13731 9069 0 11:18 pts/1 00:00:00 grep ping [root@CentOS6 ~]# taskset -pc 13729 pid 13729's current affinity list: 0
提高处理性能
开启epoll事件处理模型,提高处理效率--use epoll
multi_accept 开启多请求处理机制,worker按串行方式处理连接,一个连接只有一个worker,其他的worker处于休眠状态--multi_accept on
sendfile on 避免内核缓冲区数据和用户缓冲区数据之间的拷贝
提高并发
worker_connections 决定允许客户端每个进程的最大连接数,一般65535足够了--worker_connection 65535
worker_rlimit_nofile 能够打开的最大文件数--worker_rlimit_nofile 65535
提高连接复用率
keepalive_timeout 60 连接超时时间
tcp_nodelay on 提高高频发送小数据报文的实时性
tcp_nopush on 允许将http首部和内容在同一个报文中发送
开启Nginx压缩功能,节省带宽
gzip on
gzip_comp_level 3 压缩比,1-9,默认是1
gzip_min_length 设置页面开始压缩的最小字节数,默认为0,建议设置为1K,小于1K的页面压缩效果不好。
页面的字节数就是http头的Content_Length
gzip_buffers 设置内存大小作为压缩缓存
例如gzip_buffers 4 16K 设置4个16KB的内存作为压缩缓存
gzip_http_version 设置http协议版本,默认1.1即可
gzip_types 指定被压缩文件的类型
gzip_vary on 让前端的缓存服务器缓存经过压缩的页面
fastcgi性能优化
fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2 keys_zone=TEST:10m inactive=5m
fastcgi缓存文件路径,目录等级结构、关键字区域存储时间和非活动删除时间
fastcgi_connect_timeout 指定连接到后端fastcgi的超时时间
fastcgi_send_timeout 指定向fastcgi传送请求的超时时间
fastcgi_read_timeout 指定接受fastcgi应答的超时时间
fastcgi_buffer_size 指定读取fastcgi应答第一部分需要大多的缓冲区
fastcgi_buffers 指定本地需要多少和多大的缓冲区来缓冲fastcgi的应答请求
php-fpm参数优化(php-fpm的配置文件)
request_terminate_timeout 脚本运行超时时间
pm = dynamic | static 调整php-fpm进程的工作模式
pm.max_children 静态模式下的php-fpm进程数量
pm.start_servers 动态模式下的php-fpm的起始进程数量
pm.min_spare_servers 动态模式下的php-fpm的最小进程数量
pm.max_spare_servers 动态模式下的php-fpm的最大进程数量
pm.max_requests php-fpm进程处理多少个请求后销毁
request_slowlog_timeout 慢查询日志时间
slowlog 慢查询日志路径
rlimit_files 打开的最大文件数
针对Nginx的Linux内核参数优化
net.ipv4.tcp_syncookies = 1 开启SYN cookies
net.ipv4.tcp_max_tw_buckets = 6000 设置timewait的数量
net.ipv4.ip_local_port_range = 1024 65000 设置允许系统打开的端口范围
net.ipv4.tcp_tw_recycle = 1 启用timewait的快速回收
net.ipv4.tcp_tw_reuse = 1 启用timewait的重用,就是将TIME-WAIT sockets重新用于新的tcp连接
net.core.somaxconn = 262144 设置系统同时发起的tcp连接数
net.core.netdev_max_backlog = 262144 当网络接口接受数据包的速率大于内核处理速率的时候,设置发送到队列的数据包的最大数量
net.ipv4.tcp_max_orphans = 262144 设置系统最多多少个TCP套接字不被关联到用户文件句柄
net.ipv4.tcp_max_syn_backlog = 262144 设置尚未收到客户端确认信息的最大连接请求最大值
net.ipv4.tcp_synack_retries = 1 设置内核放弃连接之前发送syn ack数据包的数量
net.ipv4.tcp_syn_retries = 1 设置内核放弃建立连接之前发送syn数据包的数量
net.ipv4.tcp_fin_timeout = 1 设置套接字保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_keepalive_time = 30 TCP发送keepalive消息的频度,单位是小时
10.Nginx的I/O模型
Web服务器的网络I/O流程:
(1)客户发起请求到服务器网卡;
(2)服务器网卡接受到请求后转交给内核处理;
(3)内核根据请求对应的套接字,将请求交给工作在用户空间的Web服务器进程
(4)Web服务器进程根据用户请求,向内核进行系统调用,申请获取相应资源(如index.html)
(5)内核发现web服务器进程请求的是一个存放在硬盘上的资源,因此通过驱动程序连接磁盘
(6)内核调度磁盘,获取需要的资源
(7)内核将资源存放在自己的缓冲区中,并通知Web服务器进程
(8)Web服务器进程通过系统调用取得资源,并将其复制到进程自己的缓冲区中
(9)Web服务器进程形成响应,通过系统调用再次发给内核以响应用户请求
(10)内核将响应发送至网卡
(11)网卡发送响应给用户
用户请求-->送达到用户空间-->系统调用-->内核空间-->内核到磁盘上读取网页资源->返回到用户空间->响应给用户。
客户端向Web服务器请求的过程中,有两个I/O过程,一个就是客户端请求的I/O,另一个就是Web服务器请求页面的磁盘I/O
I/O模型
同步/异步(以被调用者是否有消息通知机制为依据)
同步--调用者主动等待被调用者返回结果
异步--调用者只需被调用者的消息通知,期间无需特地等待结果
阻塞/非阻塞(以调用者等待结果之前的状态为依据)
阻塞--结果返回之前,调用者被挂起
非阻塞--结果返回之前,调用者不会被挂起
通常就是同步阻塞和异步非阻塞,Nginx采用的就是异步非堵塞I/O模型。
例子:
①老王以传统的水壶烧水,老王相当于调用者,水壶相当于被调用者,老王必须一直盯着水壶,等待水烧开,这就是同步,后来老王换了一个响水壶,可以在水烧开的时候发出响声(加入消息通知机制),老王再也不用死盯着水壶(无需等待),水烧开的时候,发出响声通知老王即可,这就是异步。
②水壶在烧水的期间,老王不干别的事情,这就是堵塞,水壶在烧水的期间,老王去看个电视啥的,这就是非堵塞。
I/O复用
进程可以监听多个文件描述符
select--I/O复用模型,监听端口数量受限于/proc/sys/fs/file-max,采用轮询方式扫描端口
poll--I/O复用模型,本质和select没有区别,poll将数据放置在内核空间,select将数据放置在用户空间,放置在内核空间之后,效率提高,并且最大连接数没有限制
epoll--I/O复用模型,select和poll的增强版