schleyfox-ernie 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.
|