一文读懂NGINX
Nginx是异步框架的网页服务器,也可以用作反向代理、负载平衡器和HTTP缓存。该软件由伊戈尔·赛索耶夫创建并于2004年首次公开发布。2011年成立同名公司以提供支持。2019年3月11日,Nginx公司被F5 Networks以6.7亿美元收购。
Nginx有以下的特点
- 由C编写,占用的资源和内存低,性能高。
- 单进程多线程,当启动nginx服务器,会生成一个master进程,master进程会fork出多个worker进程,由worker线程处理客户端的请求。
- 支持反向代理,支持7层负载均衡(拓展负载均衡的好处)。
- 高并发,nginx是异步非阻塞型处理请求,采用的epollandqueue模式
- 处理静态文件速度快
- 高度模块化,配置简单。社区活跃,各种高性能模块出品迅速。
NGINX单元使用三种主要类型的配置对象。所有都是用JSON定义的。应用程序Application对象定义单元正在运行的应用程序的特征,例如语言,过程控件以及文件系统上的位置。侦听器Listener对象定义单元配置,该配置将传入的请求通过已定义的IP地址和端口定向到指定的应用程序。路由对象Router提供路由功能。路由功能包括路由到Unit应用程序,提供静态文件,代理到外部服务以及服务器池上的负载平衡。本章将建立对这些对象的基础理解。
应用程序 Application
部署在NGINX单元上的每个应用程序都由一个应用程序对象定义。用JSON定义的应用程序对象指定了应用程序的属性。每种应用程序类型都有其自己的必需和可选属性。许多不同的应用程序属性控制着单位流程的管理和限制。 type属性是所有应用程序有的唯一必需的流程管理属性。它定义了应用程序语言,例如PHP,Python,Golang,Ruby或Perl。其他属性包括应用程序进程数限制;时间限制;用户,组和环境变量;和工作目录。
监听器 Listener
为了指示NGINX单元监听传入的请求,必须定义一个监听器对象。侦听器对象定义单元将传入请求定向到的应用程序。侦听器对象是指定给定义IP和端口的键的值。在此示例中,*用于IP地址,因此指示单元监听分配给服务器的所有IP地址。侦听器对象具有两个属性:pass和tls(可选)。 pass属性采用一个字符串值,该值指定将请求定向到的应用程序,应用程序目标,路由或上游。该示例将请求直接发送到名为my-app的应用程序。
路由 Routes
路由具有两个属性:match和action。这两个属性都有其自己的对象类型,这些对象类型具有许多可配置的属性。
Match支持不同的选项:arguments, cookies, destination, headers, host, method, scheme, source, 和 uri。
Action支持pass, share, fallback, proxy, return, 和 location等。
代理 Proxy
代理操作类型会将请求中继到外部HTTP服务。此操作的功能是基本反向代理的功能。可以使用Unix,IPv4和IPv6套接字地址作为目标。该功能将适合许多用例。要获得更多高级功能,可以使用NGINX反向代理和负载平衡器服务器。
静态文件
share操作类型将提供本地目录中的静态内容。它可以与fallback操作结合使用,该fallback操作将告诉Unit如果找不到所请求的文件或Unit权限不足以访问该文件,则如何指示该请求。fallback操作可以将请求路由到pass,proxy或share操作。当回退将请求定向到另一个share操作时,可以嵌套fallback操作。
上游服务 Upstreams
单元配置的上游upstreams属性定义了许多命名的上游upstreams对象。每个上游upstreams对象都定义一个服务器servers对象。服务器servers对象使用键来定义目标,并使用该值来指定可选的权重。上游upstreams使用加权循环负载均衡算法。权重最大值为1000000,最小值为0(此类服务器未收到任何请求),默认值为1。
目标 Targets
路由Routes和应用程序Applications配置的结合。应用程序配置具有一个目标targets属性,该属性定义了应用程序上下文的另一层。 PHP应用程序必须具有在应用程序级别或目标级别定义的root和script或index属性。在该示例中,管理目标targets的根目录与网站目标的根目录不同。 admin目标还允许调用目录结构中的所有PHP文件,其中admin.php为索引。网站目标包含管理根目录;但是,它仅允许调用index.php脚本。一个应用程序最多可以配置254个目标。只需将目标名称附加到应用程序名称空间,即可路由到目标。
了解了主要对象配置后,下面我们就看看Nginx提供的一些主要的功能和相应的配置对象。
当今的互联网用户体验都要求性能和正常运行时间。为此,运行同一系统的多个副本,并将负载分布在它们上。随着负载的增加,可以使系统的另一个副本联机。这种架构技术称为水平缩放。基于软件的基础架构因其灵活性而越来越受欢迎,从而开辟了无限的可能性。无论哪种用例,都需要一种与基础架构一样动态的负载平衡解决方案。 NGINX通过HTTP,TCP和UDP负载平衡方式满足了这一需求。
HTTP 模式
TCP模式
UDP 模式
负载均衡方法
- Round robin 基于权重,按统计顺序分配流量
- Least connections 把流量分配到连接最少的上游服务上去
- Generic hash 通过管理员定义的基于文本,变量的哈希值来分配流量,常用于服务和请求内容匹配的场景
- Random 基于权重,随机分配流量
- IP hash,仅对HTTP生效,使用IP地址的前三个字节生成哈希值,并相应地分配流量
被动健康检查
利用健康检查,确保流量只会路由到健康的服务上去
慢启动
慢启动给服务时间热身,在上述的例子中,服务1会等待20秒接受新的请求,已给出足够的时间来初始化,准备缓存等。
TCP健康检查
在上述的例子中,如果上游服务器无法响应NGINX发起的三个或更多TCP连接,则将被视为不正常。 NGINX每10秒执行一次检查。仅在通过两次运行状况检查后,服务器才被视为运行状况良好。
NGINX也被归类为Web流量控制器。可以使用NGINX根据许多属性智能的路由流量和控制流。NGINX能够基于百分比拆分客户端请求,利用客户端的地理位置以及以速率,连接和带宽限制的形式控制流量的能力。
A/B测试
当测试电子商务网站上转换率的不同类型的营销和前端功能时,这种类型的A / B测试很有用。应用程序通常使用一种称为canary release的部署类型。在这种类型的部署中,流量会缓慢切换到新版本。在推出新版本的代码时,在不同版本的应用程序之间拆分客户端可能很有用,以限制发生错误时的爆炸半径。无论在两个不同的应用程序集之间拆分客户端的原因是什么,NGINX都通过使用split_clients模块使此操作变得简单。
限制连接数目
上述配置限制到Server的连接数为40,返回429错误。
限制速率
限速模块功能强大,可防止滥用快速请求,同时仍为所有人提供优质服务。限制请求速率的原因有很多,其中之一就是安全性。您可以通过在登录页面上设置非常严格的限制来拒绝暴力攻击。您可以对所有请求设置合理的限制,从而禁用可能试图拒绝为您的应用程序提供服务或浪费资源的恶意用户的计划。
限制带宽
通过限制特定连接的带宽,NGINX可以以指定的方式在所有客户端之间共享其上传带宽。这两个指令可以完成所有操作:limit_rate_after和limit_rate。 limit_rate指令默认指定给定上下文的速率限制,以每秒字节数为单位。但是,可以为兆字节指定m或为千兆字节指定g。两个指令的默认值均为0。值0表示完全不限制下载速率。此模块允许以编程方式更改客户端的速率限制。
缓存通过存储将来要再次提供的请求响应来加速内容服务。内容缓存减少了上游服务器的负载,缓存了完整的响应,而不需要运行计算并再次查询同一请求。缓存可提高性能并减少负载,这意味着可以用更少的资源更快地服务。在战略位置扩展和分发缓存服务器可能会对用户体验产生巨大影响。最好将内容托管在消费者附近,以获得最佳性能。可以将内容缓存在用户附近。这就是内容交付网络或CDN的模式。借助NGINX,可以在可以放置NGINX服务器的任何地方缓存内容,从而有效地创建自己的CDN。借助NGINX缓存,还可以在上游发生故障时被动缓存并提供缓存的响应。
缓存区域
使用proxy_cache_path定义缓存区域和响应的路径。
缓存忽略
如果HTTP请求标头cache_bypass设置为任何非0的值,该配置将告诉NGINX绕过缓存。
缓存性能
此location块指定客户端可以缓存CSS和JavaScript文件的内容。 expires指令指示客户端一年后其缓存资源将不再有效。 add_header指令将HTTP响应标头Cache-Control添加到响应中,其值为public,这允许沿途的任何缓存服务器缓存资源。如果我们指定专用,则仅允许客户端缓存该值。
缓存性能有很多因素,磁盘速度最为重要。 NGINX配置中可以执行许多操作来帮助提高缓存性能。一种选择是以这样一种方式设置响应的标题,即客户端实际上缓存了响应,根本不向NGINX发出请求,而只是从其自己的缓存中提供服务
NGINX能够验证客户端。使用NGINX对客户端请求进行身份验证可减轻工作量,并能够阻止未经批准的请求到达应用服务器。 NGINX开源可用的模块包括基本身份验证和子请求身份验证。
基本认证
生成一个密码文件,如下例:
使用openssl对该文件加密
使用auth_basic和auth_basic_user_file来配置认证
和第三方认证服务集成
安全性是分层完成的,安全模型必须有多层才能真正得到加强。NGINX提供不同的功能来支持安全能力。
基于IP地址的安全控制
allow和deny指令在HTTP,Server和Location上下文中有效。顺序检查规则,直到找到与远程地址匹配的为止。
跨域资源共享
此示例通过使用map将GET和POST方法分组在一起进行了精简。 OPTIONS请求方法向客户端返回有关此服务器的CORS规则的预检请求。 CORS允许使用OPTIONS,GET和POST方法。设置Access-Control-Allow-Origin标头可以使从该服务器提供的内容也可以在与该标头匹配的原始页面上使用。预检请求可以在客户端上缓存1,728,000秒或20天。
当诸如JavaScript之类的资源所请求的资源不是其自己的域时,它会构成跨域访问CORS。当请求被视为跨源请求时,浏览器必须遵守CORS规则。如果浏览器没有专门允许其使用的标头,则它将不使用该资源。为了使我们的资源可以被其他子域使用,我们必须设置CORS标头,这可以通过add_header指令完成。如果请求是具有标准内容类型的GET,HEAD或POST,并且该请求没有特殊的标头,则浏览器将发出请求,并且仅检查源。其他请求方法将使浏览器发出前请求,以检查该资源将服从的服务器条款。如果没有适当地设置这些标头,则浏览器在尝试利用该资源时将给出错误消息。
客户端加密
该例子配置SSL来加密http服务。
控制特定资源的访问
此配置创建一个内部且面向公众的位置块。面向公众的位置块/ resources将返回403 Forbidden,除非请求URI包含一个md5哈希字符串,可以使用提供给secure_link_secret指令的密码进行验证。除非已验证URI中的哈希,否则$ secure_link变量为空字符串。
用Secret保护资源是确保文件受到保护的好方法。该Secret与URI结合使用。然后,此字符串将被md5散列,并且该md5散列的十六进制摘要在URI中使用。哈希将放入链接中,并由NGINX评估。 NGINX知道哈希之后的URI中所请求文件的路径。 NGINX还通过secure_link_secret指令提供了Secret信息。 NGINX能够快速验证md5哈希并将URI存储在$ secure_link变量中。如果无法验证哈希,则将变量设置为空字符串。请务必注意,传递给secure_link_secret的参数必须是静态字符串;它不能是变量。
通过过期时间控制资源
secure_link指令采用两个参数,并以逗号分隔。第一个参数是保存md5哈希值的变量。本示例使用HTTP参数md5。第二个参数是一个变量,它以Unix纪元时间格式保存链接到期的时间。 secure_link_md5指令采用单个参数,该参数声明用于构造md5哈希值的字符串的格式。与其他配置一样,如果哈希未通过验证,则$secure_link变量将设置为空字符串。但是,在这种用法下,如果哈希匹配但时间已到期,则$secure_link变量将设置为0。
HTTP重定向
此配置在端口80上监听IPv4和IPv6以及任何主机名的默认服务器。 return语句将301永久重定向返回到同一主机上的HTTPS服务器并请求URI。
务必始终在适当的时候重定向到HTTPS。可能会发现不需要重定向所有请求,而仅重定向那些在客户端和服务器之间传递敏感信息的请求。在这种情况下,可能只想将return语句放在特定的位置,例如/ login。
HTTP / 2是对HTTP协议的主要修订。这个版本中完成的许多工作都集中在传输层,例如在单个TCP连接上启用完整的请求和响应多路复用。通过对HTTP头字段进行压缩获得了效率,并增加了对请求优先级的支持。该协议的另一个重要补充是服务器能够将消息推送到客户端。
基本配置
要打开HTTP / 2,只需要将http2参数添加到listen指令中
gRPC
NGINX能够接收,代理,负载均衡,路由和终止gRPC调用。使用gRPC模块,NGINX可以设置,更改或删除gRPC调用标头,设置请求超时以及设置上游SSL / TLS规范。当gRPC通过HTTP / 2协议进行通信时,可以将NGINX配置为在同一端点上接受gRPC和非gRPC Web通信。
HTTP/2服务端推送
要使用HTTP / 2服务器推送,必须配置为HTTP / 2配置服务器。可以指示NGINX使用http2_push指令推送特定文件。该指令采用一个参数,即要推送到客户端的文件的完整URI路径。
容器在应用程序层提供了一个抽象层,从而将软件包的安装和相关性从部署转移到了构建过程。这很重要,因为工程师可以以统一方式运行和部署的代码单元,而不管环境如何。将容器提升为可运行单元可减少依赖环境和环境之间的配置麻烦的风险。鉴于此,组织将其应用程序部署在容器平台上方面有很大的动力。在容器平台上运行应用程序时,通常要对尽可能多的堆栈进行容器化,包括代理或负载平衡器。 NGINX可以轻松地进行容器化和部署。它们还包括许多使交付容器化应用程序的功能。
官方镜像
NGINX已通过Docker Hub提供了正式的Docker镜像。这个官方的Docker镜像可以使用自己喜欢的应用程序交付平台NGINX轻松地在Docker中快速启动和运行。能够使用一个命令在容器中启动NGINX并运行它!这些官方镜像的Dockerfile和源可在GitHub上获得。以通过构建自己的Dockerfile并在FROM命令中指定官方镜像来扩展官方镜像。
创建自己的Docker镜像
通过以下的Dockerfile可以创建自己的Nginx镜像。
目录
使用环境变量
要使用perl_set,必须安装ngx_http_perl_module。可以通过动态或静态(从源代码构建)加载模块来实现。默认情况下,NGINX从其环境中删除所有的环境变量;需要使用env指令声明不想删除的任何变量。 perl_set指令采用两个参数:要设置的变量名称和呈现结果的perl字符串。
Kubernetes Ingress Controller
Kubernetes是容器编排和管理的领先平台。入口控制器Ingress Controller是将流量路由到应用程序其余部分的边缘容器。 NGINX非常适合担任此角色,并使其易于配置。 NGINX-Ingress项目通过DockerHub映像提供了NGINX开放源代码入口控制器。使用NGINX入口控制器启用Kubernetes集群可提供NGINX的所有相同功能,但具有Kubernetes网络和DNS的附加功能来路由流量。
Nginx作为常见的Web服务被广泛地用于部署应用服务,了解和用好Nginx是软件架构师的必修课。
看了这篇,我确定你已经彻底搞懂Nginx了
Nginx是一款轻量级的Web服务器、反向代理服务器,由于它的内存占用少(一个worker进程只占用10-12M内存),启动极快,高并发能力强,在互联网项目中广泛应用。
上图基本上说明了当下流行的技术架构,其中Nginx有点入口网关的味道。
经常听人说到一些术语,如反向代理,那么什么是反向代理,什么又是正向代理呢?下面是一个简单的总结。
正向代理
由于防火墙的原因,我们并不能直接访问谷歌,那么我们可以借助VPN来实现,这就是一个简单的正向代理的例子。这里你能够发现,正向代理“代理”的是客户端,而且客户端是知道目标的,而目标是不知道客户端是通过VPN访问的。
正向代理示意图
反向代理
当我们在外网访问百度的时候,其实会进行一个转发,代理到内网去,这就是所谓的反向代理,即反向代理“代理”的是服务器端,而且这一个过程对于客户端而言是透明的。
反向代理示意图
Nginx的Master-Worker模式
要启动nginx,只需要输入命nginx,其中xxx是你nginx的安装目录。
nginx进程
启动Nginx后,其实就是在80端口启动了Socket服务进行监听,如图所示,Nginx涉及Master进程和Worker进程。
Master-Worker模式
Master进程的作用:读取并验证配置文件nginx.conf;管理worker进程;
Worker进程的作用:每一个Worker进程都维护一个线程(避免线程切换),处理连接和请求;注意Worker进程的个数由配置文件决定,一般和CPU个数相关(有利于进程切换),配置几个就有几个Worker进程,上面的例子只有1个Worker进程。
思考1:Nginx如何做到热部署?
所谓热部署,就是配置文件nginx.conf修改后,不需要stop Nginx,不需要中断请求,就能让配置文件生效!(nginx -s reload 重新加载/nginx -t检查配置/nginx -s stop)
通过上文我们已经知道worker进程负责处理具体的请求,那么如果想达到热部署的效果,可以想象:
方案一:修改配置文件nginx.conf后,主进程master负责推送给worker进程更新配置信息,worker进程收到信息后,更新进程内部的线程信息。
方案二:修改配置文件nginx.conf后,重新生成新的worker进程,当然会以新的配置进行处理,而且新的请求都必须交给新的worker进程,至于老worker进程,等把那些以前的请求处理完毕,kill掉即可。
Nginx采用的就是方案二来达到热部署的!
思考2:Nginx如何做到高并发下的高效处理?
上文已经提及Nginx的worker进程个数与CPU绑定、worker进程内部包含一个线程高效回环处理请求,这的确有助于效率,但这是不够的。
作为专业的程序员,我们可以开一下脑洞:BIO/NIO/AIO、异步/同步、阻塞/非阻塞…
要同时处理那么多的请求,要知道,有的请求需要发生IO,可能需要很长时间,如果等着它,就会拖慢worker的处理速度。
Nginx采用了Linux的epoll模型,epoll模型基于事件驱动机制,它可以监控多个事件是否准备完毕,如果OK,那么放入epoll队列中,这个过程是异步的。worker只需要从epoll队列循环处理即可。
思考3:Nginx挂了怎么办?
Nginx既然作为入口网关,很重要,如果出现单点问题,显然是不可接受的。答案是:Keepalived+Nginx实现高可用。
Keepalived是一个高可用解决方案,主要是用来防止服务器单点发生故障,可以通过和Nginx配合来实现Web服务的高可用。(其实,Keepalived不仅仅可以和Nginx配合,还可以和很多其他服务配合)
Keepalived+Nginx实现高可用的思路:
第一:请求不要直接打到Nginx上,应该先通过Keepalived(这就是所谓虚拟IP,VIP)
第二:Keepalived应该能监控Nginx的生命状态(提供一个用户自定义的脚本,定期检查Nginx进程状态,进行权重变化,,从而实现Nginx故障切换)
Keepalived+Nginx
我们的主战场:nginx.conf
很多时候,在开发、测试环境下,我们都得自己去配置Nginx,就是去配置nginx.conf。nginx.conf是典型的分段配置文件,下面我们来分析下。在 Nginx 内部,你可以指定多个虚拟服务器,每个虚拟服务器用 server{} 上下文描述。
虚拟主机
nginx的配置文件主要由指令构成,指令主要包括名称和参数,以分号;结束。如下是一个虚拟服务器的配置:listen 指令来指定该虚拟主机在监听给定的 IP 端口组合;server_name指令检测 Host 头以决定请求到底匹配到哪个虚拟主机…nginx的配置项很多,具体可以查阅网上资料。
http的server段
访问结果
其实这是把Nginx作为web server来处理静态资源,
1:location可以进行正则匹配,应该注意正则的几种形式以及优先级。(这里不展开)
2:Nginx能够提高速度的其中一个特性就是:动静分离,就是把静态资源放到Nginx上,由Nginx管理,动态请求转发给后端。
3:我们可以在Nginx下把静态资源、日志文件归属到不同域名下(也即是目录),这样方便管理维护。
4:Nginx可以进行IP访问控制,有些电商平台,就可以在Nginx这一层,做一下处理,内置一个黑名单模块,那么就不必等请求通过Nginx达到后端在进行拦截,而是直接在Nginx这一层就处理掉。
反向代理—proxy_pass
所谓反向代理,很简单,其实就是在location这一段配置中的root替换成proxy_pass即可。root说明是静态资源,可以由Nginx进行返回;而proxy_pass说明是动态请求,需要进行转发,比如代理到Tomcat上。
反向代理,上面已经说了,过程是透明的,比如说request -> Nginx -> Tomcat,那么对于Tomcat而言,请求的IP地址就是Nginx的地址,而非真实的request地址,这一点需要注意。不过好在Nginx不仅仅可以反向代理请求,还可以由用户自定义设置HTTP HEADER。
负载均衡—upstream
上面的反向代理中,我们通过proxy_pass来指定Tomcat的地址,很显然我们只能指定一台Tomcat地址,那么我们如果想指定多台来达到负载均衡呢?
1:通过upstream来定义一组Tomcat,并指定负载策略(IPHASH、加权论调、最少连接),健康检查策略(Nginx可以监控这一组Tomcat的状态)等。
2:将proxy_pass替换成upstream指定的值即可。
负载均衡需要注意的问题:选择不同的负载均衡算法,可能会带来不同的问题,如果选择轮询方式,那么一个请求,可以到A server,也可以到B server,我们得注意用户状态的保存问题,如Session会话信息,不能在保存到服务器上。
如果选择散列,没有了上面的问题,但是又得考虑,什么样的散列算法尽可能均匀打到后端的服务器上,总之实际应用中需要根据场景权衡选择。
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。