浅谈nginx原理及配置
Nginx同Apache一样都是一种WEB服务器。基于REST架构风格,以统一资源描述符(Uniform Resources Identifier)URI或者统一资源定位符(Uniform Resources Locator)URL作为沟通依据,通过HTTP协议提供各种网络服务。Apache的发展时期很长,而且是毫无争议的世界第一大服务器。它有着很多优点:稳定、开源、跨平台等等。它出现的时间太长了,它兴起的年代,互联网产业远远比不上现在,所以它被设计为一个重量级的服务器。它不支持高并发,在Apache上运行数以万计的并发访问,会导致服务器消耗大量内存(新起线程会分配内存)。操作系统对其进行进程或线程间的切换也消耗了大量的CPU资源,导致HTTP请求的平均响应速度降低。Nginx是轻量级高并发服务器,开源、跨平台且使用基于事件驱动架构,这些优秀的设计带来的极大的稳定性。nginx的模块根据其功能基本上可以分为以下几种类型:
event module: 搭建了独立于操作系统的事件处理机制的框架,及提供了各具体事件的处理。包括ngx_events_module, ngx_event_core_module和ngx_epoll_module等。nginx具体使用何种事件处理模块,这依赖于具体的操作系统和编译选项。
phase handler: 此类型的模块也被直接称为handler模块。主要负责处理客户端请求并产生待响应内容,比如ngx_http_static_module模块,负责客户端的静态页面请求处理并将对应的磁盘文件准备为响应内容输出。
output filter: 也称为filter模块,主要是负责对输出的内容进行处理,可以对输出进行修改。例如,可以实现对输出的所有html页面增加预定义的footbar一类的工作,或者对输出的图片的URL进行替换之类的工作。
upstream: upstream模块实现反向代理的功能,将真正的请求转发到后端服务器上,并从后端服务器上读取响应,发回客户端。upstream模块是一种特殊的handler,只不过响应内容不是真正由自己产生的,而是从后端服务器上读取的。
load-balancer: 负载均衡模块,实现特定的算法,在众多的后端服务器中,选择一个服务器出来作为某个请求的转发服务器。
一.正向代理及反向代理
在如今的网络环境下,我们如果由于技术需要要去访问国外的某些网站,此时你会发现位于国外的某网站我们通过浏览器是没有办法访问的,此时大家可能都会用一个操作FQ进行访问,FQ的方式主要是找到一个可以访问国外网站的代理服务器,我们将请求发送给代理服务器,代理服务器去访问国外的网站,然后将访问到的数据传递给我们!上述这样的代理模式称为正向代理,正向代理最大的特点是客户端非常明确要访问的服务器地址;服务器只清楚请求来自哪个代理服务器,而不清楚来自哪个具体的客户端;正向代理模式屏蔽或者隐藏了真实客户端信息。客户端必须设置正向代理服务器,当然前提是要知道正向代理服务器的IP地址,还有代理程序的端口。正向代理,"它代理的是客户端",是一个位于客户端和原始服务器(origin server)之间的服务器。正向代理的用途:访问原来无法访问的资源,如Google;可以做缓存,加速访问资源;对客户端访问授权,上网进行认证;代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息。
多个客户端给服务器发送的请求,Nginx服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了。此时请求的来源也就是客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,Nginx扮演的就是一个反向代理角色。客户端是无感知代理的存在的,反向代理对外都是透明的,访问者并不知道自己访问的是一个代理。因为客户端不需要任何配置就可以访问。反向代理,"它代理的是服务端",主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息。反向代理的作用:保证内网的安全,通常将反向代理作为公网访问地址,Web服务器是内网;负载均衡,通过反向代理服务器来优化网站的负载。
二.nginx负载均衡原理
当前大多数的互联网系统都使用了服务器集群技术,集群是将相同服务部署在多台服务器上构成一个集群整体对外提供服务,这些集群可以是 Web 应用服务器集群,也可以是数据库服务器集群,还可以是分布式缓存服务器集群等等。客户端发送的、Nginx反向代理服务器接收到的请求数量,就是我们说的负载量。请求数量按照一定的规则进行分发到不同的服务器处理的规则,就是一种均衡规则。所以将服务器接收到的请求按照规则分发的过程,称为负载均衡。
负载均衡在实际项目操作过程中,有硬件负载均衡和软件负载均衡两种,硬件负载均衡也称为硬负载,如F5负载均衡,相对造价昂贵成本较高,但是数据的稳定性安全性等等有非常好的保障,如中国移动中国联通这样的公司才会选择硬负载进行操作;更多的公司考虑到成本原因,会选择使用软件负载均衡,软件负载均衡是利用现有的技术结合主机硬件实现的一种消息队列分发机制。
LVS、Nginx、HAProxy 是目前使用最广泛的三种软件负载均衡软件。目前关于网站架构一般比较合理流行的架构方案:Web 前端采用 Nginx/HAProxy+Keepalived 作负载均衡器;后端采用 MySQ L数据库一主多从和读写分离,采用 LVS+Keepalived 的架构。
1.LVS
LVS 是 Linux Virtual Server 的简称,也就是 Linux 虚拟服务器。现在 LVS 已经是 Linux 标准内核的一部分。LVS 的服务器集群由三部分组成:最前端的负载均衡层,中间的服务器集群层,最底端的数据共享存储层。LVS 是四层负载均衡,也就是说建立在 OSI 模型的第四层——传输层之上,传输层上有我们熟悉的 TCP/UDP,LVS 支持 TCP/UDP 的负载均衡,它的效率是非常高的。LVS 的转发主要通过修改 IP 地址(NAT 模式,分为源地址修改 SNAT 和目标地址修改 DNAT)、修改目标 MAC(DR 模式)来实现。
NAT(Network Address Translation)是一种外网和内网地址映射的技术。当包到达 LVS 时,LVS 做目标地址转换(DNAT),将目标 IP 改为 RS(RealServer) 的 IP。RS 接收到包以后,仿佛是客户端直接发给它的一样。RS 处理完,返回响应时,源 IP 是 RS IP,目标 IP 是客户端的 IP。这时 RS 的包通过网关(LVS)中转,LVS 会做源地址转换(SNAT),将包的源地址改为 VIP,这样,这个包对客户端看起来就仿佛是 LVS 直接返回给它的。
DR 模式下需要 LVS 和 RS 集群绑定同一个 VIP(RS 通过将 VIP 绑定在 loopback 实现),一个请求过来时,LVS 只需要将网络帧的 MAC 地址修改为某一台 RS 的 MAC,该包就会被转发到相应的 RS 处理,注意此时的源 IP 和目标 IP 都没变,LVS 只是做了一下移花接木。RS 收到 LVS 转发来的包时,链路层发现 MAC 是自己的,到上面的网络层,发现 IP 也是自己的,于是这个包被合法地接受,RS 感知不到前面有 LVS 的存在。而当 RS 返回响应时,只要直接向源 IP(即用户的 IP)返回即可,不再经过 LVS。DR 模式具有较好的性能,也是目前大型网站使用最广泛的一种负载均衡手段。
LVS 抗负载能力强、是工作在传输层上仅作分发之用,没有流量的产生,这个特点也决定了它在负载均衡软件里的性能最强的,对内存和 cpu 资源消耗比较低;配置性比较低,这是一个缺点也是一个优点,因为没有可太多配置的东西,所以并不需要太多接触,大大减少了人为出错的几率;工作稳定,因为其本身抗负载能力很强,自身有完整的双机热备方案,如 LVS + Keepalived;因为 LVS 工作在传输层,所以它几乎可以对所有应用做负载均衡,包括 http、数据库、在线聊天室等等。
2.nginx
Nginx 负载均衡主要是对七层网络通信模型中的第七层应用层上的 http、https 进行支持。Nginx 是以反向代理的方式进行负载均衡的。Nginx 实现负载均衡的分配策略有很多,Nginx 的 upstream 目前支持以下几种方式:
轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器 down 掉,能自动剔除。
weight:指定轮询几率,weight 和访问比率成正比,用于后端服务器性能不均的情况。
ip_hash:每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 的问题。
fair(第三方):按后端服务器的响应时间来分配请求,响应时间短的优先分配。
url_hash(第三方):按访问 url 的 hash 结果来分配请求,使每个 url 定向到同一个后端服务器,后端服务器为缓存时比较有效。
Nginx 负载均衡主要优点有:跨平台、配置异常简单、事件驱动、非阻塞、高并发连接、Master/Worker 结构、内存消耗小、内置的健康检查功能(如果 Nginx 代理的后端的某台 Web 服务器宕机了,不会影响前端访问)、节省带宽(支持 GZIP 压缩,可以添加浏览器本地缓存的 Header 头)、稳定性高。
Nginx 负载均衡主要缺点有:Nginx 仅能支 持http、https 、tcp、 Email等协议,这样就在适用范围上面小些;对后端服务器的健康检查,只支持通过端口来检测,不支持通过 ur l来检测;不支持 Session 的直接保持,但能通过 ip_hash 来解决。
3.HAProxy
HAProxy 支持两种代理模式 TCP(四层)和HTTP(七层),也是支持虚拟主机的。HAProxy 的优点能够补充 Nginx 的一些缺点,比如支持 Session 的保持,Cookie 的引导;同时支持通过获取指定的 url 来检测后端服务器的状态。单纯从效率上来讲 HAProxy 会比 Nginx 有更出色的负载均衡速度,在并发处理上也是优于 Nginx 的。HAProxy 负载均衡策略非常多:Round-robin(轮循)、Weight-round-robin(带权轮循)、source(原地址保持)、RI(请求URL)、rdp-cookie(根据cookie)。
三.nginx支持高并发原理
同步异步、阻塞非阻塞:
同步与异步,重点在于消息通知的方式;阻塞与非阻塞,重点在于等消息时候的行为。同步阻塞:小明在柜台干等着拿奶茶;同步非阻塞:小明在柜台边刷微博边等着拿奶茶;异步阻塞:小明拿着小票啥都不干,一直等着店员通知他拿奶茶;异步非阻塞:小明拿着小票,刷着微博,等着店员通知他拿奶茶。
select、poll、epoll:
当连接有I/O流事件产生的时候,就会去唤醒进程去处理。select与poll原理是一样的,只不过select只能观察1024个连接,poll可以观察无限个连接,进程并不知道是哪个连接产生的I/O流事件,于是进程就挨个去问:“请问是你有事要处理吗?”......问了99999遍,哦,原来是第100000个进程有事要处理。那么,前面这99999次就白问了,白白浪费宝贵的CPU时间片了。epoll当连接有I/O流事件产生的时候,epoll就会去告诉进程哪个连接有I/O流事件产生,然后进程就去处理这个连接的IO事件。Nginx是基于epoll的,异步非阻塞的服务器程序。Apache处理一个请求是同步阻塞的模式,每到达一个请求,Apache都会去fork一个子进程去处理这个请求,直到这个请求处理完毕。
nginx进程模型:
多进程:一个 Master 进程、多个 Worker 进程
Master 进程:管理 Worker 进程,加载配置文件,初始化监听的 socket,fork 出多个 Worker 进程
对外接口:接收外部的操作(信号)
对内转发:根据外部的操作的不同,通过信号管理 Worker
监控:监控 worker 进程的运行状态,worker 进程异常终止后,自动重启 worker 进程
Worker 进程:所有 Worker 进程都是平等的,在 nginx.conf 中配置,一般设置为核心数,充分利用 CPU 资源,同时,避免进程数量过多,避免进程竞争 CPU 资源,增加上下文切换的损耗;网络请求,由 Worker 进程处理,竞争新的连接,获胜方通过三次握手,建立 Socket 连接,并处理请求。
四.nginx配置
nginx.conf的内容通常是这样的:
...
... #核心摸块
events { #事件模块
...
}
http { # http 模块
server { # server块
location [PATTERN] { # location块
...
}
location [PATTERN] {
...
}
}
server {
...
}
}
mail { # mail 模块
server { # server块
...
}
}
核心模块
user admin; #配置用户或者组
worker_processes 4; #允许生成的进程数,默认为1
pid /nginx/pid/nginx.pid; #指定 nginx 进程运行文件存放地址
error_log log/error.log debug; #错误日志路径,级别
事件模块
events {
accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on
multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off
use epoll; #事件驱动模型select|poll|kqueue|epoll|resig
worker_connections 1024; #最大连接数,默认为512
}
http 模块
http {
include mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型,默认为text/plain
access_log off; #取消服务日志
sendfile on; #允许 sendfile 方式传输文件,默认为off,可以在http块,server块,location块
sendfile_max_chunk 100k; #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限
keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块
server
{
keepalive_requests 120; #单连接请求上限次数
listen 80; #监听端口
server_name 127.0.0.1; #监听地址
index index.html index.htm index.php;
root your_path; #根目录
location ~ .php$
{
fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
#fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
}
location 查找规则
location = / {
# 精确匹配 / ,主机名后面不能带任何字符串
[ config A ]
}
location / {
# 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求
# 但是正则和最长字符串会优先匹配
[ config B ]
}
location /documents/ {
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ config C ]
}
location ~ /documents/Abc {
# 匹配任何以 /documents/Abc 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ config CC ]
}
location ^~ /images/ {
# 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条
[ config D ]
}
location ~* .(gif|jpg|jpeg)$ {
# 匹配所有以 gif,jpg或jpeg 结尾的请求
# 然而,所有请求 /images/ 下的图片会被 config D 处理,因为 ^~ 到达不了这一条正则
[ config E ]
}
location /images/ {
# 字符匹配到 /images/,继续往下,会发现 ^~ 存在
[ config F ]
}
location /images/abc {
# 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在
# F与G的放置顺序是没有关系的
[ config G ]
}
location ~ /images/abc/ {
# 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,匹配到这一条正则,采用
[ config H ]
}
正则查找优先级从高到低依次如下:
“ = ” 开头表示精确匹配,如 A 中只匹配根目录结尾的请求,后面不能带任何字符串。
“ ^~ ” 开头表示uri以某个常规字符串开头,不是正则匹配。
“ ~ ” 开头表示区分大小写的正则匹配。
“ ~* ”开头表示不区分大小写的正则匹配。
“ / ” 通用匹配, 如果没有其它匹配,任何请求都会匹配到。
负载均衡配置
Nginx 的负载均衡需要用到 upstream模块,可通过以下配置来实现:
upstream test-upstream {
ip_hash; # 使用 ip_hash 算法分配
server 192.168.1.1; # 要分配的 ip
server 192.168.1.2;
}
server {
location / {
proxy_pass http://test-upstream;
}
}
上面的例子定义了一个 test-upstream的负载均衡配置,通过 proxy_pass反向代理指令将请求转发给该模块进行分配处理。
参考文章: