一、Nginx特性

Nginx使用可扩展的事件驱动,不是传统的过程驱动架构。
在传统的Web服务器体系结构中,每个客户端连接作为一个单独的进程或线程处理,随着网站的流行度增加,并发连接数量的增加,Web服务器减慢,延迟了对用户的响应。
从技术角度来看,产生一个单独的进程/线程需要将CPU切换到新的任务并创建一个新的运行时上下文,消耗额外的内存和CPU时间,从而对性能产生负面影响。

Nginx开发的目标是实现10倍以上的性能,优化服务器资源的使用,同时也能够扩展和支持网站的动态增长。因此,Nginx成为最知名的模块化,时间驱动,异步,单线程Web服务器和Web代理之一。

Nginx是一个高性能的Web和反向代理服务器,它具有很多非常优越的特性:
1)作为Web服务器
相比 Apache, Nginx 使用更少的资源,支持更多的并发连接,体现更高的效率,这点使 Nginx尤其受到虚拟主机提供商的欢迎。能够支持高达 50,000 个并发连接数的响应,感谢 Nginx为我们选择了 epoll and kqueue 作为开发模型。

2)作为负载均衡服务器
Nginx 既可以在内部直接支持 Rails 和 PHP,也可以支持作为 HTTP 代理服务器 对外进行服务。 Nginx 用 C 编写, 不论是系统资源开销还是 CPU 使用效率都比 Perlbal 要好的多。

3)作为邮件代理服务器
Nginx 同时也是一个非常优秀的邮件代理服务器(最早开发这个产品的目的之一也是作为邮
件代理服务器), Last.fm 描述了成功并且美妙的使用经验。

Nginx 安装非常的简单,配置文件 非常简洁(还能够支持 perl 语法), Bugs 非常少的服务器。
Nginx 启动特别容易,并且几乎可以做到 7*24 不间断运行,即使运行数个月也不需要重新启动。
你还能够在 不间断服务的情况下进行软件版本的升级。


二、Nginx架构

nginx 大量使用复用和事件通知,并专门用于分离进程的特定任务。 连接在有限数量的单线程进程称为工作(worker)的高效运行循环中处理。 在每个工作(worker)中, nginx 可以处理每秒数千个并发连接和请求。

2.1 代码结构

修改nginx dns 缓存时间_修改nginx dns 缓存时间

Nginx工作(worker)码包括核心和功能模块。 nginx 的核心是负责维护严格的运行循环,并在请求处理的每个阶段执行模块代码的适当部分。 模块构成了大部分的演示和应用层功能。 模块读取和写入网络和存储,转换内容,执行出站过滤,应用服务器端包含操作,并在代理启动时将请求传递给上游服务器。

nginx 的模块化架构通常允许开发人员扩展一组 Web 服务器功能,而无需修改 nginx 内核。 nginx 模块略有不同,即核心模块,事件模块,阶段处理程序,协议,可变处理程序,过滤器,上游和负载平衡器。 nginx 不支持动态加载的模块; 即在构建阶段将模块与核心一起编译。

在处理与接受,处理和管理网络连接和内容检索相关的各种操作时,nginx 在基于 Linux,Solaris 和 BSD 的操作系统中使用事件通知机制和一些磁盘 I/O 性能增强,如: kqueue, epoll,和事件端口。 目标是为操作系统提供尽可能多的提示,以便及时获取入站和出站流量,磁盘操作,读取或写入套接字,超时等异步反馈。 对于每个基于 Unix 的 nginx 运行的操作系统,大量优化了复用和高级 I/O 操作的不同方法的使用。


2.2 工作模式

Nginx不会为每一个连接生成一个进程或线程。相反,worker进程接受来自共享“listen”套接字的新请求,并在每个worker内执行高效的运行循环,以处理每个worker中的数千个连接。
worker是由操作系统内核机制完成。

启动后,将创建一组初始侦听套接字,然后worker在处理HTTP请求和响应时不断接受、读取和写入套接字。运行循环是 nginx worker代码中最复杂的部分。 它包括全面的内部调用,并且在很大程度上依赖异步任务处理的想法。 异步操作通过模块化,事件通知,广泛使用回调函数和微调定时器来实现。 总体而言,关键原则是尽可能不阻塞。

