Nginx网络epoll多进程系列:什么是惊群?怎么解决

简介: Nginx网络epoll多进程系列:什么是惊群?怎么解决

http://blog.csdn.net/tycoon1988/article/details/43083257







多方考量,最后选择参考lighttpd的Watcher/Workers模型,实现了我需要的那款多进程epoll程序,核心流程如下:  

主 进程先监听端口, listen_fd = socket(...); ,setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR,...),setnonblocking(listen_fd),listen(listen_fd,...)。

开始fork(),到达子进程数上限(建议根据服务器实际的CPU核数来配置)后,主进程变成一个Watcher,只做子进程维护和信号处理等全局性工作。

每 一个子进程(Worker)中,都创建属于自己的epoll,epoll_fd = epoll_create(...);,接着将listen_fd加入epoll_fd中,然后进入大循环,epoll_wait()等待并处理事件。千 万注意, epoll_create()这一步一定要在fork()之后。

大胆设想(未实现):每个Worker进程采用多线程方式来提高大循环的socket fd处理速度,必要时考虑加入互斥锁来做同步,但


面,以单进程和多进程处理一个http请求为例,分析一下事件处理的流程。我用nginx里面已有的ngx_log_debugX()来插入事件处理的主要函数ngx_epoll_process_events()和ngx_event_process_posted()。在编译的时候,需要加上"--with-debug"参数。并指定nginx.conf里面的"error_log   logs/debug.log  debug_core | debug_event | debug_http;"。重新启动nginx。


单进程(work_processes 1):

1. 在初始化即ngx_worker_process_init()中调用两次ngx_epoll_add_event()。第一次是在ngx_event_process_init()里面,即给每个监听的端口(在我的例子里只监听80端口)添加一个NGX_READ_EVENT事件;第二次是ngx_add_channel_event(),即给进程间通行的socketpair添加NGX_READ_EVENT事件。

2. 不断调用ngx_epoll_process_events()函数,探测监听的事件是否发生。如果此时有一个http请求进来,就会触发epoll的事件。由于之前每个监听的端口已经设置handler是ngx_event_accept(),这样,就会在ngx_epoll_process_events()里面调用rev->handler(rev),即调用ngx_event_accept()。在这个函数里,accept()被调用,即接收请求并为其分配一个新的连接,初始化这个新连接,并调用listening socket的handler,即ls->handler(c)。因为ls->handler在http_block()(读取配置之后)里面已经设置了(ls->handler = ngx_http_init_connection;),那么就会调用ngx_http_init_connection()。而在这个函数里,又会添加一个读事件,并设置其处理钩子是ngx_http_init_request()。

3. epoll触发新的事件调用ngx_http_init_request(),并继续http请求处理的每一个环节。(如process request line,process headers,各个phase等)

4. 最后client关闭了连接(我用的是Linux下的curl)。调用了ngx_http_finalize_request() => ngx_http_finalize_connection() => ngx_http_set_keepalive()。ngx_http_set_keepalive()函数设置事件的处理函数是ngx_http_keepalive_handler(),并调用ngx_post_event()把它添加到ngx_posted_events队列里。然后ngx_event_process_posted()函数就会一一处理并删除队列里所有的事件。在ngx_http_keepalive_handler()函数里,调用ngx_http_close_connection() => ngx_close_connection() => ngx_del_conn(c,NGX_CLOSE_EVENT)。ngx_del_conn()即ngx_epoll_del_connection(),即把这个处理请求的connection从epoll监听的事件列表中删除。



多进程(我设置了work_processes 2):和单进程不同,单进程设置epoll timer为-1,即没有事件就一直阻塞在那里,直到监听的端口收到请求。而多进程则不同,每个进程会设置一个epoll_wait()的timeout,去轮番尝试获取在监听端口接受请求的权利,如果没有事件就去处理其它的事件,如果获得了就阻塞(*直到有任意事件发生)

1. 在ngx_event_process_init()里面,只会调用ngx_add_channel_event()给进程间通信的socketpair添加事件,而不给http监听的端口添加事件(为了保证不会有多个工作进程来同时接受请求)。而每个进程被fork()之后,父进程(master process)都会调用ngx_pass_open_channel() => ngx_write_channel()  => sendmsg()来通知所有已经存在的进程(这会触发接收方的事件,调用ngx_channel_handler()函数)

