本文介绍一下负载层常用的开源工具:Nginx
什么是Nginx
Nginx(发音为“engine X”)是一款轻量级的web服务器/反向代理服务器,同时也是一个电子邮件IMAP/POP3/SMTP 代理服务器,并在一个BSD-like协议下发行(一种开源协议)。Nginx由俄罗斯程序设计师Lgor Sysoev开发,优点是占用内存少,并发能力强。
Nginx由C语言实现,以事件驱动的方式编写,所以有非常好的性能,同时也是一个非常高效的反向代理、负载平衡服务器 。其优点如下:
1.更快,正常情况下和高并发情况下都可以更快地响应请求;
2.高拓展性,大量的官方模块和第三方模块提供,而且Nginx的模块都是嵌入到二进制文件中执行的;
3.高可靠性,官方提供的模块都很稳定,每个worker进程相对独立;
4.低内存消耗,一般情况下10000个非活跃的HTTP Keep-Alive连接在Nginx中仅消耗2.5MB,这是Nginx高并发连接的基础;
5.单机支持10万以上的并发连接,这不是极限,理论上Nginx支持的并发连接上线取决于内存;
6.热部署,master管理进程和worker工作进程分离设计,使得Nginx能够提供热部署功能;
7.自由的BSD许可协议,允许用户修改发布源码;
上文提到,Nginx支持反向代理功能,这里说明一下正向代理和反向代理服务器的区别:
正向代理是当我们请求某一网址时,我们是知道其域名的,只是使用代理服务器做跳板;
反向代理就是客户端访问的服务器并不真正提供服务,它从别的服务器获取资源并返回结果给客户端;
当用户发来http请求时,Nginx不会立刻转发到上游服务器,而是先把用户的请求(包括http包体)完整地接收到Nginx所在服务器的硬盘或内存中,然后再向上游服务器发起连接,把缓存的客户端请求(完整的请求)转发到上游服务器。这么做的优点是减轻了上游服务器的并发压力,把负载放在的Nginx服务器上。
Nginx配置文件详解
如下的nginx.conf配置,实现了一个简单的反向代理服务器:
#运行用户
user nobody;
#启动进程,通常设置成和cpu的数量相等
worker_processes 1;
#nginx可打开最大文件数
worker_rlimit_nofile 65535;
#全局错误日志及PID文件,可指定错误级别
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
#工作模式及连接数上限
events {
#epoll是多路复用IO(I/O Multiplexing)中的一种方式,
#仅用于linux2.6以上内核,可以大大提高nginx的性能
use epoll;
#单个后台worker process进程的最大并发连接数
worker_connections 1024;
# 并发总数是 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
}
http {
#设定mime类型,类型由mime.type文件定义
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 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,
#对于普通应用,必须设为 on,
#如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,
#以平衡磁盘与网络I/O处理速度,降低系统的uptime.
sendfile on;
#tcp_nopush on;
#连接超时时间
#keepalive_timeout 0;
keepalive_timeout 65;
tcp_nodelay on;
#开启gzip压缩
gzip on;
gzip_disable "MSIE [1-6].";
#设定请求缓冲
client_header_buffer_size 128k;
large_client_header_buffers 4 128k;
# 设定负载均衡后台服务器列表
upstream server1 {
#默认是轮询,如果配置了ip_hash,就是根据ip进行哈希匹配
ip_hash;
#后端服务器ip+port,可配置多个
server 192.168.10.100:8080 max_fails=2 fail_timeout=30s ;
server 192.168.10.101:8080 max_fails=2 fail_timeout=30s ;
#如果配置了bakcup,当所有其他节点都失效以后,该节点才接收请求
server 192.168.10.102:8080 bakcup;
#加权轮询
server 192.168.10.103:8080 weight=100;
}
upstream server2 {
#默认是轮询,如果配置了ip_hash,就是根据ip进行哈希匹配
ip_hash;
#后端服务器ip+port,可配置多个
server 192.168.10.100:8080 max_fails=2 fail_timeout=30s ;
server 192.168.10.101:8080 max_fails=2 fail_timeout=30s ;
#如果配置了bakcup,当所有其他节点都失效以后,该节点才接收请求
server 192.168.10.102:8080 bakcup;
#加权轮询
server 192.168.10.103:8080 weight=100;
}
#设定虚拟主机配置
server {
#侦听80端口
listen 80;
#定义使用 www.nginx.cn访问
server_name www.nginx.cn;
#定义服务器的默认网站根目录位置
root html;
#设定本虚拟主机的访问日志
access_log logs/nginx.access.log main;
#默认请求
location / {
#定义首页索引文件的名称
index index.php index.html index.htm;
}
# 定义错误提示页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
#静态文件,nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
#过期30天,静态文件不怎么更新,过期可以设大一点,
#如果频繁更新,则可以设置得小一点。
expires 30d;
}
#server1的请求,跳转到上文配置的server1的节点列表中
location ^~ /server1/ {
proxy_pass http://server1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 100m;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#server2的请求,跳转到上文配置的server2的节点列表中
location ^~ /server2/ {
proxy_pass http://server2;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 100m;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#禁止访问 .htxxx 文件
location ~ /.ht {
deny all;
}
}
}
在上面配置中,worker_processes表示操作系统启动多少个工作进程运行nginx。在nginx运行时,会启动2种进程,一种是主进程master进程,一种是worker进程。
master进程不处理用户请求,而是管理worker进程,由worker进程来处理请求,一般将worker进程的数量设置为cpu核心数。原因是一个worker进程可以同时处理的请求数只受限于内存大小,而且在架构设计上,不同的worker进程之间处理并发请求时几乎没有同步锁的限制,worker进程通常不会进入睡眠状态。因此,当Nginx上进程数与cpu核心数相等时(最好每一个worker进程都绑定特定的cpu核心),进程间切换的代价是最小的。
worker_connection表示单个工作进程可以允许同时建立外部连接的数量,无论这个连接是nginx外部建立的还是内部建立的。需要注意,一个工作进程建立一个连接后,进程将打开一个文本副本。所以这个数量还与操作系统设定的进程最大可打开的文件副本数有关。
在linux系统中,我们需要修改操作系统“进程最大可打开文件数”。步骤如下:
1.在vim /etc/security/limits.conf文件最后加上
# * soft nofile 65535 应用软件级别限制的最大可打开文件数
# * hard nofile 65535 操作系统级别限制的最大可打开文件数
2.ulimit -n 65535 更改本次shell会话设置
3.ulimit -a 查看open files为65535说明已经修改成功
当Nginx被作为服务器使用时,可承受的最大连接数量为最大工作线程乘每个线程允许的连接数量:
max_clients = worker_processes * worker_connections
当Nginx被作为反向代理使用时,可承受的最大连接数量为最大工作线程乘每个线程允许的连接数量/4:
max_clients = worker_processes * worker_connections / 4
这里需要除以4的原因是,反向代理情况下浏览器会建立两条连接到nginx(注意两条连接都是浏览器建立的),nginx也会建立对应的两条连接到后端服务器,所以需要除以4。
Nginx的第三方插件
Nginx通过模块的方式支持了许多第三方插件,包括包括gzip模块,rewrite模块、健康检查模块、proxy-cache模块和图片操作模块image_filter_module和nginx_upstream_jxm_route模块等。
gzip压缩模块是对返回给请求端的http response body进行压缩,调用时序图如下:
rewrite模块是让Nginx重定向url时支持正则表达式,其中:
~ : 区分大小写进行正则表达式匹配
~* : 不区分大小写进行正则表达式匹配
!~ :区分大小写进行正则表达式不匹配
!~* :不区分大小写进行正则表达式不匹配
^ :以什么开头的匹配
$ : 以什么结尾的匹配
* :代表任意字符
Nginx的基础知识先介绍到这里,下文中我们再说一下在生产环境中负载层如何部署。