nginx 仍然可以阻塞的唯一情况是worker进程没有足够的磁盘存储

内存:绝大多数情况下,内存使用非常保守,非常有效,因为nginx不会连接一个进程或线程。
CPU:节省CPU周期,因为进程或线程没有持续的 ”创建-销毁“ 模式。

nginx 的作用是检查网络和存储的状态,初始化新连接,将其添加到运行循环中,并异步处理直到完成,此时连接被重新分配并从运行循环中删除。 结合仔细使用系统调用(syscall)和精确实现支持接口(如 pool 和 slab 内存分配器), nginx 通常可以在极端工作负载下实现中到低的CPU 使用。

在一些磁盘使用和 CPU 负载模式,应调整 nginx 工作(worker)的数量。建议:
1)如果负载模式是CPU密集型,如需处理大量TCP/IP,执行SSL或压缩,worker数量应与CPU内核数量匹配。
2)如果负载模式是磁盘IO绑定,如从存储或重代理服务不同的内容集合,worker的数量可能是核心数量的1到2倍。

现有worker模式问题,下面两种类型的行为将立即导致工作(worker)挂起的情况,同时影响到数千个连接。

  • 问题1:磁盘 I/O 上的大多数阻塞。如果没有足够的存储性能来提供特定工作(worker)生成的磁盘操作,该工作(worker)可能仍然阻止从磁盘读取/写入。
  • 问题2:与嵌入式脚本的有限支持有关。一个使用标准的 nginx 分发,只支持嵌入 Perl 脚本。一个简单的解释:关键问题是嵌入式脚本阻止任何操作或意外退出的可能性。

2.3 进程角色

nginx 在内存中运行多个进程; 有一个主进程和几个工作(worker)进程。 还有一些特殊用途的过程,特别是缓存加载器和缓存管理器。 所有进程都是单线程版本为 1.x 的 nginx。
所有进程主要使用共享内存机制进行进程间通信。主进程作为root用户运行。缓存加载器,缓存管理器和worker则无权限制用户运行。

》》主进程负责以下任务:

  • 读取和验证配置
  • 创建,绑定和关闭套接字
  • 启动,终止和维护配置的worker进程数
  • 重新配置,无需中断服务
  • 控制不间断的二进制升级(如果需要,启动新的二进制并回滚)
  • 重新打开日志文件
  • 编译嵌入式Perl脚本

》》worker进程接受和处理来自客户端的连接,提供反向代理和过滤功能,并执行几乎所有其他nginx能力。关于监视nginx实例的行为,系统管理员应该关注worker进程,因为它们是反应Web服务实际日常操作的过程。

》》缓存加载器进程负责检查磁盘缓存项目,并使用缓存元数据填充nginx的内存数据库。本质上,缓存加载器准备nginx实例来处理已经存储在磁盘上的特定分配的目录结构中的文件。它遍历目录,检查缓存内容元数据,更新共享内存的相关条目,然后在所有内筒清洁并准备使用时退出。

》》缓存管理器主要负责缓存到期和无效。在正常的nginx操作期间它保持在内存中,并且在失败的情况下由主进程重新启动。


2.4 缓存

在nginx中的缓存以文件系统上的分层数据存储的形式实现。
缓存密钥是可以配置的,并可以使用不同的请求特定参数来控制进入缓存的内筒。缓存密钥和缓存元数据存储在共享存储器段中,高速缓存加载,缓存管理器和worker可以访问它们。层次结构(级别和命名细节)通过 nginx 配置指令进行控制。 当响应写入缓存目录结构时,文件的路径和名称将从代理 URL 的 MD5 哈希导出。

将内容放入缓存的过程:

  • nginx从上游服务器读取响应时,内容首先写入缓存目录结构之外的临时文件。
  • nginx完成处理请求时,它重命名临时文件并将其转移到缓存目录。