2. 在ngx_process_events_and_timers()里,用一个锁来同步所有的进程ngx_trylock_accept_mutex(),并只有一个进程能够得到ngx_accept_mutex这个锁。得到这个锁的进程会调用ngx_enable_accept_events()添加一个监听端口的事件。

3. 在ngx_epoll_process_events()里,调用了ngx_locked_post_event()添加了一个读事件到accept queue(即ngx_posted_accept_events),然后在ngx_event_process_posted()里面处理,即调用ngx_event_accept(),并添加一个读事件(后面和单进程是一样的)。在处理完ngx_posted_accept_events队列里面的所有accept事件之后,ngx_accept_mutex这个锁也会被释放,即把接受请求的权利让给其它的进程。


*在多进程的模式下,每当有新的子进程启动的时候,父进程(master process)都会向其余所有进程的socketpair channel广播新的子进程的channel。这样,就会导致之前获取监听端口权限(即ngx_accept_mutex)的进程触发epoll事件,从而释放ngx_accept_mutex,虽然这个是发生在初始化阶段(之后子进程间一般不通信),一般不会产生两个或多个进程同时在epoll添加监听端口事件的情况。但是在理论上,这样的设计可能会导致系统的bug(比如有人通过给子进程发送信号的办法来实现一些特殊的功能时,就有可能让其中一个进程放弃ngx_accept_mutex,而另外某一个进程在之后先于它再次获取到ngx_accept_mutex)。


































相关文章
|
20天前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
20天前
|
负载均衡 算法 应用服务中间件
面试题:Nginx有哪些负载均衡算法?Nginx位于七层网络结构中的哪一层?
字节跳动面试题:Nginx有哪些负载均衡算法?Nginx位于七层网络结构中的哪一层?
58 0
|
20天前
|
缓存 NoSQL 应用服务中间件
2.2.2 redis,memcached,nginx网络组件
2.2.2 redis,memcached,nginx网络组件
|
20天前
|
NoSQL Java 应用服务中间件
4.网络设计与redis、memcached、nginx组件(二)
4.网络设计与redis、memcached、nginx组件(二)
31 0
|
20天前
|
存储 NoSQL 应用服务中间件
4.网络设计与redis、memcached、nginx组件(一)
4.网络设计与redis、memcached、nginx组件(一)
80 0
|
20天前
|
API
网络编程与select/poll/epoll服务器的实现(1)
什么是网络编程?     本部分主要是介绍socket网络编程的基本API——并展示一个服务器与客户端连接的具体流程是如何的实现一个一对一的网络服务器程序
40 0
|
20天前
|
Linux
网络编程之信号(处理僵尸进程的终极办法)之初识信号捕捉器
接着我们之前的管道所提出来的问题() 在创建子进程之后,子进程究竟何时终止????调用waitpid函数后还要无休止的等待子进程终止吗???”,这显然会是一个问题。因为父进程往往与子进程一样繁忙,因此我们不能只调用waitpid函数来等待子进程终止。那么我们应该怎么办呢??? 信号闪亮登场!!!!!
34 0
|
20天前
|
应用服务中间件 Linux 网络安全
centos7 下离线安装gcc g++ nginx,并配置nginx进行网络流转发
centos7 下离线安装gcc g++ nginx,并配置nginx进行网络流转发
135 0
|
20天前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
70 0
|
20天前
|
安全 算法 网络安全
构筑网络长城:网络安全漏洞解析与防御策略深入理解操作系统:进程管理与调度策略
【4月更文挑战第30天】 在数字化时代,网络安全已成为维护信息完整性、确保数据流通安全和保障用户隐私的关键。本文将深入探讨网络安全的核心问题——安全漏洞,并分享关于加密技术的最新进展以及提升个人和企业安全意识的有效方法。通过对常见网络威胁的剖析,我们旨在提供一套综合性的网络防御策略,以助力读者构建更为坚固的信息安全防线。 【4月更文挑战第30天】 在现代操作系统的核心,进程管理是维持多任务环境稳定的关键。本文将深入探讨操作系统中的进程概念、进程状态转换及进程调度策略。通过分析不同的调度算法,我们将了解操作系统如何平衡各进程的执行,确保系统资源的高效利用和响应时间的最优化。文中不仅剖析了先来先