四、nginx 架构简述
nginx的作用
- 反向代理:将多台服务器代理成一台服务器
- 负载均衡:将多个请求均匀的分配到多台服务器上,减轻每台服务器的压力,提高服务的吞吐量
- 动静分离:nginx可以用作静态文件的缓存服务器,提高访问速度.
4.1、Nginx架构图
4.2、两种进程
4.2.1、master进程
- 主进程
- 1个
- 管理worker进程
- 加载配置
- 启动工作进程
- 非停升级
- 接受信号,将信号分发给worker进程
- 监听worker进程工作状态,当worker进程退出时(非正常),启动新的worker进程。
4.2.2、worker进程
- 工作进程
- 多个
- 处理网络请求及响应
- worker进程的数量一般要根据cpu的数量而定
- 多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的 。
- 一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。
4.2.3、master接收到重新加载的信号会怎么处理(./nginx -s reload)?
- master会重新加载配置文件,然后启动新的进程,使用的新的worker进程来接受请求,并告诉老的worker进程他们可以退休了,老的worker进程将不会接受新的,老的worker进程处理完手中正在处理的请求就会退出。
4.2.4、worker进程是如何处理用户的请求呢?
- 首先,master会根据配置文件生成一个监听相应端口的socket
- 然后,再创建出多个worker进程,这样每个worker就可以接受从socket过来的消息(其实这个时候应该是每一个worker都有一个socket,只是这些socket监听的地址是一样的)。
- 当一个连接过来的时候,每一个worker都能接收到通知,但是只有一个worker能和这个连接建立关系,其他的worker都会连接失败,这就是所谓的惊群现在现象,为了解决这个问题,nginx提供一个共享锁accept_mutex,有了这个共享锁后,就会只有一个worker去接收这个连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接。
4.3、为什么几个worker进程却能支撑上万甚至上十万的并发呢?
- Nginx设计的时候是基于非阻塞式的方式,能做到非阻塞是因为它的线程模型是基于Linux里面的epoll/select模型,这个也类似于我们java中nio的多路复用选择器模型。
- 事件驱动加上异步非阻塞的io模型,可以说是nginx得以获得高并发、高性能的关键因素。
4.4、模块化设计
-
Nginx有一个特点就是模块化设计。
-
nginx的worker,包括核心和功能性模块
-
核心模块
- 负责维持一个运行循环(run-loop),执行网络请求处理的不同阶段的模块功能,如网络读写、存储读写、内容传输、外出过滤,以及将请求发往上游服务器等。
-
功能性模块
- 我们可以根据需要对功能模块进行适当的选择和修改,编译成具有特定功能的服务器。
-
核心模块图
4.5、正向代理
由于防火墙的原因,我们并不能直接访问谷歌,那么我们可以借助VPN来实现,这就是一个简单的正向代理的例子。
- 正向代理"代理"的是客户端
- 客户端知道要访问的服务器地址
- 服务器是不知道客户端是通过VPN访问的,只知道来自哪个代理
01、优点
- 访问原来无法访问的资源
- 用作缓存,加速访问速度
- 对客户端访问授权,上网进行认证
- 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
4.6、反向代理
当我们在外网访问百度的时候,其实会进行一个转发,代理到内网去,这就是所谓的反向代理
- 反向代理"代理"的是服务器端
- 客户端不知道要访问的服务器地址,只知道代理服务器地址。
- 服务器知道具体的客户端地址。
01、优点
- 保护内网安全
- 负载均衡
- 缓存,减少服务器的压力
4.7、nginx的事件驱动机制
-
他不会为每个消费事件创建一个进程或线程,这样就不会产生由于进程间频繁切换占用cpu而产生的瓶颈,
-
nginx不会让事件阻塞,即采用无阻塞事件驱动模型,这样就不会因为事件阻塞使进程睡眠而造成的资源浪费.
-
nginx将一个请求划分成多个阶段异步处理,每个阶段仅仅完成一个请求中的一部分,当本阶段任务完成后进入下一阶段。
-
事件发生源产生事件->事件收集器来收集分发事件(选择自己感兴趣的)->消费事件。
4.8、epoll库
-
epoll库是Nginx服务器支持的高性能事件驱动库之一。
-
它是公认的最好的事件驱动模型。
-
和poll库及select库有很大的区别。
- poll和select都是创建一个待处理事件列表,然后把这个列表发给内核,返回的时候,再去轮询检查这个列表。以判断这个事件是否发生。在描述符太多的情况下,就会明显效率低下了。
- epoll它把事件描述符列表的管理交给内核复制。一旦有某个事件发生,内核将发生事件的事件描述符交给Nginx的进程,而不是将整个事件描述符列表交给进程,让进程去轮询具体是哪个描述符。epoll()避免了轮询整个事件描述符列表。所以显得更高效。
-
epoll库的基本步骤:
- epoll库通过相关调用通知内核创建一个有N个描述符的事件列表。
- 给这个事件列表设置自己关心的事件。并把它添加到内核中。在具体的代码中还可以实现对相关调用的事件描述符列表进行修改和删除。
- 一旦设置完成就一直等待内核通知事件发生了,某一事件发生后,内核就将发生事件的描述符给epoll库,epoll库去处理事件。