nginx原理概述

发布于:2022-12-13 ⋅ 阅读:(746) ⋅ 点赞:(0)

nginx

nginx是什么?

Nginx 是一个高性能的Web服务器和反向代理服务器,占用内存少、启动速度快、并发能力强,在互联网项目中广泛应用。

nginx的作用

nginx主要的作用可以归纳为以下三点:

动静分离反向代理负载均衡

动静分离

Nginx将将接收到的请求分为动态请求静态请求,静态请求直接从 nginx 服务器所设定的根目录路径去取对应的资源,动态请求转发给真实的后台(即应用服务器)去处理。

nginx动静分离的作用

api接口服务化:动静分离之后,后端应用更为服务化,只需要通过提供api接口即可,可以为多个功能模块甚至是多个平台的功能使用,可以有效的节省后端人力,更便于功能维护。

前后端开发并行:前后端只需要关心接口协议即可,各自的开发相互不干扰,并行开发,并行自测,可以有效的提高开发时间,也可以有些的减少联调时间。

减轻后端服务器压力,提高静态资源访问速度;后端不用再将模板渲染为html返回给用户端,且静态服务器可以采用更为专业的技术提高静态资源的访问速度。

nginx默认配置

server {  
        # 当nginx接到请求后,会匹配其配置中的service模块
        # 匹配方法就是将请求携带的host和port去跟配置中的server_name和listen相匹配
        listen       8080;        
        server_name  localhost; # 定义当前虚拟主机(站点)匹配请求的主机名

        location / {
            root   html; # Nginx默认值
            # 设定Nginx服务器返回的文档名
            index  index.html index.htm; # 先找根目录下的index.html,如果没有再找index.htm
        }
}

nginx动静分离配置

server {  
        listen       8080;        
        server_name  localhost;

        location / {
            root   html; # Nginx默认值
            index  index.html index.htm;
        }
        
        # 静态化配置,所有静态请求都转发给 nginx 处理,存放目录为 my-project
        location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|js|css)$ {
            root /usr/local/var/www/my-project; # 静态请求所代理到的根目录
        }
        
        # 动态请求匹配到path为'node'的就转发到8002端口处理
        location /node/ {  
            proxy_pass http://localhost:8002; # 充当服务代理
        }
}

反向代理

反向代理区别于正向代理,正向代理是给客户端做代理,反向代理是给服务器做代理。

反向代理,其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,在返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器 IP 地址。

反向代理的作用

  1. 保障应用服务器的安全(增加一层代理,可以屏蔽危险攻击,更方便的控制权限)
  2. 实现负载均衡
  3. 实现跨域

nginx反向代理配置

server {  
        listen       8080;        
        server_name  localhost;

        location / {
            root   html; # Nginx默认值
            index  index.html index.htm;
        }
        
        proxy_pass http://localhost:8000; # 反向代理配置,请求会被转发到8000端口
}

负载均衡

负载均衡主要针对服务器集群。

在服务器集群中,Nginx 可以将接收到的客户端请求“均匀地”(严格讲并不一定均匀,可以调整权重)分配到这个集群中所有的服务器上。这个就叫做负载均衡

负载均衡的作用

  1. 分摊服务器集群压力
  2. 保证客户端访问的稳定性
  3. 对服务器进行健康检查**(是否需要配置?),检查是否有处于异常状态的服务器(如何保证重启平滑性?)**

负载均衡的策略

1.轮询策略

轮询负载策略是指每次将请求按顺序轮流发送至相应的服务器上。

http {
  upstream myapp1{
    # 轮询的服务器1,2,3
    server server1.com;
    server server2.com;
    server server3.com;
  }
    server {
        listen       8080;
        location / {
            proxy_pass http://myapp1;
        }
    }
}

Nginx默认选择轮询策略,conf中不需要额外进行配置,就可以在三台服务器中进行轮询。

2.最少连接数

此策略是指每次将请求分发到当前连接数最少的服务器上,也就是 Nginx 会将请求试图转发给相对空闲的服务器以实现负载平衡。

http {
  upstream myapp1{
    least_conn; # 设置为最少连接策略
    server server1.com;
    server server2.com;
    server server3.com;
  }
    server {
        listen       8080; 
        location / {
            proxy_pass http://myapp1;
        }
    }
}

3.加权轮询

根据预设的权重分发请求。比如权重3,2,2时,就每7次请求中给1号服务器分发3次,给其他两台服务器分别分发2次。

轮询和加权轮询适用于大多数场合,是最普遍的负载均衡配置。

http {
  upstream myapp1{
    server server1.com weight=3;
    server server2.com weight=2;
    server server3.com weight=2;
  }
    server {
        listen       8080;
        location / {
            proxy_pass http://myapp1;
        }
    }
}

**4.ip-hash **

