1 |
EventMachine now supports epoll, bringing large increases in performance and scalability to Ruby programs. |
---|
2 |
|
---|
3 |
Epoll(7) is a alternative mechanism for multiplexed I/O that is available in Linux 2.6 kernels. |
---|
4 |
It features significantly greater performance than the standard select(2) mechanism, when used in |
---|
5 |
applications that require very large numbers of open I/O descriptors. |
---|
6 |
|
---|
7 |
EventMachine has always used select(2) because its behavior is well standardized and broadly supported. |
---|
8 |
But select becomes unreasonably slow when a program has a |
---|
9 |
very large number of file descriptors or sockets. Ruby's version of select hardcodes a limit |
---|
10 |
of 1024 descriptors per process, but heavily loaded processes will start to show performance |
---|
11 |
degradation even after only a few hundred descriptors are in use. |
---|
12 |
|
---|
13 |
Epoll is an extended version of the poll(2) call, and it solves the problems with select. Programs |
---|
14 |
based on epoll can easily scale past Ruby's 1024-descriptor limit, potentially to tens of thousands |
---|
15 |
of connectors, with no significant impact on performance. |
---|
16 |
|
---|
17 |
(Another alternative which is very similar to epoll in principle is kqueue, supplied on BSD and its |
---|
18 |
variants.) |
---|
19 |
|
---|
20 |
|
---|
21 |
|
---|
22 |
This note shows you how to use epoll in your programs. |
---|
23 |
|
---|
24 |
=== Compiling EventMachine to use epoll. |
---|
25 |
|
---|
26 |
You don't have to do anything to get epoll support in EventMachine. |
---|
27 |
When you compile EventMachine on a platform that supports epoll, EM will |
---|
28 |
automatically generate a Makefile that includes epoll. (At this writing, this will only work |
---|
29 |
on Linux 2.6 kernels.) If you compile EM on a platform without epoll, then epoll support will |
---|
30 |
be omitted from the Makefile, and EM will work just as it always has. |
---|
31 |
|
---|
32 |
=== Using epoll in your programs. |
---|
33 |
|
---|
34 |
First, you need to tell EventMachine to use epoll instead of select (but see below, as this requirement |
---|
35 |
will be removed in a future EventMachine version). Second, you need to prepare your program to use |
---|
36 |
more than 1024 descriptors, an operation that generally requires superuser privileges. Third, you will probably |
---|
37 |
want your process to drop the superuser privileges after you increase your process's descriptor limit. |
---|
38 |
|
---|
39 |
=== Using EventMachine#epoll |
---|
40 |
|
---|
41 |
Call the method EventMachine#epoll anytime before you call EventMachine#run, and your program will |
---|
42 |
automatically use epoll, if available. It's safe to call EventMachine#epoll on any platform because |
---|
43 |
it compiles to a no-op on platforms that don't support epoll. |
---|
44 |
|
---|
45 |
require 'rubygems' |
---|
46 |
require 'eventmachine' |
---|
47 |
|
---|
48 |
EM.epoll |
---|
49 |
EM.run { |
---|
50 |
... |
---|
51 |
} |
---|
52 |
|
---|
53 |
|
---|
54 |
EventMachine#epoll was included in this initial release only to avoid changing the behavior of existing |
---|
55 |
programs. However, it's expected that a future release of EM will convert EventMachine#epoll to a no-op, |
---|
56 |
and run epoll by default on platforms that support it. |
---|
57 |
|
---|
58 |
=== Using EventMachine#set_descriptor_table_size |
---|
59 |
|
---|
60 |
In Linux (as in every Unix-like platform), every process has a internal table that determines the maximum |
---|
61 |
number of file and socket descriptors you may have open at any given time. The size of this table is |
---|
62 |
generally fixed at 1024, although it may be increased within certain system-defined hard and soft limits. |
---|
63 |
|
---|
64 |
If you want your EventMachine program to support more than 1024 total descriptors, you must use |
---|
65 |
EventMachine#set_descriptor_table_size, as follows: |
---|
66 |
|
---|
67 |
require 'rubygems' |
---|
68 |
require 'eventmachine' |
---|
69 |
|
---|
70 |
new_size = EM.set_descriptor_table_size( 60000 ) |
---|
71 |
$>.puts "New descriptor-table size is #{new_size}" |
---|
72 |
|
---|
73 |
EM.run { |
---|
74 |
... |
---|
75 |
} |
---|
76 |
|
---|
77 |
If successful, this example will increase the maximum number of descriptors that epoll can use to 60,000. |
---|
78 |
Call EventMachine#set_descriptor_table_size without an argument at any time to find out the current |
---|
79 |
size of the descriptor table. |
---|
80 |
|
---|
81 |
Using EventMachine#set_descriptor_table_size ONLY affects the number of descriptors that can be used |
---|
82 |
by epoll. It has no useful effect on platforms that don't support epoll, and it does NOT increase the |
---|
83 |
number of descriptors that Ruby's own I/O functions can use. |
---|
84 |
|
---|
85 |
#set_descriptor_table_size can fail if your process is not running as superuser, or if you try to set a |
---|
86 |
table size that exceeds the hard limits imposed by your system. In the latter case, try a smaller number. |
---|
87 |
|
---|
88 |
|
---|
89 |
=== Using EventMachine#set_effective_user |
---|
90 |
|
---|
91 |
In general, you must run your program with elevated or superuser privileges if you want to increase |
---|
92 |
your descriptor-table size beyond 1024 descriptors. This is easy enough to verify. Try running the |
---|
93 |
sample program given above, that increases the descriptor limit to 60,000. You will probably find that |
---|
94 |
the table size will not be increased if you don't run your program as root or with elevated privileges. |
---|
95 |
|
---|
96 |
But of course network servers, especially long-running ones, should not run with elevated privileges. |
---|
97 |
You will want to drop superuser privileges as soon as possible after initialization. To do this, |
---|
98 |
use EventMachine#set_effective_user: |
---|
99 |
|
---|
100 |
require 'rubygems' |
---|
101 |
require 'eventmachine' |
---|
102 |
|
---|
103 |
# (Here, program is running as superuser) |
---|
104 |
|
---|
105 |
EM.set_descriptor_table_size( 60000 ) |
---|
106 |
EM.set_effective_user( "nobody" ) |
---|
107 |
# (Here, program is running as nobody) |
---|
108 |
|
---|
109 |
EM.run { |
---|
110 |
... |
---|
111 |
} |
---|
112 |
|
---|
113 |
Of course, you will need to replace "nobody" in the example with the name of an unprivileged user |
---|
114 |
that is valid on your system. What if you want to drop privileges after opening a server socket |
---|
115 |
on a privileged (low-numbered) port? Easy, just call #set_effective_user after opening your sockets: |
---|
116 |
|
---|
117 |
require 'rubygems' |
---|
118 |
require 'eventmachine' |
---|
119 |
|
---|
120 |
# (Here, program is running as superuser) |
---|
121 |
|
---|
122 |
EM.set_descriptor_table_size( 60000 ) |
---|
123 |
|
---|
124 |
EM.run { |
---|
125 |
EM.start_server( "0.0.0.0", 80, MyHttpServer ) |
---|
126 |
EM.start_server( "0.0.0.0", 443, MyEncryptedHttpServer ) |
---|
127 |
|
---|
128 |
EM.set_effective_user( "nobody" ) |
---|
129 |
# (Here, program is running as nobody) |
---|
130 |
|
---|
131 |
... |
---|
132 |
} |
---|
133 |
|
---|
134 |
|
---|
135 |
Because EventMachine#set_effective_user is used to enforce security |
---|
136 |
requirements, it has no nonfatal errors. If you try to set a nonexistent or invalid effective user, |
---|
137 |
#set_effective_user will abort your program, rather than continue to run with elevated privileges. |
---|
138 |
|
---|
139 |
EventMachine#set_effective_user is a silent no-op on platforms that don't support it, such as Windows. |
---|
140 |
|
---|
141 |
|
---|