首页 > 技术文章 > Nginx:使用心得与八个应用场景的配置

97z4moon 2021-08-11 17:34 原文

 --- 阅读时间约 15 分钟 --- 

Nginx

众所周知,互联网已经离不开  WEB服务器  ,技术领域中  WEB服务器  不止  Nginx  一个,其他还有很多如  Apache  、 Tomcat  、 Lighthttpd  等,相信能看到这篇文章的友友们对这几个单词都不陌生。而  Nginx  因其高性能、轻量性、健壮性让互联网工厂们无法忽视它的存在。

 Nginx  是一个免费、开源、高性能的 HTTP服务器 和 反向代理 ,以及  IMAP / POP3 代理服务器。以高性能、稳定性和丰富的功能、简单的配置、低资源消耗而闻名。

包括 由淘宝发起的项目  Tengine  、由章亦春发起的开源项目  OpenResty  ,都是基于  Nginx  进行的定向开发。

- Nginx 进程模型

 Nginx  服务器,正常运行过程中:

  • 多进程:一个 Master 进程、多个 Worker 进程。
  • Master 进程:管理 Worker 进程。 对外接口 :接收外部的操作(信号); 对内转发 :根据外部操作的不同,通过信号管理 Worker; 监控 :监控 Worker 进程的运行状态,Worker 进程异常终止后,自动重启 Worker 进程。
  • Worker 进程: 所有 Worker 进程都是平等的。 实际处理 :网络请求,由 Worker 进程处理。 Worker 进程数量 :在 nginx.conf 中配置,一般设置为核心数,充分利用 CPU 资源,同时,避免进程数量过多,避免进程竞争 CPU 资源,增加上下文切换的损耗。

- Master 工作过程

- HTTP连接简历和请求处理过程

  • Nginx 启动时,Master 进程,加载配置文件。
  • Master 进程,初始化监听的 Socket。
  • Master 进程,Fork 出多个 Worker 进程。
  • Worker 进程,竞争新的连接,获胜方通过三次握手,建立 Socket 连接,并处理请求。

- Nginx 高性能、高并发

  • Nginx 采用多进程 + 异步非阻塞方式 (IO 多路复用 Epoll)。
  • 请求的完整过程:建立连接 --> 读取请求 --> 解析请求 --> 处理请求 --> 响应请i去。
  • 请求的完整过程对应到底层就是:读写 Socket 事件。

- Nginx 事件处理模型

Request:Nginx中 HTTP 请求。

基本的 HTTP Web Server 工作模式:

  • 接收请求: 逐行读取请求行和请求头,判断由请求体后,读取请求体。
  • 处理请求
  • 返回响应:根据处理结果,生成相应的 HTTP 请求(响应行、响应头、响应体)。

- 模块化体系结构

- Nginx 功能模块

根据其功能基本上分为以下几种类型:

  1. event module:搭建了独立于操作系统的事件处理机制的框架,及提供了各具体事件的处理。包括 ngx_events_module,ngx_event_core_module 和 ngx_epoll_module 等。Nginx 具体使用何种事件处理模块,这依赖于具体的操作系统和编译选项。
  2. phase handler:此类型的模块也被直接称为 handler 模块。主要负责处理客户端请求并产生待响应内容,比如 ngx_http_static_module 模块,负责客户端的静态页面请求处理并将对应的磁盘文件准备为响应内容输出。
  3. output filter:也称为 filter 模块,主要是负责对输出的内容进行处理,可以对输出进行修改。
  4. upstream:upstream 模块实现反向代理的功能,将真正的请求转发到后端服务器上,并从后端服务器上读取响应,发回客户端。upstream 模块是一种特殊的 handler,只不过响应内容不是真正由自己产生的,而是从后端服务器上读取的。
  5. load-balancer:负载均衡模块,实现特定的算法,在众多的后端服务器中,选择一个服务器出来作为某个请求的转发服务器。

- WEB服务

“ WEB服务 ”,这个概念其实很简单,个人理解大致分为以下几个部分:

大部分程序员的工作就是进行服务编写和项目搭构,再由  Nginx  这样的工具,对外持续提供服务,当然不仅是WEB服务,我们平时通过网络访问的绝大部分资源,其内部都有  Nginx  的身影。

 Nginx  是一个高性能处理  HTTP协议  的服务软件,而互联网在我们日常生活中,凡是用于给用户展示的,绝大多数都是  HTTP协议  ,因此像  Nginx  这种以高性能稳定运行的解析HTTP协议软体理所当然会无处不在。当然如果仅仅是这样我们大可以用其他软体来代替, Nginx  真正的强大还不止于这些。


