Why Nginx has a much higher performance than Apache

Source: Internet
Author: User

Why does Nginx have a much higher performance than Apache? This is thanks to Nginx's use of the latest epoll (Linux 2.6 kernel) and Kqueue (FreeBSD) network I/O models, while Apache uses the traditional select model.

Currently, the Epoll network I/O model is used for squid and memcached that can withstand high concurrent access under Linux.

The Select Network I/O model used by Apache is very inefficient in handling a large number of connection reads and writes.

The following analogy is used to parse the difference between the Select model used by Apache and the Epoll model used by Nginx:

Let's say you're in college, there are a lot of rooms in the dorm building, and your friends are coming to see you.

Select version of the tube aunt will take your friends to the room to find, until you find you.

and epoll version of the house tube aunt will first write down each student's room number,

When your friend comes, just tell your friends which room you live in, and don't take your friend to the building to find someone in person.

If there are 10,000 people, you have to find their own living in this building students, select version and Epoll version of the aunt, who is more efficient, self-evident.

Similarly, in high concurrent servers, polling I/O is one of the most time-consuming operations, and the performance of select and Epoll is also very clear.

EPOLL-I/O Event Notification Facility

In the Linux network programming, the long time uses the Select to do the event to trigger.

In the new Linux kernel, there is a mechanism to replace it, that is epoll.

The biggest benefit compared to Select,epoll is that it does not reduce efficiency as the number of monitoring FD increases.

Because in a select implementation in the kernel, it is handled by polling, the more the number of FD polled, the more natural time consuming.

Also, in the Linux/posix_types.h header file there is such a declaration:

#define __FD_SETSIZE 1024

This indicates that select listens for up to 1024 FD at the same time, and of course, you can expand this number by modifying the header file and then recompiling the kernel, but this does not seem to be the root.

The Epoll interface is very simple, with a total of three functions:

1, int epoll_create (int size);

Create a handle to the Epoll, which is used to tell the kernel how large the number of listeners is.

This parameter differs from the first parameter in select (), giving the value of the fd+1 that is the maximum listener.

It is important to note that when a epoll handle is created, it consumes an FD value, and under Linux, if you look at the/proc/process id/fd/,

is able to see this fd, so after using Epoll, you must call Close () closed, otherwise it may cause FD to be exhausted.

2, int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event);

The Epoll event registration function, which differs from select (), tells the kernel what type of event to listen to when listening to events.

Instead, register the type of event you want to listen on first. The first parameter is the return value of Epoll_create ().

The second parameter represents an action, represented by three macros:

Epoll_ctl_add: Register the new FD to EPFD;

Epoll_ctl_mod: Modify the monitoring events of the registered FD;

Epoll_ctl_del: Delete a fd from the EPFD;

The third parameter is the FD to listen to, the fourth parameter is to tell the kernel what to listen to, struct epoll_event structure as follows:

typedef Union EPOLL_DATA {

void *ptr;

int FD;

__uint32_t u32;

__uint64_t U64;

} epoll_data_t;

struct Epoll_event {

__uint32_t events; /* Epoll Events */

epoll_data_t data; /* USER Data variable */

};

Events can be a collection of several macros:

Epollin: indicates that the corresponding file descriptor can be read (including a graceful shutdown of the peer socket);

epollout: indicates that the corresponding file descriptor can be written;

Epollpri: indicates that the corresponding file descriptor has an urgent data readable (this should indicate the arrival of out-of-band data);

Epollerr: Indicates an error occurred in the corresponding file descriptor;

Epollhup: indicates that the corresponding file descriptor is hung up;

epollet: set Epoll to edge triggered mode, which is relative to the horizontal trigger (level triggered).

Epolloneshot: listen to only one event, and when you're done listening to the event,

If you still need to continue listening to the socket, you need to add the socket to the Epoll queue again.

3, int epoll_wait (int epfd, struct epoll_event * events, int maxevents, int timeout);

