Nginx把一个request的处理分为多个阶段(phases)。这些阶段会有IO阻塞吗?如果有了阻塞,Nginx会去执行其他requests,但是执行其他requests的时候会有优先级区分吗(会先执行已经进行到后面阶段的requests吗?)?还有,Nginx会在每个阶段都有一个thread pool来处理,还是至始至终就他自己一个thread?
Nginx把一个request的处理分为多个阶段(phases)。这些阶段会有IO阻塞吗?如果有了阻塞,Nginx会去执行其他requests,但是执行其他requests的时候会有优先级区分吗(会先执行已经进行到后面阶段的requests吗?)?还有,Nginx会在每个阶段都有一个thread pool来处理,还是至始至终就他自己一个thread?
8 回答2.9k 阅读
2 回答1.6k 阅读✓ 已解决
1 回答1.2k 阅读✓ 已解决
4 回答1.5k 阅读
1 回答840 阅读✓ 已解决
2 回答1.2k 阅读
1 回答972 阅读
cat /proc/3776/status|grep Threads
可见Nginx工作进程只有1个线程,其中3776是Nginx工作进程的PID。另外Nginx从1.7.11加入了AIO线程池支持,能够使用AIO多线程读取和发送大文件,以免工人进程被阻塞(小文件用sendfile,大文件用AIO线程池),要启用线程池支持,configure时需要显式加入--with-threads选项。https://www.nginx.com/blog/thread-pools-boost-performance-9x/
http://nginx.org/en/docs/ngx_core_module.html#thread_pool
转:
当listen_fd有新的accept()请求过来,操作系统会唤醒所有子进程,因为这些进程都epoll_wait()同一个listen_fd,操作系统又无从判断由谁来负责accept,索性干脆全部叫醒,但最终只会有一个进程成功accept,其他进程accept失败.所有子进程都是被"吓醒"的,所以称之为Thundering Herd(惊群).
监听套接字在启动时就完成初始化,worker进程通过这些套接字接受,读取请求和输出响应.Nginx并没有像PHP-FPM那样采用master进程来分发请求,这个工作由操作系统内核机制完成,所以可能会导致惊群现象,也就是当listen_fd有新的accept()请求过来,操作系统会唤醒所有子进程.
Nginx解决惊群的思路:避免惊群.
http://nginx.org/en/docs/ngx_core_module.html#accept_mutex
具体措施有使用全局互斥锁(accept_mutex on),每个工作进程在epoll_wait()之前先去申请锁,申请到则继续处理,获取不到则等待,并设置了一个负载均衡的算法(当某一个工作进程的任务量达到总设置量的7/8时,则不会再尝试去申请锁)来均衡各个进程的任务量.
Nginx解决惊群的新方法:使用内核提供的Socket ReusePort功能
NGINX 1.9.1 支持socket分片:
http://nglua.com/docs/sharding.html
NGINX1.9.1支持socket的SO_REUSEPORT选项,这个选项在许多操作系统的新版本有效,包括DragonFly BSD和Linux(3.9+内核).这个选项允许多个socket监听同一个IP地址和端口的组合.内核负载均衡这些进来的sockets连接,将这些socket有效的分片.当SO_REUSEPORT选项没开启时,连接进来时监听socket默认会通知某个进程.如果accept_mutex off这个指令,此时会唤醒所有的工作进程,它们将为了得到它产生竞争,这就是所谓的惊群现象.如果使用epoll且不用锁(accept_mutex off),当监听端口有读操作时,是会产生惊群现象的.启用SO_REUSEPORT选项后,每个进程都有自己独立的监听socket.内核决定哪个是有效的socket(进程)得到这个连接.这样做降低了延迟并提高了工作进程的性能,它也意味着工作进程在准备处理它们前被赋予了新的连接.
nginx默认以多进程的方式工作,一个master进程和多个worker进程,master进程主要用来管理worker进程.多个worker进程同等竞争来自客户端的请求,一个worker进程可以处理多个请求,但不能处理其它worker进程的请求.每个worker进程里面只有一个主线程,在epoll支持下,采用异步非阻塞的方式来处理请求,从而实现高并发.epoll支持监听多个事件(socket轮询),当事件没准备好时,放到epoll里面,事件准备好了,就去读写.与多线程相比,这种事件处理方式是有很大的优势的,不需要创建线程,每个请求占用的内存也很少,没有上下文切换,事件处理非常的轻量级.并发数再多也不会导致无谓的资源浪费(上下文切换),更多的并发数,只是会占用更多的内存而已.而httpd常用的工作方式是每个请求会独占一个工作线程,当并发数上到几千时,就同时有几千的线程在处理请求了,这对操作系统来说,是个不小的挑战.线程带来的内存占用非常大,线程的上下文切换带来的cpu开销很大,httpd的性能自然就上不去了.Tengine团队之前有对连接数进行过测试,在24G内存的机器上,Nginx处理的并发请求数达到过200万.(平均1G内存可以处理8万多请求)Nginx支持将某一个进程绑定在某一个核上(CPU亲缘性绑定),这样就不会因为进程的切换带来cache的失效,所以推荐设置cpu有几个核就设置几个worker进程.但注意,过多的worker进程,只会导致进程来竞争cpu资源了,从而带来不必要的上下文切换,所以worker进程不是越多越好.详细参见:
http://tengine.taobao.org/book/chapter_02.html