若用于代理的临时文件目录位于另一个文件系统上,则该文件将被复制,因此建议将临时文件目录和缓存目录保存在同一个文件系统上。因此建议将临时文件目录和缓存目录保存在同一文件系统上。


三、Nginx应用

3.1 Nginx安装和操作的一些说明

1、安装
Nginx安装的时候同时需要安装一些依赖库。

  • pcre:负责做正则表达式。
  • openssl:用作MD5验证、NTPS加密。
  • zlib:用于压缩。

2、操作

  • 启动:sudo ./sbin/nginx -c ./conf/nginx.conf
  • 快速关闭服务:./sbin/nginx -s stop
  • 正常关闭服务:./sbin/nginx -s quit
  • 重新加载配置文件:./sbin/nginx -s reload
  • 重新打开日志文件:./sbin/nginx -s reopen

3.2 四层与七层负载均衡

分层模型

修改nginx dns 缓存时间_nginx_02

1)四层负载均衡

定义:

四层负载均衡工作在OSI模型的传输层,由于在传输层,只有TCP/UDP协议,这两种协议中除了包含源IP、目标IP以外,还包含源端口号及目的端口号。四层负载均衡服务器在接受到客户端请求后,以后通过修改数据包的地址信息(IP+端口号)将流量转发到应用服务器。

修改nginx dns 缓存时间_Nginx_03

2)七层负载均衡

定义:

七层负载均衡工作在OSI模型的应用层,应用层协议较多,常用http、radius、dns等。七层负载就可以基于这些协议来负载。这些应用层协议中会包含很多有意义的内容。比如同一个Web服务器的负载均衡,除了根据IP加端口进行负载外,还可根据七层的URL、浏览器类别、语言来决定是否要进行负载均衡。

修改nginx dns 缓存时间_nginx_04


3)四层和七层的区别

  • 四层通过虚拟IP+端口接收请求,然后再分配到真实的服务器
  • 七层通过虚拟的URL或主机名接收请求,然后再分配给真实的服务器
  • 四层交换机主要分析IP层及TCP/UCP层,实现四层流量负载均衡
  • 七层交换机除了支持四层负载均衡外,还有分析应用层的信息,如HTTP协议,URI或Cookie信息
  • 四层负载均衡的本质是转发,七层负载均衡的本质是内容交换和代理

修改nginx dns 缓存时间_修改nginx dns 缓存时间_05


如果是http选择nginx做代理,如果是tcp使用haproxy。Nginx可以用于七层负载均衡。


3.3 应用

3.3.1 做负载均衡

服务器拓扑结构:

修改nginx dns 缓存时间_修改nginx dns 缓存时间_06


工作流程:

访问机器先访问路由器,然后再到Nginx,Nginx再把数据分发给同一局域网内的其他服务。

路由器也称为网关,而nginx为应用网关。路由器到nginx是通过端口映射(通过路由器或者交换机直接配置),当外部机器访问到路由器端口的时候直接对应的是nginx子网的机器。因此给人的感觉是nginx是对接在公网上。比如:路由器对外提供的访问地址是112.93.116.78.8080,映射到nginx服务192.168.1.123.80。

修改nginx dns 缓存时间_缓存_07

DNS做负载均衡
一个域名映射多个IP地址,比如www.xzq.com–>通过域名解析的时候可以拿到多个IP地址(跟端口没关系),第一次响应的可能是第一个IP地址,第二次响应的可能是第二个IP地址,这里做的就是负载均衡。
该负载均衡跟开发没有什么关系,只需要配置号就行,其他不用管。

特点:响应没有那么及时,假设有一台机器宕机,需要同步到其他域名中的时候有可能会耗费10分钟之久。

》conf文件
在开发中核心的最常用的是conf文件。
conf文件的解析原理:解析conf文件中的worker_processes参数,通过源码搜索找到源码ngx_core_module函数,所有的模块都放到ngx_modules数组中。

conf文件配置说明

