schleyfox-ernie 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +2 -0
- data/History.txt +40 -0
- data/LICENSE +20 -0
- data/README.md +306 -0
- data/Rakefile +67 -0
- data/VERSION.yml +5 -0
- data/bin/ernie +116 -0
- data/contrib/ebench.erl +76 -0
- data/ebin/ernie_server_app.app +2 -0
- data/elib/asset_pool.erl +155 -0
- data/elib/asset_pool_sup.erl +13 -0
- data/elib/bert.erl +69 -0
- data/elib/ernie.hrl +18 -0
- data/elib/ernie_access_logger.erl +170 -0
- data/elib/ernie_access_logger_sup.erl +15 -0
- data/elib/ernie_admin.erl +60 -0
- data/elib/ernie_config.erl +30 -0
- data/elib/ernie_native.erl +26 -0
- data/elib/ernie_server.erl +326 -0
- data/elib/ernie_server_app.erl +20 -0
- data/elib/ernie_server_sup.erl +22 -0
- data/elib/logger.erl +108 -0
- data/elib/logger_sup.erl +13 -0
- data/elib/port_wrapper.erl +65 -0
- data/ernie.gemspec +94 -0
- data/examples/example.cfg +12 -0
- data/examples/ext.erl +8 -0
- data/examples/ext.rb +39 -0
- data/examples/nat.erl +12 -0
- data/ext/Makefile +2 -0
- data/ext/extconf.rb +1 -0
- data/lib/ernie.rb +225 -0
- data/test/ernie_server_test.rb +77 -0
- data/test/ernie_test.rb +43 -0
- data/test/helper.rb +17 -0
- data/test/load.rb +18 -0
- data/test/sample/ext.rb +42 -0
- data/test/sample/sample.cfg +4 -0
- metadata +134 -0
data/.document
ADDED
data/.gitignore
ADDED
data/History.txt
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
= 2.2.0 / 2010-03-12
|
2
|
+
* Minor Additions
|
3
|
+
* Set procline for external Ruby handlers
|
4
|
+
|
5
|
+
= 2.1.0 / 2010-02-20
|
6
|
+
* Major Additions
|
7
|
+
* Add access logging
|
8
|
+
|
9
|
+
= 2.0.0 / 2010-02-16
|
10
|
+
* Major Changes
|
11
|
+
* Use configuration file for defining handlers
|
12
|
+
* Add Native Erlang modules
|
13
|
+
* Abstract handler logic to support handlers in any language
|
14
|
+
* Add High/Low connection queues
|
15
|
+
* Remove Ruby DSL (must use Ernie.expose now)
|
16
|
+
|
17
|
+
= 1.3.0 / 2009-11-30
|
18
|
+
* API Additions
|
19
|
+
* Add loglevel for setting log level
|
20
|
+
* Add Ernie.auto_start bool
|
21
|
+
* Major changes
|
22
|
+
* Better logging granularity
|
23
|
+
|
24
|
+
= 1.2.0 / 2009-11-23
|
25
|
+
* API Additions
|
26
|
+
* Add Ernie.expose
|
27
|
+
* Internal Changes
|
28
|
+
* Remove 15s internal timeout
|
29
|
+
|
30
|
+
= 1.1.0 / 2009-10-28
|
31
|
+
* Major changes
|
32
|
+
* Remove dependency on Erlectricity
|
33
|
+
* Simplify processing loop
|
34
|
+
|
35
|
+
= 1.0.0 / 2009-10-19
|
36
|
+
* No Changes. Production ready!
|
37
|
+
|
38
|
+
= 0.4.0 / 2009-10-08
|
39
|
+
* Major changes
|
40
|
+
* Convert to use BERT gem.
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Tom Preston-Werner
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
Ernie
|
2
|
+
=====
|
3
|
+
|
4
|
+
By Tom Preston-Werner (tom@mojombo.com)
|
5
|
+
|
6
|
+
Ernie is a BERT-RPC server implementation that uses an Erlang server to accept
|
7
|
+
incoming connections, and then delegates the request to custom modules that
|
8
|
+
you can write in any language (currently only Ruby and Erlang support is
|
9
|
+
included).
|
10
|
+
|
11
|
+
Modules that are written in Ruby or any non-Erlang language are known as
|
12
|
+
"external" modules and you must specify how many workers of each module should
|
13
|
+
be spawned. Requests against these modules are balanced between the workers.
|
14
|
+
Modules that are written in Erlang are known as "native" modules and run
|
15
|
+
within the Erlang server's runtime. Since these are spawned as lightweight
|
16
|
+
processes, there is no balancing necessary and much less communication
|
17
|
+
overhead when compared to external modules.
|
18
|
+
|
19
|
+
Ernie supports multiple heterogenous modules. For instance, you can have an
|
20
|
+
external Ruby module running 10 workers *and* a native Erlang module running
|
21
|
+
simultaneously. Ernie keeps track of sending requests to the proper module.
|
22
|
+
Using a technique called "shadowing," you can selectively optimize certain
|
23
|
+
external module functions with native code and Ernie will handle selecting the
|
24
|
+
correct function.
|
25
|
+
|
26
|
+
See the full BERT-RPC specification at [bert-rpc.org](http://bert-rpc.org).
|
27
|
+
|
28
|
+
Ernie currently supports the following BERT-RPC features:
|
29
|
+
|
30
|
+
* `call` requests
|
31
|
+
* `cast` requests
|
32
|
+
|
33
|
+
Ernie was developed for GitHub and is currently in production use serving
|
34
|
+
millions of RPC requests every day. The stability and performance have been
|
35
|
+
exemplary.
|
36
|
+
|
37
|
+
Ernie follows [Semantic Versioning](http://semver.org/) for release
|
38
|
+
versioning.
|
39
|
+
|
40
|
+
Installation
|
41
|
+
------------
|
42
|
+
|
43
|
+
Step 1: Install Erlang (R13B or higher).
|
44
|
+
|
45
|
+
http://www.erlang.org/download.html
|
46
|
+
|
47
|
+
Step 2: Install Ernie:
|
48
|
+
|
49
|
+
$ [sudo] gem install ernie
|
50
|
+
|
51
|
+
|
52
|
+
Running
|
53
|
+
-------
|
54
|
+
|
55
|
+
Usage: ernie [command] [options]
|
56
|
+
-c, --config CONFIG Config file.
|
57
|
+
-p, --port PORT Port.
|
58
|
+
-l, --log-level Log level (0-4).
|
59
|
+
-a, --access-log LOGFILE Access log file
|
60
|
+
-d, --detached Run as a daemon.
|
61
|
+
-P, --pidfile PIDFILE Location to write pid file.
|
62
|
+
|
63
|
+
Commands:
|
64
|
+
<none> Start an Ernie server.
|
65
|
+
reload-handlers Gracefully reload all of the external handlers
|
66
|
+
and use the new code for all subsequent requests.
|
67
|
+
stats Print a list of connection and handler statistics.
|
68
|
+
|
69
|
+
Examples:
|
70
|
+
ernie -d -p 9999 -c example.cfg
|
71
|
+
Start the ernie server in the background on port 9999 using the
|
72
|
+
example.cfg configuration file.
|
73
|
+
|
74
|
+
ernie reload-handlers -p 9999
|
75
|
+
Reload the handlers for the ernie server currently running on
|
76
|
+
port 9999.
|
77
|
+
|
78
|
+
|
79
|
+
Configuration File
|
80
|
+
------------------
|
81
|
+
|
82
|
+
Ernie configuration files are written as a series of dotted Erlang terms. Each
|
83
|
+
term is a list of 2-tuples that specify options for a module.
|
84
|
+
|
85
|
+
### Native Modules
|
86
|
+
|
87
|
+
The form for native modules is:
|
88
|
+
|
89
|
+
[{module, Module},
|
90
|
+
{type, native},
|
91
|
+
{codepaths, CodePaths}].
|
92
|
+
|
93
|
+
Where Module is an atom corresponding to the module name and CodePaths is a
|
94
|
+
list of strings representing the file paths that should be added to the
|
95
|
+
runtime's code path. These paths will be prepended to the code path and must
|
96
|
+
include the native module's directory and the directories of any dependencies.
|
97
|
+
|
98
|
+
### External Modules
|
99
|
+
|
100
|
+
The form for external modules is:
|
101
|
+
|
102
|
+
[{module, Module},
|
103
|
+
{type, external},
|
104
|
+
{command, Command},
|
105
|
+
{count, Count}].
|
106
|
+
|
107
|
+
Where Module is an atom corresponding to the module name, Command is a string
|
108
|
+
specifying the command to be executed in order to start a worker, and Count is
|
109
|
+
the number of workers to spawn.
|
110
|
+
|
111
|
+
### Shadowing
|
112
|
+
|
113
|
+
If you specify a native module and an external module of the same name (and in
|
114
|
+
that order), Ernie will inspect the native module to see if it has the
|
115
|
+
requested function exported and use that if it does. If it does not, then it
|
116
|
+
will fall back on the external module. This can be used to selectively
|
117
|
+
optimize certain functions in a module without any modifications to your
|
118
|
+
client code.
|
119
|
+
|
120
|
+
### Predicate Shadowing
|
121
|
+
|
122
|
+
In some circumstances it can be nice to conditionally shadow a function in an
|
123
|
+
external module based on the nature of the arguments. For example, you might
|
124
|
+
want requests for `math:fib(X)` to be routed to the external module when X is
|
125
|
+
less than 10, but to be handled by the native module when X is 10 or greater.
|
126
|
+
This can be accomplished by implementing a function `math:fib_pred(X)` in the
|
127
|
+
native module. Notice the `_pred` appended to the normal function name (pred
|
128
|
+
is short for predicate). If a function like this is present, Ernie will call
|
129
|
+
it with the requested arguments and if the return value is `true` the native
|
130
|
+
module will be used. If the return value is `false` the external module will
|
131
|
+
be used.
|
132
|
+
|
133
|
+
|
134
|
+
Example Configuration File
|
135
|
+
--------------------------
|
136
|
+
|
137
|
+
The following example config file informs Ernie of two modules. The first term
|
138
|
+
identifies a native module 'nat' that resides in the nat.beam file under the
|
139
|
+
'/path/to/app/ebin' directory. The second term specifies an external module
|
140
|
+
'ext' that will have 2 workers started with the command 'ruby
|
141
|
+
/path/to/app/ernie/ext.rb'.
|
142
|
+
|
143
|
+
[{module, nat},
|
144
|
+
{type, native},
|
145
|
+
{codepaths, ["/path/to/app/ebin"]}].
|
146
|
+
|
147
|
+
[{module, ext},
|
148
|
+
{type, external},
|
149
|
+
{command, "ruby /path/to/app/ernie/ext.rb"},
|
150
|
+
{count, 2}].
|
151
|
+
|
152
|
+
|
153
|
+
Access Log
|
154
|
+
----------
|
155
|
+
|
156
|
+
If you have requested that an access log be written (using the -a or
|
157
|
+
--access-log option) then all requests will be logged to that file. Each
|
158
|
+
request is printed on a single line. The elements of the log line are as
|
159
|
+
follows (with comments on the right side):
|
160
|
+
|
161
|
+
ACC type of message [ ACC | ERR ]
|
162
|
+
[2010-02-20T11:42:25.259750] time the connection was accepted
|
163
|
+
0.000053 seconds from connection to processing start
|
164
|
+
0.000237 seconds from processing start to finish
|
165
|
+
- delimiter
|
166
|
+
0 size of high queue at connect time
|
167
|
+
0 size of low queue at connect time
|
168
|
+
nat type of handler [ nat | ext ]
|
169
|
+
high priority [ high | low ]
|
170
|
+
- delimiter
|
171
|
+
{call,nat,add,[1,2]} message
|
172
|
+
|
173
|
+
|
174
|
+
Log lines are written when the request completes so they may appear out of
|
175
|
+
order with respect to connection time. To facilitate log rotation, Ernie will
|
176
|
+
create a new access log file if the current log file is moved or deleted.
|
177
|
+
|
178
|
+
|
179
|
+
Native (Erlang) Handler
|
180
|
+
-----------------------
|
181
|
+
|
182
|
+
Native handlers are written as normal Erlang modules. The exported functions
|
183
|
+
will become available to BERT-RPC clients.
|
184
|
+
|
185
|
+
### Example
|
186
|
+
|
187
|
+
-module(nat).
|
188
|
+
-export([add/2]).
|
189
|
+
|
190
|
+
add(A, B) ->
|
191
|
+
A + B.
|
192
|
+
|
193
|
+
### BERT-RPC Sequence Example
|
194
|
+
|
195
|
+
-> {call, nat, add, [1, 2]}
|
196
|
+
<- {reply, 3}
|
197
|
+
|
198
|
+
|
199
|
+
External (Ruby) Handler
|
200
|
+
-----------------------
|
201
|
+
|
202
|
+
Included in this gem is a library called `ernie` that makes it easy to write
|
203
|
+
Ernie handlers in Ruby. All you have to do is write a standard Ruby module and
|
204
|
+
expose it to Ernie and the functions of that module will become available to
|
205
|
+
BERT-RPC clients.
|
206
|
+
|
207
|
+
### Example
|
208
|
+
|
209
|
+
Using a Ruby module and Ernie.expose:
|
210
|
+
|
211
|
+
require 'rubygems'
|
212
|
+
require 'ernie'
|
213
|
+
|
214
|
+
module Ext
|
215
|
+
def add(a, b)
|
216
|
+
a + b
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
Ernie.expose(:ext, Ext)
|
221
|
+
|
222
|
+
### BERT-RPC Sequence Example
|
223
|
+
|
224
|
+
-> {call, nat, add, [1, 2]}
|
225
|
+
<- {reply, 3}
|
226
|
+
|
227
|
+
### Logging
|
228
|
+
|
229
|
+
You can have logging sent to a file by adding these lines to your handler:
|
230
|
+
|
231
|
+
logfile('/var/log/ernie.log')
|
232
|
+
loglevel(Logger::INFO)
|
233
|
+
|
234
|
+
This will log startup info, requests, and error messages to the log. Choosing
|
235
|
+
Logger::DEBUG will include the response (be careful, doing this can generate
|
236
|
+
very large log files).
|
237
|
+
|
238
|
+
### Autostart
|
239
|
+
|
240
|
+
Normally Ruby Ernie handlers will become active after the file has been loaded
|
241
|
+
in. you can disable this behavior by setting:
|
242
|
+
|
243
|
+
Ernie.auto_start = false
|
244
|
+
|
245
|
+
|
246
|
+
Selecting Queue Priority
|
247
|
+
------------------------
|
248
|
+
|
249
|
+
Ernie maintains High and Low priority queues for incoming connections. If
|
250
|
+
there are any connections in the High priority queue, they will always be
|
251
|
+
processed first. If the High priority queue is empty, connections will be
|
252
|
+
processed from the Low priority queue. By default, connections go into the
|
253
|
+
High priority queue. To select a queue, an info BERP of the following form
|
254
|
+
must be sent preceding the call.
|
255
|
+
|
256
|
+
-- {info, priority, Priority}
|
257
|
+
|
258
|
+
Where `Priority` is either the `high` or `low` atom. An example sequence where
|
259
|
+
the low priority queue is being selected would look like the following.
|
260
|
+
|
261
|
+
-> {info, priority, low}
|
262
|
+
-> {call, nat, add, [1, 2]}
|
263
|
+
<- {reply, 3}
|
264
|
+
|
265
|
+
|
266
|
+
Using the BERTRPC gem to make calls to Ernie
|
267
|
+
--------------------------------------------
|
268
|
+
|
269
|
+
You can make BERT-RPC calls from Ruby with the [BERTRPC gem](http://github.com/mojombo/bertrpc):
|
270
|
+
|
271
|
+
require 'bertrpc'
|
272
|
+
|
273
|
+
svc = BERTRPC::Service.new('localhost', 8000)
|
274
|
+
svc.call.ext.add(1, 2)
|
275
|
+
# => 3
|
276
|
+
|
277
|
+
|
278
|
+
Contribute
|
279
|
+
----------
|
280
|
+
|
281
|
+
If you'd like to hack on Ernie, start by forking my repo on GitHub:
|
282
|
+
|
283
|
+
http://github.com/mojombo/ernie
|
284
|
+
|
285
|
+
To get all of the dependencies, install the gem first. To run ernie from
|
286
|
+
source, you must first build the Erlang code:
|
287
|
+
|
288
|
+
rake ebuild
|
289
|
+
|
290
|
+
The best way to get your changes merged back into core is as follows:
|
291
|
+
|
292
|
+
1. Clone down your fork
|
293
|
+
1. Create a topic branch to contain your change
|
294
|
+
1. Hack away
|
295
|
+
1. Add tests and make sure everything still passes by running `rake`
|
296
|
+
1. If you are adding new functionality, document it in the README.md
|
297
|
+
1. Do not change the version number, I will do that on my end
|
298
|
+
1. If necessary, rebase your commits into logical chunks, without errors
|
299
|
+
1. Push the branch up to GitHub
|
300
|
+
1. Send me (mojombo) a pull request for your branch
|
301
|
+
|
302
|
+
|
303
|
+
Copyright
|
304
|
+
---------
|
305
|
+
|
306
|
+
Copyright (c) 2009 Tom Preston-Werner. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ernie"
|
8
|
+
gem.rubyforge_project = "ernie"
|
9
|
+
gem.summary = %Q{Ernie is a BERT-RPC server implementation.}
|
10
|
+
gem.description = %Q{Ernie is an Erlang/Ruby hybrid BERT-RPC server implementation packaged as a gem.}
|
11
|
+
gem.email = "tom@mojombo.com"
|
12
|
+
gem.homepage = "http://github.com/mojombo/ernie"
|
13
|
+
gem.authors = ["Tom Preston-Werner"]
|
14
|
+
gem.files.include(["ext"])
|
15
|
+
gem.extensions << 'ext/extconf.rb'
|
16
|
+
gem.add_dependency('bert', '>= 1.1.0')
|
17
|
+
gem.add_dependency('bertrpc', '>= 1.0.0')
|
18
|
+
|
19
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'rake/testtask'
|
26
|
+
Rake::TestTask.new(:test) do |test|
|
27
|
+
test.libs << 'lib' << 'test'
|
28
|
+
test.pattern = 'test/**/*_test.rb'
|
29
|
+
test.verbose = true
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
require 'rcov/rcovtask'
|
34
|
+
Rcov::RcovTask.new do |test|
|
35
|
+
test.libs << 'test'
|
36
|
+
test.pattern = 'test/**/*_test.rb'
|
37
|
+
test.verbose = true
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
task :rcov do
|
41
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default => :test
|
46
|
+
|
47
|
+
# require 'rake/rdoctask'
|
48
|
+
# Rake::RDocTask.new do |rdoc|
|
49
|
+
# if File.exist?('VERSION.yml')
|
50
|
+
# config = YAML.load(File.read('VERSION.yml'))
|
51
|
+
# version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
52
|
+
# else
|
53
|
+
# version = ""
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# rdoc.rdoc_dir = 'rdoc'
|
57
|
+
# rdoc.title = "ernie #{version}"
|
58
|
+
# rdoc.rdoc_files.include('README*')
|
59
|
+
# rdoc.rdoc_files.include('lib/**/*.rb')
|
60
|
+
# end
|
61
|
+
|
62
|
+
task :ebuild do
|
63
|
+
ERLC_TEST_FLAGS = ""
|
64
|
+
ERLC_FLAGS = "-o ../ebin"
|
65
|
+
cd "elib"
|
66
|
+
sh "erlc #{ERLC_FLAGS} #{ERLC_TEST_FLAGS} #{Dir["**/*.erl"].join(" ")}"
|
67
|
+
end
|
data/VERSION.yml
ADDED
data/bin/ernie
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), *%w[.. lib]))
|
4
|
+
ERNIE_ROOT = File.join(File.dirname(__FILE__), *%w[..])
|
5
|
+
|
6
|
+
DEFAULT_ERLANG_CODEPATHS = %w[ebin]
|
7
|
+
DEFAULT_PORT = 8000
|
8
|
+
|
9
|
+
def rel(path)
|
10
|
+
File.join(ERNIE_ROOT, path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def code_paths
|
14
|
+
DEFAULT_ERLANG_CODEPATHS.map {|n| "-pz #{rel(n)}" }.join(" ") + " \\"
|
15
|
+
end
|
16
|
+
|
17
|
+
def version
|
18
|
+
yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml])))
|
19
|
+
"#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'optparse'
|
23
|
+
require 'pp'
|
24
|
+
require 'yaml'
|
25
|
+
|
26
|
+
help = <<HELP
|
27
|
+
Ernie is an Erlang/Ruby BERT-RPC Server.
|
28
|
+
|
29
|
+
Basic Command Line Usage:
|
30
|
+
ernie [command] [options]
|
31
|
+
|
32
|
+
Commands:
|
33
|
+
<none> Start an Ernie server.
|
34
|
+
reload-handlers Gracefully reload all of the the ruby handlers
|
35
|
+
and use the new code for all subsequent requests.
|
36
|
+
|
37
|
+
Options:
|
38
|
+
HELP
|
39
|
+
|
40
|
+
options = {}
|
41
|
+
OptionParser.new do |opts|
|
42
|
+
opts.banner = help
|
43
|
+
opts.version = version
|
44
|
+
|
45
|
+
opts.on("-c CONFIG", "--config CONFIG", "Config file") do |x|
|
46
|
+
options[:config] = x
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on("-p PORT", "--port PORT", "Port") do |x|
|
50
|
+
options[:port] = x
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("-l LOGLEVEL", "--log-level LOGLEVEL", "Log level (0-4)") do |x|
|
54
|
+
options[:log_level] = x
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("-a LOGFILE", "--access-log LOGFILE", "Access log file") do |x|
|
58
|
+
options[:access_log] = x
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("-d", "--detached", "Run as a daemon") do
|
62
|
+
options[:detached] = true
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on("-P", "--pidfile PIDFILE", "Location to write pid file.") do |x|
|
66
|
+
options[:pidfile] = x
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on("--name NAME", "Erlang node name") do |x|
|
70
|
+
options[:name] = x
|
71
|
+
end
|
72
|
+
end.parse!
|
73
|
+
|
74
|
+
if command = ARGV[0]
|
75
|
+
if !%w{reload-handlers stats}.include?(command)
|
76
|
+
puts "Invlalid command. Valid commands are:"
|
77
|
+
puts " reload-handlers"
|
78
|
+
puts " stats"
|
79
|
+
exit(1)
|
80
|
+
end
|
81
|
+
|
82
|
+
require 'rubygems'
|
83
|
+
require 'bertrpc'
|
84
|
+
port = options[:port] || DEFAULT_PORT
|
85
|
+
svc = BERTRPC::Service.new('localhost', port)
|
86
|
+
puts svc.call.__admin__.send(command.gsub(/-/, '_'))
|
87
|
+
else
|
88
|
+
if !options[:config]
|
89
|
+
puts "A config file must be specified: ernie -c /path/to/config.yml"
|
90
|
+
exit(1)
|
91
|
+
end
|
92
|
+
|
93
|
+
config = options[:config]
|
94
|
+
port = options[:port] || DEFAULT_PORT
|
95
|
+
log_level = options[:log_level] || 2
|
96
|
+
pidfile = options[:pidfile] ? "-ernie_server_app pidfile \"'#{options[:pidfile]}'\"" : ''
|
97
|
+
detached = options[:detached] ? '-detached' : ''
|
98
|
+
access_log = options[:access_log] ? "-ernie_server_app access_log '\"#{options[:access_log]}\"'" : ''
|
99
|
+
name = options[:name] ? "-name '#{options[:name]}'" : ''
|
100
|
+
|
101
|
+
cmd = %Q{erl -boot start_sasl \
|
102
|
+
#{detached} \
|
103
|
+
+Bc \
|
104
|
+
+K true \
|
105
|
+
-smp enable \
|
106
|
+
#{code_paths}
|
107
|
+
#{pidfile} \
|
108
|
+
#{access_log} \
|
109
|
+
#{name} \
|
110
|
+
-ernie_server_app port #{port} \
|
111
|
+
-ernie_server_app config '"#{config}"' \
|
112
|
+
-ernie_server_app log_level #{log_level} \
|
113
|
+
-run ernie_server_app boot}.squeeze(' ')
|
114
|
+
puts cmd
|
115
|
+
exec(cmd)
|
116
|
+
end
|
data/contrib/ebench.erl
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
% erlc *.erl && erl ebench.beam -run ebench start 10000 20 ext add
|
2
|
+
|
3
|
+
-module(ebench).
|
4
|
+
-export([start/1]).
|
5
|
+
|
6
|
+
start([Ni, Ci, Modi, Funi]) ->
|
7
|
+
Nt = list_to_integer(Ni),
|
8
|
+
C = list_to_integer(Ci),
|
9
|
+
Mod = list_to_atom(Modi),
|
10
|
+
Fun = list_to_atom(Funi),
|
11
|
+
N = round(Nt / C),
|
12
|
+
T0 = erlang:now(),
|
13
|
+
Waiter = spawn(fun() -> wait(T0, N * C) end),
|
14
|
+
spawner(Waiter, N, C, Mod, Fun).
|
15
|
+
|
16
|
+
spawner(_Waiter, _N, 0, _Mod, _Fun) ->
|
17
|
+
ok;
|
18
|
+
spawner(Waiter, N, C, Mod, Fun) ->
|
19
|
+
spawn(fun() -> loop(Waiter, N, Mod, Fun) end),
|
20
|
+
spawner(Waiter, N, C - 1, Mod, Fun).
|
21
|
+
|
22
|
+
% X is the total number of responses to wait for
|
23
|
+
wait(T0, XTotal, 0) ->
|
24
|
+
T1 = erlang:now(),
|
25
|
+
Diff = timer:now_diff(T1, T0),
|
26
|
+
Mean = Diff / XTotal,
|
27
|
+
io:format("~p requests completed in ~.2fs~n", [XTotal, Diff / 1000000]),
|
28
|
+
io:format("Mean request time: ~.2fms (~.2f r/s)~n", [Mean / 1000, XTotal / (Diff / 1000000)]),
|
29
|
+
init:stop();
|
30
|
+
wait(T0, XTotal, X) ->
|
31
|
+
receive
|
32
|
+
done -> wait(T0, XTotal, X - 1)
|
33
|
+
end.
|
34
|
+
|
35
|
+
wait(T0, X) ->
|
36
|
+
wait(T0, X, X).
|
37
|
+
|
38
|
+
loop(_Waiter, 0, _Mod, _Fun) ->
|
39
|
+
ok;
|
40
|
+
loop(Waiter, N, Mod, Fun) ->
|
41
|
+
hit(Waiter, Mod, Fun),
|
42
|
+
loop(Waiter, N - 1, Mod, Fun).
|
43
|
+
|
44
|
+
hit(Waiter, Mod, Fun) ->
|
45
|
+
% io:format("outgoing!~n", []),
|
46
|
+
Host = "localhost",
|
47
|
+
case gen_tcp:connect(Host, 8000, [binary, {packet, 4}]) of
|
48
|
+
{ok, Sock} -> process(Waiter, Mod, Fun, Sock);
|
49
|
+
Any ->
|
50
|
+
io:format("Unable to establish connection: ~p~n", [Any]),
|
51
|
+
Waiter ! done
|
52
|
+
end.
|
53
|
+
|
54
|
+
process(Waiter, Mod, Fun, Sock) ->
|
55
|
+
% Info = term_to_binary({info, priority, [low]}),
|
56
|
+
% ok = gen_tcp:send(Sock, Info),
|
57
|
+
Request = term_to_binary({call, Mod, Fun, args(Fun)}),
|
58
|
+
ok = gen_tcp:send(Sock, Request),
|
59
|
+
receive
|
60
|
+
{tcp, _Port, Reply} ->
|
61
|
+
% io:format("~p~n", [Reply]),
|
62
|
+
Res = res(Fun),
|
63
|
+
{reply, Res} = binary_to_term(Reply);
|
64
|
+
{tcp_closed, Port} ->
|
65
|
+
io:format("Connection closed after sending data: ~p~n", [Port]);
|
66
|
+
Any ->
|
67
|
+
io:format("Unexpected message: ~p~n", [Any])
|
68
|
+
end,
|
69
|
+
Waiter ! done,
|
70
|
+
ok = gen_tcp:close(Sock).
|
71
|
+
|
72
|
+
args(add) -> [1, 2];
|
73
|
+
args(fib) -> [20].
|
74
|
+
|
75
|
+
res(add) -> 3;
|
76
|
+
res(fib) -> 10946.
|