Nginx原理

目录

Nginx源码分析

One loop pre process:类多 Reactor 多进程

Nginx多进程模型的好处

 Nginx进程事件分发器:解决Worker间协同

Nginx解决惊群和进程和负载均衡处理的要点

Accept锁结构

ngx_trylock_accept_mutex 获取accept锁


nginx服务器的网络模块设计,基于进程设计,采用多个Reactors充当I/O进程和工作进程,通过一把 accept锁完美解决多个Reactors的“惊群现象”,并通过双向链表管理业务,并通过计算其偏移量来得到数据结构的指针地址,完成数据结构与业务层的解耦。同时使用零拷贝技术,利用sendfile来传输小文件,利用直接IO+异步IO来传输大文件。

One loop pre process:类多 Reactor 多进程

master进程

<1> 管理worker进程
<2> 接收外界信号,向各worker发送信号(./nginx -s reload 进行热部署)
<3> 监控worker的工作状态,worker退出后自动启动新的worker进程
<4> 创建了 socket、然后 bind 并 listen

问:Master进程与标准的多 Reactor 多进程有些差异。
具体差异表现在主进程中仅仅用来初始化 socket,并没有创建 mainReactor 来 accept 连接,而是由子进程的 Reactor 来 accept 连接。

worker进程
<1> 由master进程fork而来。由于每个进程都一样,所以每个 worker 都有 Master 创建出来的         listenfd。所以每个worker进程都能监听到新连接加入的事件。
<5> worker进程个数是可配置的,一般与机器cpu核心数量一致。

work_connection(worker的连接数)

问:当发送一个请求的时候,占用worker的多少个连接数?

2个(访问静态资源,一来一回)或者4个(访问动态资源,一来一回+和 tomcat 的一来一回)。

最大并发量

问:nginx有1个master,有4个worker,每个worker的最大连接数为1024,那么Nginx支持的最大并发为多少?

访问静态资源: work_connections * worker_processes /2

访问动态资源::work_connections * worker_processes /4

Nginx多进程模型的好处

<1> 每个worker进程都是独立处理连接数据,处理过程中不需要锁,节省了开销。
<2> worker进程互相独立,一个出现异常其他的worker可以继续服务
<3> 多进程模型为reload热部署提供了良好的支撑

 Nginx进程事件分发器:解决Worker间协同

此方法为进程实现的核心函数。主要作用:事件分发;惊群处理;简单的负载均衡

负载均衡:
当事件配置初始化的时候,会设置一个全局变量:

ngx_accept_disabled = ngx_cycle->​​​connection_n / 8 - ngx_cycle->free_connection_n;

当ngx_accept_disabled为正数的时候,connection达到连接总数的7/8的时候就不再处理新的连接accept事件,只处理当前连接的read事件

惊群处理:
<1> 通过ngx_trylock_accept_mutex争抢文件锁,拿到文件锁的,才可以处理accept事件。
<2> ngx_accept_mutex_held是拿到锁的一个标志,当拿到锁了,flags会被设置成NGX_POST_EVENTS,这个标志会在事件处理函数ngx_process_events中将所有事件(accept和read)放入对应的ngx_posted_accept_events和ngx_posted_events队列中进行延后处理。
<3> 当没有拿到锁,调用事件处理函数ngx_process_events的时候,可以明确都是read的事件,所以可以直接调用事件ev->handler方法回调处理
<4> 拿到锁的进程,接下来会优先处理ngx_posted_accept_events队列上的accept事件,处理函数:ngx_event_process_posted
<5> 处理完accept事件后,就将文件锁释放
<6> 接下来处理ngx_posted_events队列上的read事件,处理函数:ngx_event_process_posted

Nginx解决惊群和进程和负载均衡处理的要点

<1> Nginx的N个进程会争抢文件锁,当只有拿到文件锁的进程,才能处理accept的事件
<2> 没有拿到文件锁的进程,只能处理当前连接对象的read事件
<3> 当单个进程总的connection连接数达到总数的7/8的时候,就不会再接收新的accpet事件。
<4> 如果拿到锁的进程能很快处理完accpet,而没拿到锁的一直在等待(等待时延:ngx_accept_mutex_delay),容易造成进程忙的很忙,空的很空,类似于自旋锁

Accept锁结构

typedef struct {
#if (NGX_HAVE_ATOMIC_OPS)
    ngx_atomic_t *lock;
#if (NGX_HAVE_POSIX_SEM)
    ngx_atomic_t *wait;
    ngx_uint_t   semaphore;
    sem_t        sem;
#endif
#else
    ngx_fd_t fd;
    u_char   *name;
#endif
    ngx_uint_t  spin; //衰变值
} ngx_shmtx_t;

ngx_trylock_accept_mutex 获取accept锁

/**
 * 获取accept锁
 */
ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle) {
	/**
	 * 拿到锁
	 */
	if (ngx_shmtx_trylock(&ngx_accept_mutex)) {
 
		ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
				"accept mutex locked");
 
		/* 多次进来,判断是否已经拿到锁 */
		if (ngx_accept_mutex_held && ngx_accept_events == 0) {
			return NGX_OK;
		}
 
		/* 调用ngx_enable_accept_events,开启监听accpet事件*/
		if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
			ngx_shmtx_unlock(&ngx_accept_mutex);
			return NGX_ERROR;
		}
 
		ngx_accept_events = 0;
		ngx_accept_mutex_held = 1;
 
		return NGX_OK;
	}
 
	ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
			"accept mutex lock failed: %ui", ngx_accept_mutex_held);
 
	/**
	 * 没有拿到锁,但是ngx_accept_mutex_held=1
	 */
	if (ngx_accept_mutex_held) {
		/* 没有拿到锁,调用ngx_disable_accept_events,将accpet事件删除 */
		if (ngx_disable_accept_events(cycle, 0) == NGX_ERROR) {
			return NGX_ERROR;
		}
 
		ngx_accept_mutex_held = 0;
	}
 
	return NGX_OK;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值