WEB服务器

- 概述

 Nginx  本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用它来做服务器,同时现在也很流行动静分离,亦可通过它来实现。

- Nginx配置

server {
        listen       80;                                                       
        server_name  localhost;                                             
 
        location / {
               root   e:/www/data;
               index  index.html;
       }
}

通过以上配置,我们在访问  http://localhost  时就会默认访问到  E://www/data  目录下的  index.html 

- 应用场景

项目只有静态资源

- 实现方式

更改Nginx配置并启动,即可完成部署。


反向代理

- 概述

反向代理是大部分用户通过  Nginx  完成最多的一件工作, 反向代理(Reverse Proxy) 方式是指以代理服务器来接受 Internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 Internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器处于同一个网络环境,当然也可能是同一台服务器的不同端口,这就是国内市场上大部分中小型企业的服务器中存在  Nginx  服务的原因,甚至隔壁 SegmentFault 也经常会全站访问 502 Bad Gateway —— Nginx xx.xx.xx(版本号)。

- Nginx配置

server {
       listen 80;
       server_name localhost;

       location / {
             proxy_pass http://localhost:8080;
              proxy_set_header Host $host:$server_port;
  }
}

- 应用场景

业务服务器不能直接被外部网络访问,即不能直接映射外网ip,需要一台代理服务器,而代理服务器能被外部网络访问同时又和业务服务器的网络相通。

- 实现方式

使用反向代理功能。将  Nginx  服务器映射外网ip,业务服务器无需映射,外网用户访问时首先访问  Nginx  服务器,然后再由  Nginx  服务器访问业务服务器资源后转发给用户,这是目前互联网服务器的主流方案,既满足了业务外网访问的需求,又保证了业务服务器的安全。


动静分离

- 概述

动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好拆分后,我们就可以根据静态资源(如html、css、js、图片文件)的特点对其做缓存操作,这样就完成了网站静态化处理的核心思路。并且  Nginx  安装在服务器上的 http 和 反向代理服务组件,除了WEB服务意外还提供了  负载均衡  服务,这对用户访问服务器尤其在高并发的时候尤其关键,可以将用户访问静态资源和动态请求区分开,分配给不同的执行单元,提高响应速度,具体会在下文中提出。

- Nginx配置

upstream test{ 
       server localhost:8080; 
       server localhost:8081; 
} 

server { 
       listen       80; 
       server_name  localhost; 

       location / { 
            root   e:/wwwroot; 
            index  index.html; 
       } 

       # 所有静态请求都由nginx处理,存放目录为html 
       location ~ .(gif|jpg|jpeg|png|bmp|swf|css|js)$ { 
            root    e:/wwwroot; 
       } 

       # 所有动态请求都转发给tomcat处理 
       location ~ .(do)$ { 
            proxy_pass  http://test; 
       } 

       error_page   500 502 503 504  /50x.html; 
       location = /50x.html { 
            root   e:/wwwroot; 
       } 
}

- 应用场景

项目对性能有要求,需要写出对并发和健壮性有要求的WEB服务。

- 实现方式

上述需求我们就可以使用静态服务器及反向代理模块,将静态资源由  Nginx  本地提供,动态资源反向代理到后端的  Tomcat  提供,其中  Tomcat  和  Nginx  可以在同一台服务器也可以在不同服务器中。


访问入口统一

- 应用场景

最近开发公司一个项目就是,不同地级市分别对应不同的业务首页,就是用  Nginx  实现的。多套业务系统使用同一个ip或者域名进行访问,以不同后缀进行区分,需要一台  Nginx  服务器做统一的访问入口。

- 实现方式

使用  Nginx  对入口项目进行部署,再使用反向代理模块将所有  Nginx  服务器作为外网或内网用户统一的访问入口,然后根据设置的 localtion 规则匹配不同的后缀转发至不同的业务服务器。通常和前文提到的反向代理场景结合使用实现一个域名来访问多个业务系统,同时甚至还可以实现统一的 https 访问,通过在  Nginx  服务器上配置证书,后端所有业务服务器无需每台额外配置证书即可实现 https 访问。


浏览器跨域

- 概述

这一场景其实就是使用了  反向代理  模块,由于服务器不存在  同源策略  ,因此通过  Nginx  代理服务器将请求中转即可解决开发时临时请求跨域问题,这应该是前端开发用  Nginx  做的最多的事。

- Nginx配置

阅读我的另一篇文章:Nginx:多项目开发配置跨域代理

- 应用场景

将前后端项目都部署在服务器之后,因为请求和服务放在一起所以不存在跨域问题。但在非维护的开发时,如果后端没有为自己的服务提供跨域,我们必须自己来完成以请求数据来完成开发。