#每个worker_processes进程对应的连接数--每一个连接,可读可写的时候就是事件
events {
    worker_connections  1024; 
}
#http协议模块,如果是自己封装的通信协议,也可以在配置文件中作为一个模块
http { 
    server { 
#80为http默认端口,自定义端口最好大于1024
            	listen 8080; 
            	server_name  localhost; 
				#客户端请求body最大数量 网页设置小些,视频文件设置大些
				client_max_body_size 100m;	
				#要访问的网页或者文件
            	location / { 
                    root /usr/local/nginx/html/www; 
           		} 
            	location /images/ { 
                    root /usr/local/nginx/html; 
            	} 
        	} 
}

》http请求
http请求比较大的文件,有一个头range: 0–1024,都有一个对应的请求位置,如果服务器中的文件大小为500M,每次可以取25M,确定了开始和结束位置(位置由请求的客户端定义),就可以使用20个线程同时下载。比如第一个线程请求0-25,第二个线程请求26-50M…

问:20个线程,写入到同一个文件是不是回到了单线程处理?
事先已经分配好文件大小,每个线程写入到确定的位置即可。

问:磁头只有一个,写到盘里还是单线程?
磁盘SSD写入可以达到1G/s,软件写入一般比较快的100M/s。这里的瓶颈就不是磁盘的问题了。

问:断点续传是否可以分片,如果可以如何保证顺序?
断点续传也是需要分片的,但并非可以一个字节一个字节的续传:
1)多线程同时下载一个文件,需要确定每个线程的开始和结束位置。
2)断点续传,记住上一次的结束位置作为下一次的开始。

3.3.2 做代理

1、代理单台

修改nginx dns 缓存时间_修改nginx dns 缓存时间_08


A请求123,实际123会把数据传给168进行处理。123就是一个代理。

实例:
123服务器上IP地址为192.168.139.132,168服务器地址为192.168.139.133。

1)123服务的nginx.conf配置文件为:

events {
    worker_connections  1024; 
}
http { 
    server { 
            	listen 8080; 
            	server_name  localhost; 
				
            	location / { 
					proxy_pass http://192.168.139.133;
           		}  
        	} 
}

2)168服务的nginx.conf配置文件为:

events {
    worker_connections  1024; 
}
http { 
    server { 
            	listen 80; 
            	server_name  localhost; 
				
            	location / { 
					root   html;
   					 index  index.html index.htm;
           		}  
        	} 
}

3)启动123服务器Nginx,168服务器Nginx

./sbin/nginx -c ./conf/nginx.conf

4)使用网页访问123服务

修改nginx dns 缓存时间_修改nginx dns 缓存时间_09


可以看到显示的网页数据为168服务的数据。

2、代理多台
nginx.conf配置文件可以修改如下:

events {
    worker_connections  1024; 
}
http { 
		 upstream backend{
			server 192.168.139.133 weight=2;
			server 192.168.139.134 weight=1;
			}
    server { 
            	listen 80; 
            	server_name  localhost; 
				
            	location / { 
					proxy_pass http:// backend;
           		}  
        	} 
}

在网页请求Nginx服务,可以看到显示的网页不断在192.168.139.133和192.168.139.134服务之间进行切换。

指定轮询几率,weight 和访问比率成正比,用于后端服务器性能不均的情况。
那么 3次一般只会有 1 次会访问到 192.168.139.134,而有2 次会访问到192.168.139.133。

3.3.3 访问图片

1)在Nginx新建images文件夹,文件夹存放1.jpg 2.jpg 3.jpg三张图片。

修改nginx dns 缓存时间_nginx_10


2)修改nginx.conf 配置文件

events {
    worker_connections  1024; 
}
http { 
		 upstream backend{
			server 192.168.139.133 weight=2;
			server 192.168.139.134 weight=1;
		}
   		server { 
            	listen 80; 
            	server_name  localhost; 
				
            		location / { 
						proxy_pass http:// backend;
           			}  
				
					location /images/ { 
						root ./;
           			}
       		 } 
}

3)网页访问图片:

修改nginx dns 缓存时间_缓存_11

3.3.4 访问视频

1)在Nginx新建media文件夹,文件夹存放视频文件RB.mp4。
2)修改nginx.conf配置文件

