Skip to content
Newer
Older
100644 261 lines (185 sloc) 9.97 KB
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
1 # Einhorn: the language-independent shared socket manager
2
3 ![Einhorn](https://stripe.com/img/blog/posts/meet-einhorn/einhorn.png)
4
5 Let's say you have a server process which processes one request at a
6 time. Your site is becoming increasingly popular, and this one process
7 is no longer able to handle all of your inbound connections. However,
8 you notice that your box's load number is low.
9
10 So you start thinking about how to handle more requests. You could
11 rewrite your server to use threads, but threads are a pain to program
12 against (and maybe you're writing in Python or Ruby where you don't
13 have true threads anyway). You could rewrite your server to be
14 event-driven, but that'd require a ton of effort, and it wouldn't help
15 you go beyond one core. So instead, you decide to just run multiple
16 copies of your server process.
17
18 Enter Einhorn. Einhorn makes it easy to run (and keep alive) multiple
19 copies of a single long-lived process. If that process is a server
20 listening on some socket, Einhorn will open the socket in the master
21 process so that it's shared among the workers.
22
23 Einhorn is designed to be compatible with arbitrary languages and
24 frameworks, requiring minimal modification of your
25 application. Einhorn is simple to configure and run.
26
27 ## Installation
28
29 Install from Rubygems as:
30
31 $ gem install einhorn
32
33 Or build from source by:
34
35 $ gem build einhorn.gemspec
36
37 And then install the built gem.
38
39 ## Usage
40
41 Einhorn is the language-independent shared socket manager. Run
42 `einhorn -h` to see detailed usage. At a high level, usage looks like
43 the following:
44
45 einhorn [options] program
46
47 Einhorn will open one or more shared sockets and run multiple copies
48 of your process. You can seamlessly reload your code, dynamically
49 reconfigure Einhorn, and more.
50
51 ## Overview
52
53 To set Einhorn up as a master process running 3 copies of `sleep 5`:
54
55 $ einhorn -n 3 sleep 5
56
57 You can communicate your running Einhorn process via `einhornsh`:
58
59 $ einhornsh
60 Welcome gdb! You are speaking to Einhorn Master Process 11902
61 Enter 'help' if you're not sure what to do.
62
63 Type "quit" or "exit" to quit at any time
64 > help
65 You are speaking to the Einhorn command socket. You can run the following commands:
66 ...
67
68 ### Server sockets
69
70 If your process is a server and listens on one or more sockets,
8f16a0c @gdb Update README
gdb authored Sep 27, 2012
71 Einhorn can open these sockets and pass them to the workers. You can
72 specify the addresses to bind by passing one or more `-b ADDR`
73 arguments:
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
74
8f16a0c @gdb Update README
gdb authored Sep 27, 2012
75 einhorn -b 127.0.0.1:1234 my-command
76 einhorn -b 127.0.0.1:1234,r -b 127.0.0.1:1235 my-command
77
78 Each address is specified as an ip/port pair, possibly accompanied by options:
79
80 ADDR := (IP:PORT)[<,OPT>...]
81
82 In the worker process, the opened file descriptors will be represented
c889d79 @ebroder Switch from EINHORN_FDS to EINHORN_FD_N/EINHORN_FD_COUNT
ebroder authored Mar 12, 2013
83 as file descriptor numbers in a series of environment variables named
bd33db7 @ebroder Fix the docs to match actual behavior of EINHORN_FD_N vars
ebroder authored Mar 14, 2013
84 EINHORN_FD_0, EINHORN_FD_1, etc. (respecting the order that the `-b`
c889d79 @ebroder Switch from EINHORN_FDS to EINHORN_FD_N/EINHORN_FD_COUNT
ebroder authored Mar 12, 2013
85 options were provided in), with the total number of file descriptors
86 in the EINHORN_FD_COUNT environment variable:
8f16a0c @gdb Update README
gdb authored Sep 27, 2012
87
bd33db7 @ebroder Fix the docs to match actual behavior of EINHORN_FD_N vars
ebroder authored Mar 15, 2013
88 EINHORN_FD_0="6" # 127.0.0.1:1234
c889d79 @ebroder Switch from EINHORN_FDS to EINHORN_FD_N/EINHORN_FD_COUNT
ebroder authored Mar 12, 2013
89 EINHORN_FD_COUNT="1"
90
bd33db7 @ebroder Fix the docs to match actual behavior of EINHORN_FD_N vars
ebroder authored Mar 15, 2013
91 EINHORN_FD_0="6" # 127.0.0.1:1234,r
92 EINHORN_FD_1="7" # 127.0.0.1:1235
c889d79 @ebroder Switch from EINHORN_FDS to EINHORN_FD_N/EINHORN_FD_COUNT
ebroder authored Mar 12, 2013
93 EINHORN_FD_COUNT="2"
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
94
95 Valid opts are:
96
97 r, so_reuseaddr: set SO_REUSEADDR on the server socket
98 n, o_nonblock: set O_NONBLOCK on the server socket
99
100 You can for example run:
101
8f16a0c @gdb Update README
gdb authored Sep 27, 2012
102 $ einhorn -b 127.0.0.1:2345,r -m manual -n 4 -- example/time_server
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
103
104 Which will run 4 copies of
105
bd33db7 @ebroder Fix the docs to match actual behavior of EINHORN_FD_N vars
ebroder authored Mar 15, 2013
106 EINHORN_FD_0=6 EINHORN_FD_COUNT=1 example/time_server
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
107
108 Where file descriptor 6 is a server socket bound to `127.0.0.1:2345`
109 and with `SO_REUSEADDR` set. It is then your application's job to
110 figure out how to `accept()` on this file descriptor.
111
112 ### Command socket
113
114 Einhorn opens a UNIX socket to which you can send commands (run
115 `help` in `einhornsh` to see what admin commands you can
116 run). Einhorn relies on file permissions to ensure that no malicious
117 users can gain access. Run with a `-d DIRECTORY` to change the
118 directory where the socket will live.
119
c91a0f0 @gdb Update README
gdb authored Sep 26, 2012
120 Note that the command socket uses a line-oriented YAML protocol, and
121 you should ensure you trust clients to send arbitrary YAML messages
122 into your process.
123
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
124 ### Seamless upgrades
125
126 You can cause your code to be seamlessly reloaded by upgrading the
127 worker code on disk and running
128
129 $ einhornsh
130 ...
131 > upgrade
132
133 Once the new workers have been spawned, Einhorn will send each old
134 worker a SIGUSR2. SIGUSR2 should be interpreted as a request for a
135 graceful shutdown.
136
137 ### ACKs
138
139 After Einhorn spawns a worker, it will only consider the worker up
140 once it has received an ACK. Currently two ACK mechanisms are
141 supported: manual and timer.
142
143 #### Manual ACK
144
145 A manual ACK (configured by providing a `-m manual`) requires your
146 application to send a command to the command socket once it's
147 ready. This is the safest ACK mechanism. If you're writing in Ruby,
148 just do
149
150 require 'einhorn/worker'
151 Einhorn::Worker.ack!
152
153 in your worker code. If you're writing in a different language, or
154 don't want to include Einhorn in your namespace, you can send the
155 string
156
157 {"command":"worker:ack", "pid":PID}
158
159 to the UNIX socket pointed to by the environment variable
160 `EINHORN_SOCK_PATH`. (Be sure to include a trailing newline.)
161
8f16a0c @gdb Update README
gdb authored Sep 27, 2012
162 To make things even easier, you can pass a `-g` to Einhorn, in which
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
163 case you just need to `write()` the above message to the open file
8f16a0c @gdb Update README
gdb authored Sep 27, 2012
164 descriptor pointed to by `EINHORN_SOCK_FD`.
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
165
166 (See `lib/einhorn/worker.rb` for details of these and other socket
167 discovery mechanisms.)
168
169 #### Timer ACK [default]
170
171 By default, Einhorn will use a timer ACK of 1 second. That means that
172 if your process hasn't exited after 1 second, it is considered ACK'd
173 and healthy. You can modify this timeout to be more appropriate for
174 your application (and even set to 0 if desired). Just pass a `-m
175 FLOAT`.
176
177 ### Preloading
178
179 If you're running a Ruby process, Einhorn can optionally preload its
180 code, so it only has to load the code once per upgrade rather than
181 once per worker process. This also saves on memory overhead, since all
182 of the code in these processes will be stored only once using your
183 operating system's copy-on-write features.
184
185 To use preloading, just give Einhorn a `-p PATH_TO_CODE`, and make
186 sure you've defined an `einhorn_main` method.
187
188 In order to maximize compatibility, we've worked to minimize Einhorn's
c91a0f0 @gdb Update README
gdb authored Sep 26, 2012
189 dependencies. It has no dependencies outside of the Ruby standard
190 library.
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
191
192 ### Command name
193
194 You can set the name that Einhorn and your workers show in PS. Just
195 pass `-c <name>`.
196
197 ### Options
198
c889d79 @ebroder Switch from EINHORN_FDS to EINHORN_FD_N/EINHORN_FD_COUNT
ebroder authored Mar 12, 2013
199 -b, --bind ADDR Bind an address and add the corresponding FD via the environment
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
200 -c, --command-name CMD_NAME Set the command name in ps to this value
201 -d, --socket-path PATH Where to open the Einhorn command socket
202 -e, --pidfile PIDFILE Where to write out the Einhorn pidfile
203 -f, --lockfile LOCKFILE Where to store the Einhorn lockfile
8f16a0c @gdb Update README
gdb authored Sep 27, 2012
204 -g, --command-socket-as-fd Leave the command socket open as a file descriptor, passed in the EINHORN_SOCK_FD environment variable. This allows your worker processes to ACK without needing to know where on the filesystem the command socket lives.
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
205 -h, --help Display this message
206 -k, --kill-children-on-exit If Einhorn exits unexpectedly, gracefully kill all its children
207 -l, --backlog N Connection backlog (assuming this is a server)
208 -m, --ack-mode MODE What kinds of ACK to expect from workers. Choices: FLOAT (number of seconds until assumed alive), manual (process will speak to command socket when ready). Default is MODE=1.
209 -n, --number N Number of copies to spin up
210 -p, --preload PATH Load this code into memory, and fork but do not exec upon spawn. Must define an "einhorn_main" method
211 -q, --quiet Make output quiet (can be reconfigured on the fly)
212 -s, --seconds N Number of seconds to wait until respawning
213 -v, --verbose Make output verbose (can be reconfigured on the fly)
aff18e4 @ebroder Update our assortment of READMEs
ebroder authored Aug 5, 2014
214 --drop-env-var VAR_NAME Delete VAR_NAME from the environment that is restored on upgrade
215 --reexec-as=CMDLINE Substitute CMDLINE for \"einhorn\" when upgrading
216 --nice MASTER[:WORKER=0][:RENICE_CMD=/usr/bin/renice]
217 Unix nice level at which to run the einhorn processes. If not running as root, make sure to ulimit -e as appopriate.
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
218 --with-state-fd STATE [Internal option] With file descriptor containing state
aff18e4 @ebroder Update our assortment of READMEs
ebroder authored Aug 6, 2014
219 --upgrade-check [Internal option] Check if Einhorn can exec itself and exit with status 0 before loading code
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
220 --version Show version
221
222
223 ## Contributing
224
225 Contributions are definitely welcome. To contribute, just follow the
226 usual workflow:
227
228 1. Fork Einhorn
229 2. Create your feature branch (`git checkout -b my-new-feature`)
230 3. Commit your changes (`git commit -am 'Added some feature'`)
231 4. Push to the branch (`git push origin my-new-feature`)
232 5. Create new Github pull request
233
234 ## History
235
236 Einhorn came about when Stripe was investigating seamless code
237 upgrading solutions for our API worker processes. We really liked the
238 process model of [Unicorn](http://unicorn.bogomips.org/), but didn't
239 want to use its HTTP functionality. So Einhorn was born, providing the
240 master process functionality of Unicorn (and similar preforking
241 servers) to a wider array of applications.
242
243 See https://stripe.com/blog/meet-einhorn for more background.
244
245 Stripe currently uses Einhorn in production for a number of
8c238a1 @ebroder Modify thin example to use thin-attach_socket + EventMachine-LE
ebroder authored Apr 3, 2014
246 services. You can use Conrad Irwin's thin-attach_socket gem along with
247 EventMachine-LE to support file-descriptor passing. Check out
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
248 `example/thin_example` for an example of running Thin under Einhorn.
249
250 ## Compatibility
251
33d3171 @gdb Add basic support for a config file
gdb authored Apr 12, 2015
252 Einhorn is being developed on 2.1, but should be compatible back to
253 1.8.7 (see the .travis.yml for the exact set of Rubies we test on). We
254 plan to drop official support for 1.8.7 in the near future.
3cc1e1e @gdb Initial import of Einhorn
gdb authored May 17, 2012
255
256 ## About
257
258 Einhorn is a project of [Stripe](https://stripe.com), led by [Greg
259 Brockman](https://twitter.com/thegdb). Feel free to get in touch at
260 info@stripe.com.
Something went wrong with that request. Please try again.