不过我们在工作中并不是每个项目都使用了  webpack  或者引入了类似  http-proxy-middleware  的中间件,甚至还有可能维护  JSP  项目,所以我们有必要了解一个轻量级即开即用的跨域工具。


正向代理

- 概述

正向代理,意思是一个位于客户端和原始服务器 (origin server) 之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标 (原始服务器) ,然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。

 Nginx  不支持  CONNECT  ,所以无法正向代理  HTTPS  网站(网上银行、Gmail)。如果访问  HTTPS  网站,比如:https://www.google.com ,

Nginx access.log 日志:" CONNECT www.google.com:443 HTTP/1.1 " 400 。

- Nginx配置

 1 server {
 2     resolver 8.8.8.8;
 3     resolver_timeout 5s;
 4  
 5     listen 80;
 6  
 7     access_log  /home/reistlin/logs/proxy.access.log;
 8     error_log   /home/reistlin/logs/proxy.error.log;
 9  
10     location / {
11         proxy_pass scheme://scheme://host$request_uri;
12         proxy_set_header Host $http_host;
13  
14         proxy_buffers 256 4k;
15         proxy_max_temp_file_size 0;
16  
17         proxy_connect_timeout 30;
18  
19         proxy_cache_valid 200 302 10m;
20         proxy_cache_valid 301 1h;
21         proxy_cache_valid any 1m;
22     }
23 }

行2    resolver  是配置正向代理的  DNS服务器  ,比如 Google Public DNS .

行3   - 超时时间 5s .

行5    listen  是正向代理的端口 .

行11 - 配置正向代理参数,与 行12 均是由  Nginx  变量组成 .

行12 - 解决当 URL 中带 " . " 后  Nginx 503  错误 .

行14 - 配置缓存大小 . 

行15 - 关闭磁盘缓存读写,减少  I / O  .

行17 - 代理连接超时时间 .

- 应用场景

当我们需要把我们的服务器作为代理服务器时。

- 实现方式

可以用  Nginx  来实现正向代理,但是目前  Nginx  有一个需要注意的问题:不支持  HTTPS  *,如果想实现  HTTPS  的需求需要借助第三方模块  ngx_http_proxy_connect_module  ,配置  HTTPS  请阅读fs_Dong的文章:搭建Nginx正向代理服务


行为分析

- 应用场景

统计整个系统访问的浏览器信息、IP、地理位置、操作系统等信息作为项目上大数据展示的初始数据来源。

- 实现方式

通常搭配  反向代理  、 访问入口统一  的场景,使用  Nginx  的日志 及 地理位置库插件 来采集信息,然后由 eCloud平台 进行初步分析后供大俗韩剧平台采集和展示。主流发布的一键部署包已包含 地理位置库插件。


负载均衡

重头戏,无法拒绝的硬菜。

- 概述

 负载均衡 也是  Nginx  的一个常用功能,简而言之就是当存在 2台或2台以上的服务器时,根据规则随机将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡。而  Nginx  目前支持自带 3种负载均衡策略,还有 2种常用的第三方策略:

  -- RR(默认)

  每个请求按时间顺序注意分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。简易配置:

 1 upstream test {
 2     server localhost:8080;
 3     server localhost:8081;
 4 }
 5 server {
 6     listen       80; 
 7     server_name  localhost; 
 8     client_max_body_size 1024M;
 9  
10     location / {
11         proxy_pass http://test;
12         proxy_set_header Host $host:$server_port;
13     }
14 }

行2、3 - 负载均衡的核心代码,配置两台服务器,这里实际上是一台服务器的不同端口,而8081的服务器是不存在的,也就是说访问不到,但是我们访问 http://localhost 的时候也不会有问题,会默认跳转到 http://localhost:8080 ,具体是因为 Nginx 会自动判断服务器的状态,如果服务器不能访问(服务挂了)就不会跳转到这台服务器,所以避免了一台服务器挂了影响使用的情况。由于 Nginx 默认是 RR 策略,所以我们不需要其他更多的设置。

  -- 权重 weight

  必须实现 session 共享,否则导致用户 session 不同步,致使用户重新登陆。指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。例如

1 upstream test {
2     server localhost:8080 weight=9;  #请求的 90% 进入到8080服务器
3     server localhost:8081 weight=1;  #请求的 10% 进入到8081服务器
4 }