events {
    worker_connections  1024; 
}
http { 
		 upstream backend{
			server 192.168.139.133 weight=2;
			server 192.168.139.134 weight=1;
		 }
    	server { 
            	listen 80; 
            	server_name  localhost; 
				
            	location / { 
					proxy_pass http:// backend;
           		}  
				
				location /images/ { 
					root ./;
           		}
				location ~ \.(mp3|mp4) { 
					root media;
           		}

        } 
}

静态资源都可以使用上面两种方式进行配置,比如css、js、png。

3.3.5 CGI公共网关接口

CGI是一个进程,当一个客户端请求Web服务器的时候,服务器把一些响应的数据转换给CGI,计算出来的结果返回给Web服务器。

修改nginx dns 缓存时间_Nginx_12

问:CGI与后端的Server有什么区别?
CGI是对外提供输入输出流,比如std cout,printf,直接打印到网页上。Server的协议是http协议。

问:CGI到底用在哪里?在哪种场景下必须要用到CGI?
在线编译工具,当你提交代码的时候编译工具可以给你实时输出结果,这是使用CGI实现。

问:CGI与fastcgi的区别?
CGI是一请求一进程方式,fastcgi:启动进程池等待。

问:Nginx如何做CGI?
需要用到fcgi和spawn-fcgi两个工具。其中:
fcgi:CGI的库文件,我们可以通过这里的接口开发CGI。
spawn-fcgi:启动CGI的工具。

CGI编程
1)spawn-fcgi编译:解压后进到文件夹执行./configure,然后执行make,再把src文件里的spawn-fcgi拷贝到Nginx的sbin文件夹。

2)fcgi编译:解压后进到文件夹执行./configure,然后执行make。

这个时候可能会出现以下编译错误提示:

修改nginx dns 缓存时间_nginx_13


我们可以通过在include/fcgio.h头文件中增加#include <stdio.h>即可解决。

最后执行 sudo make install即可。

3)源码示例

  • 编码
#include <stdio.h>
#include <fcgi_stdio.h>

int main()
{
	while(0 <= FCGI_Accept())
	{
		printf("Content-type:text/html\r\n");
		printf("\r\n");
		printf("<title> Fast CGI Hello!</title>");
		printf("<h1>ZVoice cgi</h1>");
		printf("Thank you cgi\n");
	}
}
  • 编译
    编译CGI程序:gcc –o zvoice_cgi zvoice_cgi.c -lfcgi
  • 运行
./zvoice_cgi

发现程序运行失败,报找不到对应库文件错误:

修改nginx dns 缓存时间_修改nginx dns 缓存时间_14


于是从头开始检查cgi库文件的安装,从./configure到make到sudo make install,都没有问题,但是依旧报错。

这个时候我们思考一下,库文件已经安装但是调用失败,应该是系统没有识别到,这个时候我们设置动态库为系统共享,执行指令:sudo ldconfig,然后再运行程序发现程序运行成功。

修改nginx dns 缓存时间_Nginx_15


ldconfig:动态库管理,让动态库为系统共享,把新安装的so,为系统共享。

  • 启动CGI程序:./spawn-fcgi -a 127.0.0.1 -p 9002 -f /home/ubuntu/nginx/zvoice_cgi
  • 配置Nginx的conf文件
events {
    worker_connections  1024; 
}
http { 
		 upstream backend{
			server 192.168.139.133 weight=2;
			server 192.168.139.134 weight=1;
		}
    	server { 
            	listen 80; 
            	server_name  localhost; 
				
            	location / { 
					proxy_pass http:// backend;
           		}  
				
				location /images/ { 
					root ./;
           		}
				location ~ \.(mp3|mp4) { 
					root media;
           		}

        	} 
		server{
			listen 9000;
			location ~ \.cgi{
			fastcgi_pass  127.0.0.1:9002; 
			fastcgi_index index.cgi;
		    fastcgi_param SCRIPT_FILENAME cgi$fastcgi_script_name;
			include ../conf/fastcgi_params;
			}
		}
}
  • 重启Nginx
  • 访问网页:http://192.168.139.132:9000/zvoice_cgi.cgi
  • 修改nginx dns 缓存时间_修改nginx dns 缓存时间_16