以上三种负载均衡的配置策略都不能保证将每个客户端的请求固定的分配到一台服务器上,而又Nginx不支持session的保持,假如用户的登录信息是保存在单台服务器上的(比如session),如果不能将每个客户端的请求固定的分配到一台服务器上,就会导致用户的登录信息丢失。因此用户在每次请求服务器时都需要进行登录验证,这样显然是不合理的,也是不能被用户所接受的,所以在特殊情况下我们就需要使用 ip-hash 的负载均衡策略。

http {
  upstream myapp1{
    ip_hash; # 设置为ip_hash
    server server1.com;
    server server2.com;
    server server3.com;
  }
    server {
        listen       8080; 
        location / {
            proxy_pass http://myapp1;
        }
    }
}

5.一致性哈希

一致性哈希主要解决的是需要频繁添加**(如何保证添加的情况下能打到同一台服务器上?)** 或者移除节点的情况。在普通的ip_hash中,只要集群的数量N发生变化,之前的所有Hash映射就会全部失效。如果集群中的每个机器提供的服务没有差别,倒不会产生什么影响,但对于分布式缓存这样的系统而言,映射全部失效就意味着之前的缓存全部失效,后果将会是灾难性的。一致性Hash通过构建环状的Hash空间代替线性Hash空间的方法解决了这个问题。

节点和请求都被映射到环上,请求会顺着环找到最近的节点。

如果每个节点在环上只有一个节点,那么可以想象,当某一节点从环中消失时,它原本所负责的任务将全部交由顺时针方向的下一个节点处理。例如,当节点1退出时,它原本所负责的缓存将全部交给节点2处理。这就意味着节点2的访问压力会瞬间增大。设想一下,如果节点2因为压力过大而崩溃,那么更大的压力又会向节点3压过去,最终服务压力就像滚雪球一样越滚越大,最终导致雪崩。

因而,可以采用虚拟节点的策略,在环上设置许多虚拟节点,来分摊节点消失带来的负载压力。

Nginx高并发原理

异步非阻塞

Nginx是基于NIO事件驱动的,是异步非阻塞的。

同步:当一个同步调用发出去后,调用者要一直等待调用结果的通知后,才能进行后续的执行。
异步:当一个异步调用发出去后,调用者不能立即得到调用结果的返回。

阻塞:阻塞调用在发出去后,在消息返回之前,当前进/线程会被挂起,直到有消息返回,当前进/线程才会被激活。
非阻塞:非阻塞调用在发出去后,不会阻塞当前进/线程,而会立即返回。

  1. 同步阻塞:小明收到信息后,啥都不干,等快递;
  2. 同步非阻塞:小明收到信息后,边刷微博,边等着取快递;
  3. 异步阻塞:小明收到信息后,啥都不干,一直等着快递员通知他取快递;
  4. 异步非阻塞:小明收到信息后,边刷着微博,边等快递员通知他取快递。

小明:应用程序;快递员:系统内核;取快递:IO操作。

传统的服务器采用的就是同步阻塞的多进程模型。一个server采用一个进程负责一个request的方式,一个进程负责一个request,直到会话结束。进程数就是并发数,而操作系统支持的进程数是有限的,且进程数越多,调度的开销也越大,因此无法面对高并发。而Nginx采用了异步非阻塞的方式工作,多个IO可以复用同一个进程,因而大大提高了效率。

IO复用

I/O复用模型会用到select、poll、epoll函数,在这种模型中,这时候并不是进程直接发起资源请求的系统调用去请求资源,进程不会被“全程阻塞”,进程是调用select/poll/epoll函数。进程不是被阻塞在真正IO上了,而是阻塞在select或者poll上了。Select或者poll帮助用户进程去轮询那些IO操作是否完成。

从下图可以看到,select()函数真正用在IO操作上的时间并不多,很多时间都是用在等待系统内核返回结果,相当于select()函数代替进程进入阻塞状态。

Nginx与epoll

但是随着并发数的增加,select()函数遍历数组的时间太长,从而影响服务的效率。为了增大并发数,Nginx采用的是epoll函数。epoll可以理解为event poll,不同于忙轮询和无差别轮询,当连接有I/O流事件产生的时候,epoll就会去告诉进程哪个连接有I/O流事件产生,然后进程就去处理这个事件。此时我们对这些流的操作都是有意义的。

image-20220712153348616

不同函数的区别:

select:基于数组实现,最多支持1024路IO。

poll:基于链表实现IO管理无限制,可以超过1024,没有IO量的限制。

epoll:linux 2.6以上才支持,是基于红黑树**(为什么用红黑树?)**+链表,拥有更高的IO管理性能。

select poll epoll
查询方式 遍历 遍历 回调
底层实现 数组 链表 红黑树+双向链表
IO效率 O(n) O(n) O(1)
最大连接数 1024/2048 无上限 无上限

参考资料:

英文文档:nginx.org/en/docs/

中文文档:www.nginx.cn/doc/


网站公告

今日签到

点亮在社区的每一天
去签到