行2、3 - 访问10次种一般只会有 1此访问到8081,而其他 9次回访问到8080

  -- ip_hash:

  上面的 2种方式都存在一个缺陷,当下一个请求到达的时候请求可能分发到另外一个服务器,当我们的程序不是无状态(采用了 session 保存数据),这时候就会产出BUG,比如把登录信息保存在 session 种,那么跳转到另外一台服务器的时候就需要重新登录,所以很多时候我们需要一个用户之访问一个服务器,此时就需要用 ip_hash 替代了。

  ip_hash 的每个请求按 访问ip的 hash结果进行分配,这样每个访客固定访问一个后端服务器,可以解决 session 的问题。

1 upstream test {
2         ip_hash;
3         server localhost:8080;
4         server localhost:8081;
5 }

  -- fair (第三方)

  按后端服务器的响应时间来分配请求,优先分配响应时间最短的服务器。

1 upstream backend { 
2         fair; 
3         server localhost:8080;
4         server localhost:8081;
5 }

  -- url_hash (第三方)

  按 访问url的 hash结果来分配请求,使每个 url 定向到同一个后端服务器,后端服务器为缓存时比较有效。在 upstream 中加入 hash语句,server语句中不能写入 weight 等其他参数,hash_method 是使用的 hash 算法。

1 upstream backend { 
2         hash $request_uri; 
3         hash_method crc32; 
4         server localhost:8080;
5         server localhost:8081;
6 }

  -- 总结

  以上 5种负载均衡 各自适用不同情况下使用,所以可以根据实际情况选择哪种策略模式,但  fair  与  url_hash  需要安装第三方模块才能使用。

- 应用场景

主流分布式架构方案中,实现将请求按一定的策略分发至后端多台业务服务器,实现业务服务的高可用及高并发。

- 实现方式

使用  Nginx  的  反向代理  负载均衡  模块,根据配置的 url后缀规则 转发至  upstream  中个服务器列表,默认按  轮询策略  ,同时还支持  权重  和  ip_hash  ,有  redis  缓存登录  session  的方案中推荐使用  轮询策略  ,否则使用  ip_hash  策略实现同一ip 的用户请求固定至后端同一台服务器。


扩展 负载均衡

负载均衡(LB, Load Balance)是一种技术解决方案,用来在多个资源(一般是服务器)中分配负载,达到最优化资源使用,避免过载。

 资源,相当于每个服务实例的执行操作单元,负载均衡就是将大量的数据处理操作分摊到多个操作单元去执行,用来解决互联网分布式系统的大流量、高并发和高可用的问题。


扩展 高可用

高可用是  CAP 定理  ,也是分布式系统的基础,0分布式系统的3个指标:

  • Consistency(一致性)
  • Availability(可用性)
  • Partition tolerance(分区容错性)

 高可用  简称 HA,是系统一种特征或者指标,通常是指提供一定性能上的服务运行时间,高于平均正常时间段。反之消除系统服务不可用的时间。

衡量系统是否满足  高可用  ,就是当一台或多台服务器宕机的时候,系统整体和服务依然正常可用。举个例子,一些知名的网站保证 4 个 9 以上的可用性,也就是可用性超过 99.99%。那 0.01% 就是所谓故障时间的百分比。比如电商网站有赞,服务不可用会造成商家损失金钱和用户。那么在提高可用性基础上同时,对系统宕机和服务不可用会有补偿。

比如下单服务,可以使用带有负载均衡的多个下单服务实例,代替单一的下单服务实例,即使用冗余的方式来提高可靠性。

总而言之,负载均衡(Load Balance)是分布式系统架构设计中必须考虑的因素之一。一般通过负载均衡,冗余同一个服务实例的方式,解决分布式系统的大流量、高并发和高可用的问题。负载均衡核心关键:在于是否分配均匀。


扩展 常见的负载均衡案例

- 场景1

微服务架构中,网关路由到具体的服务实例 hello:

两个相同的服务实例 hello service ,一个接口 8000, 另一个接口 8082 ,通过 Kong 的负载均衡 LB 功能,让请求均匀的分发到两个 hello 服务实例。

Kong 的负载均衡策略算法很多:默认 weighted-round-robin 孙发,还有 consumer: consumer id 作为 hash 算法输入值等。

- 场景2 

微服务架构中, A 服务调用 B 服务的集群。通过了 Ribbon 客户端负载均衡组件:

  • 负载均衡策略算法并不高级,最简单的是随机选择和轮询

互联网分布式系统解决方案

 常见的互联网分布式系统架构分为几层,一般如下:

  • 客户端层:比如用户浏览器APP
  • 反向代理层:技术选型  Nginx  或者  F5 
  • Web层:前后端分离场景下,Web 端可以用  NodeJS  RN  Vue 
  • 业务服务层:用  Java  Go  ,一般互联网公司,技术方案选型就是  SC  或者  Spring Boot  Dubbo  服务化
  • 数据存储层:DB 选型  MySQL  ,Cache 选型  Redis  ,搜索选型  ES 

