本文详细介绍了如何处理nginx意外问题。原文根据示例代码非常详细,对大家的学习培训或者工作都有一定的参考价值。有必要的朋友陪我去了解一下。
要解决nginx的组惊喜问题,首先要明白在nginx启动的整个过程中,主进程会监听环境变量中的每个特定端口,然后主进程会启动fork()建立每个子进程。根据进程的原理,子进程会继承继父进程的所有运行内存数据信息以及它所监听的端口,换句话说,工作者进程在启动后也会监听每个端口。突击组是指当手机客户端有新的连接创建请求时,会打开每个工作进程的连接创建事件,但只有一个工作进程能正常解决该事件,而其他工作进程会发现该事件已经无效,然后循环系统再次等待。这种因为一个事件而“震惊”所有工作进程的情况,就是群体性休克问题。显然,如果启动所有的工作进程,将会耗费大量的资源。本文将重点讨论nginx如何处理突击群问题。
1。处理方法
上一篇文章大家都说了,每建立一个worker进程,就会启用ngx_worker_process_init()来重置当前的worker进程。整个过程中有一个非常关键的进程,就是每个worker进程都会启用epoll_create()为自己建立一个唯一的epoll句柄。对于每个必须监控的端口,都有一个与之匹配的文件描述符。只有当工作进程根据epoll_ctl()方法将文件描述符添加到当前进程的epoll句柄中,并监测到接受事件时,才会被移动客户端的连接创建事件打开,然后解决事件。从这里也可以看出,如果worker进程没有将它必须监听的端口匹配的文件描述符添加到进程的epoll句柄中,就无法打开匹配的事件。根据这一基本原理,nginx应用共享资源锁来 *** 纵当前进程是否具有管理权限,并将必须被监控的端口添加到当前进程的epoll句柄中。换句话说,只有获得锁的进程将监控整个目标端口。根据这种方法,每次事件发生时,只会启动一个工作进程。下图显示了工作进程中循环系统的平面图:
关于这里图中的步骤,必须指出的是,每个工作进程在进入循环系统后,都会试图获取共享资源锁。如果未能获得,它将从当前进程的epoll句柄中清除被监控端口的文件描述符(即使它不存在,也会被清除)。这样做的关键目的是避免丢失手机客户端的连接事件,即使这样做很可能会导致少量的混乱,但并不严重。想象一下,根据基本理论,如果在当前进程释放锁的时候,被监控端口的文件描述符被从epoll句柄中清除,那么在下一个工作进程获得锁之前,没有epoll句柄来监控这期间每个端口的文件描述符,就会导致事件的丢失。相反,如果根据图,只有在没有成功获取锁的情况下,才清除被监控的文件描述符,因为没有成功获取锁,所以说明一定有一个进程已经监控了这个文件描述符,所以此时清除是安全的。但是,会导致的一个问题是,根据图,当前进程会在实现循环系统时释放锁,然后解决其他事件。注意被监控的文件描述符在整个过程中是不释放的。此时,如果另一个进程获得锁并监听文件描述符,那么此时会有两个进程监听文件描述符。因此,如果移动电话客户端生成连接创建事件,那么将启动两个工作进程。这个问题可以容忍有两个主要原因:
2。源代码解释
工作进程原始事件的关键方式是在ngx_process_events_and_timers()模式下。让我们讨论一下这个模式是如何处理所有步骤的。下面是这种模式的源代码:
voidngx_process_events_and_timers(ngx_cycle_t*cycle){ ngx_uint_tflags; ngx_msec_ttimer,delta; if(ngx_trylock_accept_mutex(cycle)==NGX_ERROR){ return; } //这儿刚开始解决事件,针对kqueue实体模型,其偏向的是ngx_kqueue_process_events()方式, //而针对epoll实体模型,其偏向的是ngx_epoll_process_events()方式 //这一方式的关键功效是,在相匹配的事件实体模型中获得事件目录,随后将事件加上到ngx_posted_accept_events //序列或是ngx_posted_events序列中 (void)ngx_process_events(cycle,timer,flags); //这儿刚开始解决accept事件,将其交给ngx_event_accept.c的ngx_event_accept()方式解决; ngx_event_process_posted(cycle,&ngx_posted_accept_events); //刚开始释放出来锁 if(ngx_accept_mutex_held){ ngx_shmtx_unlock(&ngx_accept_mutex); } //假如不用在事件序列中开展解决,则立即解决该事件 //针对事件的解决,如果是accept事件,则将其交给ngx_event_accept.c的ngx_event_accept()方式解决; //如果是读事件,则将其交给ngx_http_request.c的ngx_http_wait_request_handler()方式解决; //针对解决进行的事件,最终会交给ngx_http_request.c的ngx_http_keepalive_handler()方式解决。 //这儿刚开始解决除accept事件外的别的事件 ngx_event_process_posted(cycle,&ngx_posted_events); }在上面的代码中,省略了大部分检查工作,只留下了帧代码。首先,工作进程将启用ngx_trylock_accept_mutex()来获取锁,其中,如果获取了锁,它将监视与每个端口匹配的文件描述符。然后,将启用ngx_process_events()方法来解析epoll句柄中监控的事件。然后,共享资源锁将被释放,最后,连接的移动客户端的读写能力事件将被解决。让我们来看看ngx_trylock_accept_mutex()方法是如何获得共享资源锁的:
ngx_int_tngx_trylock_accept_mutex(ngx_cycle_t*cycle){ //试着应用CAS优化算法获得共享资源锁 if(ngx_shmtx_trylock(&ngx_accept_mutex)){ //ngx_accept_mutex_held为1表明当今进程早已获得来到锁 if(ngx_accept_mutex_held&&ngx_accept_events==0){ returnNGX_OK; } //这儿主要是将当今联接的文件描述符申请注册到相匹配事件的序列中,例如kqueue实体模型的change_list数组 //nginx在开启每个worker进程的情况下,默认设置状况下,worker进程是会承继master进程所监听的socket句柄的, //这就造成一个难题,便是当某一端口有手机客户端事件时,就会把监听该端口的进程都给唤起, //可是只有一个worker进程可以取得成功解决该事件,而别的的进程被唤起以后发觉事件早已到期, //因此会再次进到等候情况,这类状况称之为"惊群"状况。 //nginx处理惊群状况的方法一方面是根据这儿的共享资源锁的方法,即仅有获得到锁的worker进程才可以解决 //手机客户端事件,但事实上,worker进程是根据在获得锁的全过程中,为当今worker进程再次加上每个端口的监听事件, //而别的worker进程则不容易监听。换句话说同一时间只有一个worker进程会监听每个端口, //那样就防止了"惊群"难题。 //这儿的ngx_enable_accept_events()方式便是为当今进程再次加上每个端口的监听事件的。 if(ngx_enable_accept_events(cycle)==NGX_ERROR){ ngx_shmtx_unlock(&ngx_accept_mutex); returnNGX_ERROR; } //标示当今早已取得成功获得来到锁 ngx_accept_events=0; ngx_accept_mutex_held=1; returnNGX_OK; } //前边获得锁失败了,因此这儿必须重设ngx_accept_mutex_held的情况,而且将当今联接的事件给消除掉 if(ngx_accept_mutex_held){ //假如当今进程的ngx_accept_mutex_held为1,则将其重设为0,而且将当今进程在每个端口上的监听 //事件给删掉掉 if(ngx_disable_accept_events(cycle,0)==NGX_ERROR){ returnNGX_ERROR; } ngx_accept_mutex_held=0; } returnNGX_OK; }在上面的编码中,基本上完成了三件关键的事情:
3。摘要
本文首先解释了死机情况产生的原因,然后详细介绍了nginx如何解决死机问题,最后从源代码的角度说明了nginx解决死机问题的方法。
文章里的内容就这些了。期待对大家的学习和培训有所帮助,也期待大家的应用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)