Nginx的架构介绍 Nginx第二篇

本文探讨了Nginx如何通过多进程模型实现高效的并发处理。解析了master进程与worker进程的角色分配,以及worker进程间如何避免惊群现象。此外,还介绍了Nginx采用异步非阻塞的方式提高并发能力的技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    Nginx是多进程的方式来完成并发工作的。nginx在启动后,会有一个master进程和多个worker进程。每个worker进程负责处理若干个来自客户端的请求。worker进程之间的地位是相等的,推荐设置worker的个数为cpu的核数。一个请求只能由一个worker来负责,不能做重复的工作。

    master进程主要用来管理worker进程,包含:接收来自外界(管理员)的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。


问题一:多个worker进程如何处理请求?

    每个worker进程都是从master进程fork过来的子进程,在master进程里面,先建立好需要listen的socket之后,然后再fork出多个worker进程,这样每个worker进程都可以去accept这socket(即每个进程的这个socket会监控在同一个ip地址与端口)。如果不做特殊处理的话,当来了一个请求之后,只会有一个worker会accept成功,其他所有worker会accept失败(惊群现象)。Nginx为避免这样,做出的处理是提供了一个accept_mutex,给每个worker的accept加上一把共享锁。

    现在貌似大多数内核已经解决了惊群的问题,我在mac osx10.9上测试,有两个进程监听同一个套接字,当来到请求时,只有一个进程被唤醒去处理这个请求。

#include<iostream>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
using namespace std;

int main()
{	
	int socket_listen, socket_data, c, read_size;
	struct sockaddr_in server, client;
	char client_message[2000];
	socket_listen = socket(AF_INET, SOCK_STREAM, 0);
	if(socket_listen == -1)
		cout<<"create socket fail!"<<endl;
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port = htons(8888);
	if(bind(socket_listen, (struct sockaddr*)&server, sizeof(server)) < 0)
		cout<<"bind fail!"<<endl;
	listen(socket_listen, 5);
	c = sizeof(struct sockaddr_in);

	if(fork() != 0)
	{
		cout<<"father will accept."<<endl;
		socket_data = accept(socket_listen, (struct sockaddr*)&client, (socklen_t*)&c);
		cout<<"this is father."<<endl;
	}
	else
	{
		cout<<"son will accept."<<endl;
		socket_data = accept(socket_listen, (struct sockaddr*)&client, (socklen_t*)&c);
		cout<<"this is son."<<endl;
	}

	return 0;
}

程序运行起来之后,换一个终端用lsof -i:8888 可以看出有两个进程监听的本地的8888端口。

telnet localhost 8888

访问第一次,父进程被激发;访问第二次,子进程被激发。可见内核不存在这个问题了。


问题二:为何nginx的可以高并发?

    nginx采用了异步非阻塞的方式来处理请求。什么阻塞?阻塞是进程等待读写事件,这等待期间进程啥也不做,cpu让给别的进程。什么是非阻塞?事件没有准备好,马上返回EAGAIN(一会儿再来),然后需要不断再过来看,这样会造成切换上下文的代价。现在采用的机制是:当事件没准备好时,放到epoll里面,事件准备好了,我们就去读写;当返回EAGAIN时,我们将它再次加入到epoll里面。这样的方式可以做到一个线程能不断切换同时处理多个请求。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值