Waits for the event to occur, similar to a select () call.

Parameter events is used to get the collection of event from the kernel, the maxevents tells the kernel how big the events are, the value of this maxevents cannot be greater than the size when the Epoll_create () was created, the parameter timeout is the timeout (in milliseconds, 0 returns immediately , 1 will be uncertain, and there are statements that are permanently blocked).

The function returns the number of events that need to be processed, such as returning 0 to indicate a timeout.

4, about the ET, lt two Modes of operation:

The conclusion can be drawn that:

The ET pattern is only notified when the state changes, and the so-called state changes do not include the data in the buffer, which means that, if you want to use the ET pattern, you need to read/write until an error occurs. Many people reflect why the ET model receives only a subset of the data and is no longer notified, mostly because of this, while the LT mode is kept informed as long as there is no data processing.

So how do you actually use Epoll? It's actually very simple.

By including a header file in # # <sys/epoll.h> and a few simple APIs will greatly increase the number of support for your Web server.

Start by create_epoll (int Maxfds) to create a epoll handle, where Maxfds is the maximum number of handles that you epoll support.

This function returns a new Epoll handle, and all subsequent operations are handled by this handle.

When you're done, remember to close the created Epoll handle with close ().

Then in your main loop of the network, each frame calls epoll_wait (int epfd, epoll_event events, int max events, int timeout) to query all the network interfaces, to see which one can be read and which one can be written. The basic syntax is:

Nfds = epoll_wait (KDPFD, events, maxevents,-1);

Where KDPFD is the handle created with Epoll_create, events is a epoll_event* pointer, and when epoll_wait this function succeeds, all read and write events are stored in epoll_events.

Max_events is the number of socket handles currently required to be monitored. The last timeout is a timeout of epoll_wait, which is 0 when the return is immediately, and 1 is the time to wait until there is an event range, for any positive integer to represent such a long time, if there is no event, then the range. In general, if the network main loop is a separate thread, you can use-one, and so on, this can guarantee some efficiency, if it is the same thread as the main logic, you can guarantee the efficiency of the main loop with zero.

The epoll_wait range should be followed by a loop, all the events of interest.

Almost all Epoll programs use the following framework:

for (;;)

{

Nfds = epoll_wait (epfd,events,20,500);

for (I=0;i<nfds;++i)

{

if (EVENTS[I].DATA.FD==LISTENFD)//There is a new connection

{

CONNFD = Accept (LISTENFD, (SOCKADDR *) &clientaddr, &clilen); Accept this connection

EV.DATA.FD=CONNFD;

ev.events=epollin| Epollet;

Epoll_ctl (Epfd,epoll_ctl_add,connfd,&ev); Add a new FD to the Epoll listening queue

}

else if (Events[i].events&epollin)//received data, read socket

{

n = Read (SOCKFD, line, MAXLINE)) < 0//Reading

EV.DATA.PTR = MD; MD for custom types, adding data

Ev.events=epollout| Epollet;

Epoll_ctl (Epfd,epoll_ctl_mod,sockfd,&ev);//Modify the identifier, wait for the next loop to send data, the essence of asynchronous processing

}

else if (events[i].events&epollout)//have data to send, write socket

{

struct myepoll_data* MD = (myepoll_data*) events[i].data.ptr; Fetch data

SOCKFD = md->fd;

Send (SOCKFD, md->ptr, strlen (char*) md->ptr), 0); Send data

EV.DATA.FD=SOCKFD;

ev.events=epollin| Epollet;

Epoll_ctl (Epfd,epoll_ctl_mod,sockfd,&ev); Modify identifiers to receive data while waiting for the next loop

}

Else

{

The other treatment

}

}

}

Links: http://blog.csdn.net/ajaxuser/article/details/7803038

Links: http://blog.sina.com.cn/s/blog_5cef35c50100f3bv.html

Links: http://blog.csdn.net/ljx0305/article/details/4065058

Why Nginx has a much higher performance than Apache

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.