一般请求从客户端层业务服务层,层层访问都需要负载均衡。即每个上游调用下游多个业务方的时候,需要均匀调用。这样整体系统来看,就比较负载均衡。

- 第 1 层: 客户端 -> 反向代理层 的负载均衡

通过  DNS 轮询   实现, DNS  可以通过 A(Address,返回域名指向 IP 地址)设置多个 IP 地址。比如图例访问 bysocket.com 的  DNS  配置了 ip1 和 ip2 。为了反向代理层的高可用,至少回有两条 A 记录。这样冗余的两个 ip 对应的  nginx  服务实例,防止单点故障。

每次请求 bysocket.com 域名的时候,通过  DNS 轮询  ,返回对应的 ip 地址,每个 ip 对应的反向代理层的服务实例,也就是  nginx  的外网 ip。这样可以做到每一个反向代理层实例得到的请求分配是均衡的。

- 第 2 层: 反向代理层 -> Web层 的负载均衡

通过反向代理层的  负载均衡 模块  处理。比如  Nginx  有多种均衡方法:

  1. 请求轮询。以  RR 默认策略  请求按时间顺序,逐一分配到 Web 层服务,然后周而复始。如果 Web 层服务 down掉,自动剔除。

1 upstream web-server { 
2     server ip3; 
3     server ip4; 
4 } 

   2. ip_hash。按照 ip 的哈希值,确定路由到对应的 Web 层。只要用户的 ip 是均匀的,那么轻巧到 Web 层也是均匀的。

        还有个好处就是同一个 ip 的请求回分发到相同的 Web 层服务。这样每个用户固定访问一个 Web层服务,可以解决 session 问题。

1 upstream web-server { 
2     ip_hash; 
3     server ip3; 
4     server ip4; 
5 } 

  3. weight 权重。

  4. fair。

  5. url_hash 等。

- 第 3 层:Web 层 -> 业务服务层 的负载均衡

比如  Dubbo  是一个服务治理安敢,包括服务注册、服务降级、访问控制、动态配置路由规则、权重调节、负载均衡。其中一个特性就是智能负载均衡:内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。

为了避免单点故障和支持服务的横向扩容,一个服务通常回部署多个实例,即  Dubbo  集群部署。回将多个服务实例成为一个服务提供方,然后根据配置的随机负载均衡策略,在20个  Provider  中随机选择一个来调用,假设随机到了第 7 个  Provider  LoadBalance  组件从提供者地址列表中,使用均衡策略,选择一个提供者进行调用,如果调用失败,再选另一台调用。

 Dubbo  内置了4种负载均衡策略:

  • RandomLoadBalance:随机负载均衡。随机的选择一个,是  Dubbo  的默认负载均衡策略。
  • RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。
  • LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机。活跃数指调用前后计数差,使慢的  Provider  收到更少请求,因为越慢的  Provider  的调用前后计数差会越大。
  • ConsistenHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。

同样,因为业务的需要也可以实现自己的负载均衡策略。

- 第 4 层:业务服务层 -> 数据存储层 的负载均衡

数据存储层的负载均衡,一般通过  DBProxy  实现。比如  MySQL  分库分表。

当单库或者单表访问太大,数据量太大的情况下,需要进行垂直拆分和水平拆分两个维度。比如水平切分规则:

  • Range、时间
  • hash 取模。订单根据店铺ID 等

但伴随着这块的负载会出现下面的问题需要解决:

  • 分布式事务
  • 跨库 join 等

现状分库分表的产品方案很多: 当当  sharding-jdbc  、阿里的  Cobar  等。

- 小结

对外来看,负载均衡是一个系统或软件的整体。对内看来,层层上下游调用。只要存在调用,就需要考虑负载均衡这个因素。所以负载均衡(Load Balance)使分布式系统架构设计种必须考虑的因素之一。考虑主要是如何让下游接收到的请求使均匀分布的:

  • 第 1 层:客户端层 -> 反向代理层 的负载均衡。通过  DNS 轮询  。
  • 第 2 层:反向代理层 -> Web 层 的负载均衡。通过  Nginx  的负载均衡模块。
  • 第 3 层:Web 层 -> 业务服务层 的负载均衡。通过服务治理框架的负载均衡模块。
  • 第 4 层:业务服务层 -> 数据存储层 的负载均衡。通过数据的水平分布,数据均匀了,理论上请求也会均匀。比如通过买家ID 分片类似。

- END -


参考文献:

推荐阅读