一、基础概念
engine X, 是一个高性能的HTTP服务器和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务,nginx因高性能、稳定、丰富的功能集、配置简单和低资源消耗而知名,是众多用于解决C10K问题的服务器之一。跟传统的服务器不同,nginx并不依赖线程处理请求,是一个具有扩展能力基于事件驱动或异步I\O的实现,在非常繁重的生产环境中,它只需要很少的内存资源,即便你没有机会处理那么大的并发量,依然可以从nginx的高性能和低内存消耗中受益
1. nginx的架构
由两级程序结构组成:master、worker。master负责管理worker,每一个worker都能处理多个用户请求,可以通过http、fastcgi、memcache协议代理至后端服务器,nginx也可以管理缓存,后端服务器的响应内容可以先缓存在本地,通过cache loader(缓存加载器)和cache manager(缓存管理器)管理缓存空间
2. Nginx特性
- 高度模块化设计、有较好的扩展性,早期的的模块不支持DSO机制,近期版本支持模块动态装载和卸载
1. 核心模块(core module):nginx的主核心模块 2. 标准http协议类模块 3. Standard http modules:标准的http模块 4. option http modules:可选的http模块 5. Stream modules:流模块,主要实现四层代理的模块 6. mail modules:邮件模块 7. 第三方类模块(3rd party modules),此种模块需要在编译时手动指定模块位置,才能编译成nginx的组成部分
|
- 高可靠性:其主控进程(master)不接收并响应任何用户请求,主要负责读取并验证配置文件、创建绑定或关闭套接字、启动终止维护worker进程的个数、无需重启进程就可以让新配置生效,甚至完成平滑版本升级等。而子进程(worker)有些负责缓存加载、有些通过反向代理模块代理至后端服务器,而有些负责响应用户请求
- 低内存消耗:10000个keep-alive模式下的connection,仅需要2.5MB内存
- 不停机更新配置文件
- 不停机滚动日志文件
- 平滑升级:不用停服务就可以升级程序版本,其先升级master进程,而后新来的请求用新版本worker响应,等老的worker线程都响应完请求后,在关闭老的worker进程
- 支持事件驱动机制:nginx利用了libevent(高性能的网络服务程序库),可以实现了epoll(),默认使用边缘触发
- AIO(异步I\O)
- mmap(内存映射):一次I\O分为两步,先从磁盘到内核内存,在从内核内存到进程内存,而mmap通过从磁盘到内存的映射,省去的复制的过程
- 支持sendfile和sendfile64:数据从磁盘到内核内存,不用在复制到进程内存,直接构建成响应报文,响应给客户端。sendfile只能发送很小的文件,默认不会超过几K,而sendfile64支持更大的文件发送
3. I/O类型
从不同的角度划分可以分为两种不同的I\O类型
(1) 同步和异步
同步(synchronous)、异步(asyncronous),此种分类关注的是消息通知机制。简单来讲就是调用发出之后,被调用者如何通知调用者
- 同步:调用发出之后,被调用者不会立即返回调用结果,但一旦返回的即是最终结果
- 异步:调用发出之后,被调用方立即返回消息,但返回的并非最终结果。待结果处理完毕后,被调用者通过状态、通知机制或回调函数来通知调用者处理
(2) 阻塞和非阻塞
阻塞(block)、非阻塞(nonblock),此种分类关注的是调用者等待返回调用结果时的状态
- 阻塞:调用结果返回之前调用者会被挂起(转为不可中断睡眠状态),只有在得到返回结果之后,才能继续
- 非阻塞:调用者在结果返回之前不会被挂起,所以本次调用操作不会阻塞调用者
4. I/O模型
以一次进程的读取磁盘资源为例,讲解以下几种I/O模型的不同之处。不过首先需要知道是,用户空间的进程是没有权限访问硬盘资源的。当进程需要read某个文件时
- 第一步:进程先向内核发起调用,内核读取文件到自己的内存空间
- 第二步:内核把内存空间的数据复制到进程内存空间
(1) 阻塞式I/O(blocking I/O)
调用者发出调用请求给内核时,调用者会阻塞在等待内核把数据准备好的过程中,等待的过程会被挂起并转为不可中断睡眠,一直阻塞到两步过程结束
(2) 非阻塞式I/O(nonblocking I/O)
调用者发出请求后,内核会立即回应请求已收到,但是此时数据并没有准备好,不过其不会阻塞调用者,此时调用者会进入盲等待阶段,隔一段时间就会来问数据有没有准备好,所以此种方式并不会比阻塞I/O性能提升多大。需要注意的时,第一步内核准备数据的时候虽然不会阻塞进程,但是在第二步内核将数据复制到进程内存的时候依然会阻塞进程
(3) 复用型I/O(I/O multiplexing)
此种方式调用者不会直接向内核发出调用请求,而是向内核代理发出调用请求。每个调用者与代理进行沟通调度,此种模型中调用者会阻塞在代理阶段,只不过代理还可以接受其他调用请求,当内核将数据准备完毕后,调用者会再次阻塞在数据复制到自己的内存空间中的过程
select()
:BSD风格的内核调用代理,其内生性限制最多代理不能超过1024个请求,httpd的prefork模式就是基于select()
调用模型实现的
poll()
:SysV风格的内核调用代理
(4) 事件驱动型I/O(signal driven I/O)
调用者发出调用后不会被阻塞,内核会立即通知进程请求已收到,等数据准备完成后内核会通知进程过来复制数据,此种方式调用者会阻塞在第二段,httpd的event模型就是基于此种调用模型实现的。内核的通知机制有两种
- 水平触发通知机制:多次通知,直到进程响应为止
- 边缘触发通知机制:只通知一次,如果进程没响应的话内核会将本次通知存储在一个特定的位置,而后进程通过回调函数获取此通知事件,nginx最初设计时就是参照事件驱动模型边缘触发机制设计的
(5) 异步I/O(asynchronous I/O)
调用发出后不会阻塞进程,内核在后台默默的完成第一、二步后立即通知进程,接着进程就可以立即打包数据给客户端,整个过程都是非阻塞
二、yum安装
1. yum安装
创建指向nginx官方的源,这里使用的是官方站点的yum文件内容
[nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true ---------------------------------------------------------------------
我自行删减了一些内容 [nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=0 enabled=1
|
2. 源码
-
第一步:安装开发环境及依赖到的库文件,如pcre-devel包,nginx实现url重写时需要用到pcre扩展的正则表达式
yum groupinstall "Development Tools" "Server Platform Development"
yum install pcre-devel openssl-devel
-
第二步:编译安装,如果编译时指定了临时目录,记得创建目录
./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf --sbin-path=/usr/sbin/nginx --user=nginx --group=nginx --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 --with-threads --with-file-aio --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_flv_module --with-http_mp4_module --with-debug
--sbin-path=PATH:主程序的路径 --conf-path=PATH:主配置文件的路径 --lock-path=PATH:锁文件路径 --http-client-body-temp-path=PATH:客户端上传数据时可能会上传很大的数据,这些数据不能都保存在内存中,这个选项就是定义客户端上传 的数据,临时存放的位置 --with-http_stub_status_module:启用nginx状态页面 --with-http_gzip_static_module:启用压缩静态内容的gzip模块 --with-http_flv_module:支持流媒体模块 --with-http_mp4_module:支持mp4模块 --with-threads:新版nginx基于线程池响应,性能更好 --with-file-aio:文件异步I\O
--------------------------------- 在编译过程中如果报错:nginx: [emerg] getpwnam("nginx") failed,则说明没有nginx用户
|
三、配置文件
1. main配置段:全局配置段
主要配置nginx进程自己的特性的
-
daemon {on|off}
:是否以守护进程方式运行nginx,如果为off则运行为前台,用于调试使用
-
error_log path level
:定义错误日志路径及记录级别
-
master_process {on|off}
:是否以master、worker模型来运行nginx,调试问题时可以设定为off
-
timer_resolution #ms
:时间解析度。每一次接收请求,内核都会发起系统调用gettimeofday()
去记录时间信息,比如记录日志,每一次记录时间都要发起gettimeofday()
系统调用,如果过于频繁,会大量浪费资源进行模式切换
-
worker_cpu_affinity:0001 0010 0100
,cpu亲缘性,绑定worker进程
-
worker_priority #
:worker进程的nice值,nice值越小,优先级越高。取值范围为-20 - 19
-
worker_processes
:worker进程个数,通常比物理核心数少一到两个
-
worker_rlimit_core
:所有的worker进程所能够使用的核心文件大小
-
worker_rlimit_nofile #
:单个worker进程能够打开的文件数量上限
2. event配置段
定义event模型工作特性
-
accept mutex {off|on}
:master调度用户请求至各worker进程时使用的负载均衡锁,on表示能让多个worker轮流、序列化的响应新请求,off表示请求来了会通知所有worker进程,谁先拿到谁响应,on保证起点公平,off保证结果公平
-
accept_mutex_delay 500ms
:如果启用了负载均衡锁,被挑选的worker进程由于正在响应其他用户请求导致过于繁忙无法响应,那么剩余的worker进程要等待多少时间后自动接替这个请求
-
lock_file file
:accept_mutex用到的锁文件路径
-
use [epoll|rtsig|select|poll]
:定义使用的事件驱动模型
-
worker_connections #
:一个worker进程所能够接收的最大并发连接数量,所能够接收的最大数量与最大进程数有关
3. http配置段
定义http协议相关配置
(1) 基本配置
格式: server { listen # :定义监听端口 serner_name NAME:名称可以使用通配符,匹配优先级为完全匹配 --> 左* --> 右* --> 正则表达式匹配 root PATH:在url下找本目录,起点为左边/ alias:与url相替换,起点为右边/ }
|
root和alias指令的区别
location /images { root "/vhost/web" }
location /images { alias "/vhost/web" } root:访问http://busyops/images/a.jpg --> 系统路径/vhosts/web/images/a.jpg alias:访问http://busyops/images/a.jpg --> 系统路径/vhosts/web/a.jpg
|
location [ = | ~ | ~* | ^~ ] uri { ... }
:引入新的上下文
匹配优先级:优先级从上打下 =:精确匹配,必须严格和uri的名称一样 ^~:URL的前半部分匹配,前半部分不包括片段、参数部分,不支持正则表达式 ~:正则表达式模式匹配,区分字符大小写 ~*:正则表达式,不区分字符大小写 不带符号优先级最低
|
匹配示例: location = / { [ configuration A ] }
location / { [ configuration B ] }
location /documents/ { [ configuration C ] }
location ^~ /images/ { [ configuration D ] }
location ~* \.(gif|jpg|jpeg)$ { [ configuration E ] } 请求/,A和B会被匹配,但是A为=,精确匹配,返回A 请求index.html,返回B 请求/documents/document.html,B和C会被匹配,但C更精确,反回C 请求/images/1.gif,B、D、E会匹配,但是^*优先级略高,会返回D 请求/documents/1.jpg,B、C、E会匹配,但是B、C无符号,所以优先级略低,返回E
|
示例: error_page 404 /404.html; error_page 500 502 503 504 /50x.html;
|
stub_status {on|off}
:状态页面,仅能用于location上下文
配置示例: location /status { stub_status on; allow 192.168.2.208; deny all; }
|
状态页面输出信息 Active connections:当前所有处于打开状态的连接数 server accepts handled requests:已经接受链接、已经处理链接、总处理请求数 93 93 88 accept:接受的请求数,有些链接可能拒绝了,比如认证失败 handled:已经处理完成的请求数 requests:客户端发来的总请求数 Reading: 0 Writing: 1 Waiting: 正处于接受请求状态、正在发送请求报文、保持链接状态并且活动状态的链接数
|
-
types_hash_max_size #
:nginx内部为了加速对资源类型分析,在其启动的时候会把mime类型文件直接装入内存。因此当客户端请求到来时nginx就可以立即在内存中分析出请求的资源类型(这些类型都是通过hash在内存中保存的),如果在磁盘上根据字符串比较效率就太低了,这个选项就是定义保存hash类型表的最大值
-
tcp_nodelay on|off
:当客户端请求的资源很小时,服务器可以将多个请求的资源合并在一起发送,避免多个小资源都需要单独封装发送,但是其必然会产生延迟,并且仅对保持链接有效,因为短连接每一次请求都需要单独建立一次连接并单独发送一次资源
-
sendfile <on|off>
:当服务端收到请求,内核会将数据从磁盘读取到内核内存,接着不用把资源复制到进程内存,而是直接构建成响应报文向客户端发送
-
tcp_nopush
:此项只有在sendfile on时有效,因为sendfile的功能可以让内核直接把资源发送给客户端而不用复制到进程内存,但是http首部及其他信息需要应用层封装,这样就会略晚于主体部分到达客户端。如果开启tcp_nopush,内核会等待应用层把主体部分准备好合并在一起并发送到客户端
(2) 链接相关
-
keepalive_timeout #
:持久链接的超时时间
-
keepalive_requests #
:一次持久链接允许请求的最大资源数
-
keepalive_disable [msie6|safari|none]
:表示为指定类型的User Agent禁用长链接,现在浏览器几乎都支持长连接,不过对于网络爬虫可启用
-
client_header_timeout #
:读取http请求报文首部的超时时长,有时服务器带宽有限,客户端半天挤不进来,就要调长一点
-
client_body_timeout #
:读取http请求报文body部分的超时时长
-
send_timeout #
:发送响应报文的超时时长。客户端接收的特别慢,只好超时
-
client_body_buffer_size size
:设置客户端上传缓冲大小,默认为16k,一旦超过定义大小,就将暂时由client_body_temp_path所指定的临时磁盘路径存储
-
client_body_temp_path path [level1[level2[level3]]]
:设置客户端上传临时文件存储目录,有几个level表示就有几层目录结构,相应的level上的数字是几,就表示用几个十六进制的字母命名,示例client_body_temp_path /var/tmp/client_body 1 2 2
-
limit_rate #
:设置客户端传输速率,默认是字节每秒,0表示无限制
(3) 压缩相关
-
gzip on|off
:是否打开压缩,牺牲cpu时钟周期
-
gzip_comp_level N
:定义压缩比,数字越大压缩比越高,但也越消耗CPU,可调成范围为1到9
-
gzip_disable regex
:匹配哪种浏览器不压缩
-
gzip_min_length length
:压缩最小阈值
-
gzip_buffers # size
:表示为了实现压缩机制,启动多少个内存段,每一段有多大。默认为32 4K或16 8K,表示一共有32个缓冲区,每一个4K。或者启用16个缓冲区,每一个8K。此处默认值到底是哪个和你的系统是32位或64位有关
-
gzip_proxied off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any
:nginx充当代理服务器时,对于后端服务器响应报文,在何种条件下启用压缩功能
-
gzip_types mime-type
:指明哪种媒体类型做压缩,默认包含html,根据情况加入text/xml、text/css、
application/javascript、text/plain
-
gzip_vary on|off
:如果启用压缩,是否在响应报文首部插入vary:accept-encoding
(4) 文件操作
-
aio on|off|threads[=poll]
:是否启用aio
-
directio size|off
:直接io。当文件大于给定大小时,同步写磁盘
-
open_file_cache off
:nginx可缓存文件元数据,包括文件描述符、文件大小、最近一次修改时间、目录结构及没有找到或没有权限访问的文件相关信息。示例:open_file_cache max=N [inactive=time]
,max=N是可缓存上线条目,到上线后根据LRU(最近最少使用)算法淘汰数据,inactive=time:缓存项的非活动时长,在此处指定的时长内未被命中或命中次数少与open_file_cache_min_uses指令所指定次数的缓存项即为非活动项,将被删除
-
open_file_cache_min_uses #
:指定时长内至少被命中次数,与open_file_cache结合使用
-
open_file_cache_valid time
:多长时间检查一次缓存,默认为60秒
-
open_file_cache_errors on|off
:错误界面缓存是否开启
(5) 安全相关
- 基于ip的访问控制:在需要做访问控制的配置段中加入相应的指令即可
allow IP/Network; deny IP/Network;
|
- 基于用户的访问控制:支持basic、digest算法
1. 在需要认证的页面中加入配置
auth_basic “string”:指定认证提示信息 auth_basic_user_file /path:指定认证文件路径
2. 创建认证文件: yum install httpd-tools htpasswd -c -m /data/.nginxpasswd tom 认证示例格式: Location ~* ^\(admin|login) { auth_basic “STRING” auth_basic_user_file /file }
|
(1)创建私有CA
cd /etc/pki/CA (umask 077;openssl genrsa -out private/cakey.pem 2048) openssl req -new -x509 -key private/cakey.pem -out cacert.pem -days 7300 touch index.txt echo 01 > serial (2)nginx生成证书请求
cd /usr/local/nginx mkdir ssl && cd ssl (umask 077; openssl genrsa -out nginx.key 2048) openssl req -new -key nginx.key -out nginx.csr (3)CA签署证书
openssl ca -in nginx.csr -out nginx.crt -days 3655 (4)编辑nginx配置文件
listen 443 ssl; server_name www.busyops.com; ssl_certificate /usr/local/nginx/ssl/nginx.crt; ssl_certificate_key /usr/local/nginx/ssl/nginx.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 10m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache:shared表示使用共享worker缓存 (5)nginx检查语法并重读配置文件 /usr/local/nginx/sbin/nginx -t /usr/local/nginx/sbin/nginx -s reload
|
示例:表示对这个url,所有人都可以使用GET方法,但除了GET方法仅允许2.0网段的客户端使用 limit_except GET { allow 192.168.2.0 deny all }
|
(6) 重定向相关
rewrite <regex> <replacement> <flag>
:url地址重写
示例: rewrite ^/images/(.*\.jpg)$ /imgs/$1 break; rewrite ^/images/(.*)$ http://www.baidu.com redirect; rewrite ^/(.*)\.png$ /$1.jpg; rewrite /(.*)$ https://www.busyops.com/$1; flag: last:用户请求到来时,如果匹配到flag为last,则把地址重写,由客户点记录重新请求,重新检查匹配规则 break:一旦被匹配到并重写完成后,不会再被任何重写规则检查而直接进行后续处理 redirect:以302的响应码返回新URL,临时重定向 permanent:以301响应码返回新的URL,永久重定向
|
try_files
:测试文件是否存在,不存在则返回默认页面
try_files $uri /imgs/default.jpg try_files $uri $uri/index.html $uri.html =404
|
语法: if (condition) {...} condition的取值可以使用: 1. 变量名:任何空串、为0、以0开始都为假,其余为真 2. 以变量为操作数构成的比较表达式可以使用=、!=类似的操作符 3. 正则表达式的模式匹配操作 ~:区分大小写 ~*:不区分大小写 !~和!~*:取反 4. 测试文件存在性:-f 、!-f 5. 测试是否为目录:-d 、!-d 6. 测试文件是否存在:-e、!-e 7. 测试文件是否有执行权限:-x、!-x
示例1:匹配用户浏览器类型 if ($http_user_agent ~* MSIE) { rewrite ^(.*)$ /msie/$1 break; }
|
示例配置: location ~* \.(jpg|gif|jpeg|png)$ { valid_referers none blocked *.busyops.com; if ($invalid_referer) { return http://www.busyops.com } } none:表示直接键入访问 blocked:表示有referer但没有值,这种情况可能是代理服务器将这个首部删除了
|
(7) 日志相关
log_format 格式名 格式
:定制访问日志格式,定义完后结合access_log参数使用
access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]]
buffer=size:定义缓冲区大小,可以先将访问日志存在缓冲区中, 然后一并存储到磁盘上 gzip:调整日志压缩比 flush:调整日志滚动时间 access_log off:对于指定url,不记录日志
|
open_log_file_cache max=N [inactive=time] [min_users=N [valid=time]
:访问日志可以定义多个,如果服务器中有多个虚拟主机都需要记录日志,那么就可以使用此项将日志文件缓存,省的每一次记录日志都要去磁盘上去找
max:最大缓存日志元数据项数 inactive:非活动时间 min_users:最少使用次数 valid:检查是否为活动的超时时长 open_log_file_cache=off:不使用日志文件缓冲 缓冲:一般是为了加速写,加速的是元数据 缓存:一般是为了加速读
|
(8) 代理相关
向后端主机代理时,后端主机会记录代理服务器请求,而不是真正的客户端。在定义代理路径URL时,可以对应后端主机的非根路径,代理指令只能定义在location中。如果前端定义为一个非根路径,代理至后端末尾没有/,则把location中定义的路径补在代理路径后,如果代理路径有/,则不会补在后端
proxy_pass http://192.168.2.220
:代理至后端的主机,代理至后端主机时,如果最后的/没有,那么就要把location中的路径补在url后,如果有/,那么就做路径映射
示例一: location /bbs { proxy_pass http://192.168.2.200; } 没有/,表示代理到后端主机http://192.168.2.200/bbs
示例二: location /bbs { proxy_pass http://192.168.2.200/; } 有/,表示后端主机的http://192.168.2.200
|
proxy_cache <name|off>
:定义使用哪个名称缓存空间
proxy_cache_key string
:定义调用缓存hash的键值长度。如果hash的是全url,那么不同的客户端请求相同资源,会出现因url不同而不能命中。但如果只hash后面的资源名称,假如站点有多个域名并使用同一个缓存,那么调用时就可能出现因为访问的资源名称相同而误命中其他缓存
默认值为:proxy_cache_key $scheme$proxy_host$request_uri $scheme:协议 $proxy_host:主机名称 $request_uri:资源路径
|
proxy_cache_valid
:定义不同的响应码的缓存时长
proxy_cache_valid 200 302 10m; proxy_cache_valid 301 1h; proxy_cache_valid any 1m;
|
proxy_cache_path PATH [levels=levels] keys_zone=name:size [inactive=time] [max_size=size] [purger=on|off] [purger_sleep=time]
:只能定义在http中
[levels=levels]:定义缓存目录层级 keys_zone=name:size:定义内存中用多大的空间来放键hash与缓存名 [inactive=time]:非活动时间 [max_size=size]:最大用多少空间存数据 [purger=on|off]:定义缓存修剪 [purger_sleep=time]:缓存清理每睡多久清理一次
缓存配置示例: proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m; 表示缓存位置为/data/nginx/cache,一共有两级子目录,一级子目录名字用一个字符表示,二级子目录名字用两个字符表示,
生成文件:/data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c
|
proxy_cache_use_stale error | timeout | invalid_header | updating| http_500 | http_502 | http_503 | http_504 | htp_403 | http_404|off
:定义后端服务器向我返回哪种错误时,可以使用缓存资源响应客户端
error:后端服务器发送了一个错误响应 timeout:后端服务器超时了 invalid_header:后端服务器响应的内容有一个非法的首部 updating:后端服务器升级
|
-
proxy_cache_min_uses #
:设置某个响应报文响应多少次后才会缓存,默认为1次
-
proxy_cache_methods GET | HEAD| POST
:定义客户端使用哪些请求方法时才用缓存,默认就是get和head
-
proxy_cache_purge string
:定义缓存修剪方式,需要事先定义一种请求方法,接着使用这种请求方法去访问某个资源,就表示清除某个资源的缓存
-
proxy_hide_header HEADER_NAME
:指定哪些首部不响应给客户端,默认就隐藏了后端的Date、Server、X-Pad、X-Accel-
-
proxy_cache_revalidate <on|off>
:对缓存做过期后的重新校验
-
proxy_connect_timeout #
:代理服务器与后端服务器创建tcp链接的超时时长
-
proxy_read_timeout time
:后端服务发送响应报文的超时时长
-
proxy_send_timeout time
:发送给后端服务器请求的超时时长
-
proxy_set_header field vaild
:代理服务器可以在发往后端服务器请求中加入一些首部给后端,通常用于加入客户端IP地址,之后便于后端主机的访问日志中可以记录真正可客户端地址
示例: proxy_set_header X-Real-IP $remote_addr proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
(9) 负载均衡
负载均衡需要用到upstream模块,它可以将多个主机做成一个组,而后用proxy_pass指令代理到这个组即可
upstream name [parameters]
:只能定义在httpd中
parameters可用参数: 1. 健康状态监测 (1) max_conns=#:最大可接受的链接数 (2) max_fails=#:最大探测失败次数 (3) fail_timeout=#:每次失败次数超时时间 (4) backup:sorry_server,所有在线都down后才上线,修复好线上业务后,sorry_server自动下线 (5) down:手动指定down主机,主要用于服务器发布时 ----------------------------------------------------------------------------------------------------------------------- 2. 调度算法
(1) weight=#:权重
(2) wrr:默认的调度算法
(3) least_conn:最小链接,适用于长连接场景,调度结果与wrr差不多,但比wrr多一个后端主机连接数探测,所以更适用于长连接
(4) ip_hash:源IP一样,调度结果一样 (5) hash key [consistent]:静态数组法,默认的ip_hash算法通过内存中的hash表来维持用户会话,但其占用空间会随着访问量增大 而增大。而hash key算法可以不用维护会话即可完成会话绑定。首先把所有服务器的权重相加,得到的结果是几就将服务器看成多少台,然后 对客户端IP地址进行其hash计算,计算结果对权重之和取模,取模结果是几就调度到哪台服务器上。但是缺点也很明显,一旦服务器数量改变, 就会影响全局的hash结果,导致全局的调度失效,要重新hash
为了解决因服务器数量变动而影响全局hash结果的后果,诞生了一致性hash算法。它很类似于生活中的钟表,把表盘看成一个资源环,然后 将服务器进行hash,假设有12台服务器,hash结果正好对应钟表的12个小时刻度。当客户端请求到来时一样用hash算法计算IP地址,假如计算 的结果是1:15分,那么就顺时针寻找离他最近的服务器,也就是2点的那台服务器。在这种情况中如果主机变动,也只影响一小段的请求,不会影 响全局,比如3点的服务器down了,那么原先落在2:01到2:59这段的请求就顺时针调度到4点的服务器即可。再则,如果在2:30分加一台服务 器,那么2:00到2:29的请求调度到新的服务器即可 但是一致性hash算法也是有缺点的,因为服务器的hash结果无法像表盘时钟刻度一样均匀,有些服务器hash结果离的很近、有些又很远,这就导 致有些服务器会承担大部分请求,而有些有很闲,这种情况就叫做hash环偏斜,为了解决hash环偏斜,于是就在权重中加入随机数,使得服务器 权重之和增大,这样计算的结果不能说多均匀的落在表盘上,起码可以使得表盘的刻度很密集,于是偏斜的结果就少的多,这就是consistent (一致性hash)的作用,但是缺点在于对权重取模与对权重加随机数后取模,需要的计算量也是不一样的
key的取值: $remote_addr,根据客户端ip做hash,与ip hash效果一样 $request_uri:根据请求地址做hash,会把不同主机同一请求调度到同一服务器
|
示例:
(1) 首先定义upstream服务器组
http {
...
upstream web_cluster1 {
server 192.168.1.100 max_fails=3 fail_timeout=30s;
server busyops.com:8080 weight=5;
server backup.busyops.com backup;
}
...
}
(2) 使用location调用
location / {
proxy_pass http://web_cluster1;
}
(10) php相关
-
fastcgi_cache
:定义使用哪个缓存
-
fastcgi_cache_path path
:与proxy_cache_path选项一样
-
fastcgi_cache_key
:
-
fastcgi_cache_methods
:为哪些请求方法缓存
-
fastcgi_cache_min_uses number
:在指定时长内至少几次才使用缓存
-
fastcgi_cache_valid [code] time
:为不同的状态码指定缓存时长
-
fastcgi_pass IP:PORT
:定义代理至后端主机的IP与端口
-
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name
:指定向后端传递的网页参数
/var/www/html为后端主机网页目录的跟路径 $fastcgi_script_name:匹配到网页的脚本名,变量中默认有/,所以定义时/加不加都可
|
-
fastcgi_index index.php
:定义fastcgi的默认主页
-
include fastcgi_params
:nginx提供的默认文件中定义了很多自带变量,用include包含进来
-
fastcgi_keep_conn on|off
:默认情况下fastcgi_server会在响应后关闭连接,如果我们设定了这个指令为ON,nginx不会断开与后端的fastcgi的链接
lnmp配置示例
1. 本机使用yum安装php-fpm,编辑/etc/php-fpm.d/www.conf
listen:调整监听地址,生产环境中要遵守最小权限法则 listen.allowed_clients:做访问授权 pm.status_path:内置的状态页 ping.path:测试后端主机的存活行 启动php-fpm:systemctl start php-fpm.service
----------------------------------------------------------------------------------------------- 启用状态页面:编辑/etc/php-fpm.d/www.com,启用pmstate与ping.path,然后在前端的nginx配置文件中加入代理配置 location ~* ^/(status|ping) { fastcgi_pass 192.168.60.100:9000; fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; include fastcgi_params; } -----------------------------------------------------------------------------------------------
2. 编辑nginx配置文件,将代理配置信息填入
location ~ \.php$ { root /usr/share/nginx/html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; include fastcgi_params; }
3. 编辑网页文件 vim /usr/share/nginx/html/index.php
<?php phpinfo(); ?>
4. 测试网页时出错了,于是将/etc/nginx/fastcgi_params文件中的内容全部替换如下,网页可见了
fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200;
---------------------------------------分割线---------------------------------------- 具体原因不清楚
|
配置php缓存机制
1. 首先在nginx配置文件中加入fastcgi_cache的定义
fastcgi_cache_path /cache/php levels=1 keys_zone=php_cache:20m inactive=20; fastcgi_cache_key localhost:9000$request_uri; fastcgi_cache_valid 200 302 10m; fastcgi_cache_valid 301 1m; fastcgi_cache_valid any 1m;
2. 在需要启用缓存的location中调用cache
location ~ \.php$ { ... fastcgi_cache php_cache; ... }
3. 如果想查看缓存状态,可以在对应的server中加入header追踪缓存状态 add_header X-Via $server_addr; add_header X-Cache $upstream_cache_status;
4. 设置完毕后使用ab查看前后效果
ab -n 1000 -c 20 http://192.168.60.100/index.php
|
4. stream配置段
实现4层代理的模块
listen
:监听的地址和端口
proxy_protocol_timeout
:代理协议的超时时间
server
:与http中的server一样,只不过它只需要定义代理至后端的主机地址
proxy_connect_time
:设置nginx与后端服务器建立链接的超时时长
proxy_next_upstream <on|off>
:如果代理到后端的一台服务器时,客户端无法与其正常建立链接,那么就调度到下一台,默认就是开启的
proxy_timeout #
:设置代理与被代理服务器之间的读或写操作,如果未正常响应,隔多久进行重试,一般此项不定义
proxy_next_upstream_timeout #
:与proxy_next_upstream结合使用,意为调度到下一台的超时时长
proxy_next_upstream_tries #
:与后端服务器建立链接不成功时,尝试重连几次,默认为0,表示无限制
access_log 路径 格式 缓冲大小
:定义访问日志路径、使用格式及缓冲区大小
log_format name format
:定义访问日志格式及名称
示例: stream { upstream backend { hash $remote_addr consistent;
server busyops.com:12345 weight=5; server 127.0.0.1:12345 max_fails=3 fail_timeout=30s; server unix:/tmp/backend3; }
# 使用upstream指令定义一个名为backend的服务器组供后续引用,1号主机代理至12345端口,其权重为5。2号主机代理本机的12345端口, 并且做健康状态监测,3号主机使用本地的unix套接字文件代理 upstream dns { server 192.168.0.1:53535; server dns.busyops.com:53; }
# 使用upstream指令定义一个名为dns的服务器组供后续引用,1号主机使用ip地址代理到53535端口,2号主机使用主机名称代理至53号端口 server { listen 12345; proxy_connect_timeout 1s; proxy_timeout 3s; proxy_pass backend; }
# 使用server指令定义虚拟主机,监听在12345端口,与后端建立链接的时间为1秒,前后端不能正常读写3秒重试,通过proxy_pass指令明 确指定代理至backend的服务器组 server { listen 127.0.0.1:53 udp reuseport; proxy_timeout 20s; proxy_pass dns; }
server { listen [::1]:12345; proxy_pass unix:/tmp/stream.socket; } }
|
四、nginx内核参数调整
由于Linux内核参数默认是按照最通用的场景设置的,这明显不适用支持高并发访问的Web服务器,所以需要修改Linux内核参数使得Nginx可以拥有更高的性能。不过Nginx作为静态Web内容服务器、反向代理服务器或者是提供压缩机制的压缩服务器,其内核参数的调整都是不同的,此处针对最通用的、使Nginx支持更多并发请求的TCP网络参数做简单的配置,需要修改/etc/sysctl.conf
-
fs.file-max = 999999
:单个进程最大可以打开的文件描述符数量
-
net.ipv4.tcp_tw_reuse = 1
:tw表示TIME_WAIT,这个参数表示如果某个链接处于TIME_WAIT状态时,是否允许它被复用处理新请求。通俗来讲就是当客户端请求到来时,nginx都要建立一个套接字文件,非常繁忙的服务器就会频繁的创建、删除套接字文件,如果可以复用此前存在的文件描述符,在很大程度上是可以提升性能的,参数设置为1表示允许复用
-
net.ipv4.tcp_keepalive_time = 600
:当keepalive启动时,TCP发送keepalive消息的频度,默认是2小时,将其设置为10分钟,可以更快的清理无效链接,一般不要调整这个参数,因为客户端与服务端可能不一样,会引起一些异常状态
-
net.ipv4.tcp_fin_timeout = 30
:当服务器主动关闭链接时,socket保持在FIN_WAIT_2状态的最大时间。通俗的将就是在保持连接中,服务器端允许主动断开连接的超时时长
-
net.ipv4.tcp_max_tw_buckets = 5000
:操作系统允许TIME_WAIT套接字数量的最大值,如果超过这个数字,TIME_WAIT套接字将立即被清除并打印警告信息,默认为8000,过多的TIME_WAIT套接字会使Web服务器变慢
-
net.ipv4.ip_local_port_range = 1024 65000
:定义UDP和TCP连接的本地端口取值范围
-
net.ipv4.tcp_rmam = 10240 87380 12582912
:定义TCP接受缓存的最小值、默认值、最大值
-
net.ipv4.tcp_wmem = 10240 87380 12582912
:定义TCP发送缓存的最小值、默认值、最大值
-
net.core.netdev_max_backlog = 8096
:当网卡接受数据包的速度大于内核的处理速度时,会有一个队列保存这些数据包,这个参数表示该队列的最大值
-
net.ipv4.tcp_syncookies = 1
:与性能无关,在一定程度上利于解决TCP的SYN攻击
-
net.ipv4.tcp_max_syn_backlog = 8192
:TCP三次握手建立阶段,接受SYN请求队列的最大长度,默认为1024。将其设置的大一些可避免因Nginx繁忙来不及accept新链接时,而丢失客户端发起的链接请求
-
net.ipv4.tcp_tw_recycle = 1
:用于设置启用time_wait的快速收回
-
net.core.somaxconn = 262114
:此选项默认值是128,用于调节系统同时发起的TCP连接数。在高并发的场景中,默认的值可能会导致链接超时或重传,因此需要结合高并发请求数量来调节此值
-
net.ipv4.tcp_max_orphans = 262114
:用于设定系统中最多有多少个TCP套接字可以不被关联到任何一个用户文件的句柄上。如果超过这个数字,孤立链接将立即被复位并输出警告信息,这个限制只是为了防止简单的DOS攻击,不用过分依靠这个限制甚至人为的减少这个值,更多的情况是增加这个值.
-
net.core.rmem_default = 6291456
:表示内核套接字接受缓存区默认大小
-
net.core.wmem_default = 6291456
:表示内核套接字发送缓存区默认大小
-
net.core.rmem_max = 12582912
:表示内核套接字接受缓存区最大大小
-
net.core.wmem_max = 12582912
:表示内核套接字发送缓存区最大大小
最后四个参数需要根据业务逻辑和实际的硬件成本来综合考虑