How can event driver nginx process high concurrent requests with only 2 worker processes?
As we kn开发者_StackOverflow中文版ow nginx is not threaded,only 2 worker processes by default.
And we also know that accept()
will block until new requests come:
s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
How can it handle more than 2 requests at the same time,basically 2
processes running more than 2
routines cocurrently?
Can someone explain this with some pseudo code?
The trick is that it uses non-blocking IO in the second process. For some background on what motivated non-blocking IO servers, I suggest reading the c10k problem website. Superb.
Anyway, the second process will register with the kernel its interest in readable events, writable events, and error events with a non-blocking IO interface: select(2)
, poll(2)
, epoll(4)
, or similar. select(2)
is easiest to talk about, so here we go. First, all sockets are opened with SOCK_NONBLOCK
, to make sure operations on the socket won't block. Second, there's a control loop that is waiting for activity on the client sockets:
fd_set *f
FD_CLR(f)
foreach client socket
FD_SET(socket, f)
n_ready_to_read = select(max(socketvalue), f, NULL, f, 0)
for (i=0; i<max(socketvalue) && n_ready_to_read; n_ready_to_read--, i++)
if FD_ISSET(i, f)
handle_input_from_client(i)
}
This is just a sketch of how the process discovers when a client has sent data along and the socket won't block when reading. So a read(2)
call on the socket won't block. Of course, since a single server is writing to many clients simultaneously as well, it needs to keep track of the sockets it is writing to, and send data to them when they are ready to accept write(2)
calls, and similar for handling errors.
There's a better, fuller, example in select_tut(2)
.
select(2)
has largely been replaced in newer systems because there was significant overhead in creating the list of fds to check, the fd_set
is implemented as a 1024-bit bitfield on Linux, and for ABI reasons can never grow beyond that, and since there's no good way to tell the kernel that you're only interested in a sparse subset of that list, it must check from 0- every time, even if the array doesn't have many fds set.
So, it's been replaced with mechanisms that allow adding and removing specific file descriptors from a long-lived set, that allow for edge-triggered and level-triggered notification of 'ready', and don't perform silly searches over large fixed-size arrays to find the file descriptors you're interested in.
The libevent library provides a wonderful abstraction layer to this process that automatically selects the best available interface on a wide variety of platforms, and allows programmers to focus on the code specific to their server.
精彩评论