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/elib/asset_pool.erl
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
-module(asset_pool).
|
2
|
+
-behaviour(gen_server).
|
3
|
+
|
4
|
+
%% api
|
5
|
+
-export([start_link/2, lease/1, return/2, reload_assets/1, idle_worker_count/1]).
|
6
|
+
|
7
|
+
%% gen_server callbacks
|
8
|
+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
9
|
+
terminate/2, code_change/3]).
|
10
|
+
|
11
|
+
-record(state, {assets = undefined,
|
12
|
+
handler = undefined,
|
13
|
+
token = undefined}).
|
14
|
+
|
15
|
+
%%====================================================================
|
16
|
+
%% API
|
17
|
+
%%====================================================================
|
18
|
+
|
19
|
+
start_link(Handler, Count) ->
|
20
|
+
gen_server:start_link(?MODULE, [Handler, Count], []).
|
21
|
+
|
22
|
+
lease(Pid) ->
|
23
|
+
gen_server:call(Pid, lease).
|
24
|
+
|
25
|
+
return(Pid, Asset) ->
|
26
|
+
gen_server:call(Pid, {return, Asset}).
|
27
|
+
|
28
|
+
reload_assets(Pid) ->
|
29
|
+
gen_server:call(Pid, reload_assets).
|
30
|
+
|
31
|
+
idle_worker_count(Pid) ->
|
32
|
+
gen_server:call(Pid, idle_worker_count).
|
33
|
+
|
34
|
+
%%====================================================================
|
35
|
+
%% gen_server callbacks
|
36
|
+
%%====================================================================
|
37
|
+
|
38
|
+
%%--------------------------------------------------------------------
|
39
|
+
%% Function: init(Args) -> {ok, State} |
|
40
|
+
%% {ok, State, Timeout} |
|
41
|
+
%% ignore |
|
42
|
+
%% {stop, Reason}
|
43
|
+
%% Description: Initiates the server
|
44
|
+
%%--------------------------------------------------------------------
|
45
|
+
init([Handler, Count]) ->
|
46
|
+
process_flag(trap_exit, true),
|
47
|
+
error_logger:info_msg("~p starting~n", [?MODULE]),
|
48
|
+
Token = make_ref(),
|
49
|
+
Assets = start_handlers(Count, Handler, Token),
|
50
|
+
logger:debug("Assets = ~p~n", [Assets]),
|
51
|
+
{ok, #state{assets = Assets, handler = Handler, token = Token}}.
|
52
|
+
|
53
|
+
%%--------------------------------------------------------------------
|
54
|
+
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
55
|
+
%% {reply, Reply, State, Timeout} |
|
56
|
+
%% {noreply, State} |
|
57
|
+
%% {noreply, State, Timeout} |
|
58
|
+
%% {stop, Reason, Reply, State} |
|
59
|
+
%% {stop, Reason, State}
|
60
|
+
%% Description: Handling call messages
|
61
|
+
%%--------------------------------------------------------------------
|
62
|
+
handle_call(lease, _From, State) ->
|
63
|
+
logger:debug("Leasing...~n", []),
|
64
|
+
Token = State#state.token,
|
65
|
+
case queue:out(State#state.assets) of
|
66
|
+
{{value, Asset}, Assets2} ->
|
67
|
+
{asset, Port, AssetToken} = Asset,
|
68
|
+
case AssetToken =:= Token of
|
69
|
+
false ->
|
70
|
+
port_wrapper:close(Port),
|
71
|
+
Handler = State#state.handler,
|
72
|
+
NewAsset = create_asset(Handler, Token);
|
73
|
+
true ->
|
74
|
+
NewAsset = Asset
|
75
|
+
end,
|
76
|
+
{reply, {ok, NewAsset}, State#state{assets = Assets2}};
|
77
|
+
{empty, _Assets2} ->
|
78
|
+
{reply, empty, State}
|
79
|
+
end;
|
80
|
+
handle_call({return, Asset}, _From, State) ->
|
81
|
+
Token = State#state.token,
|
82
|
+
{asset, Port, AssetToken} = Asset,
|
83
|
+
case AssetToken =:= Token of
|
84
|
+
false ->
|
85
|
+
port_wrapper:close(Port),
|
86
|
+
Handler = State#state.handler,
|
87
|
+
NewAsset = create_asset(Handler, Token);
|
88
|
+
true ->
|
89
|
+
NewAsset = Asset
|
90
|
+
end,
|
91
|
+
Assets2 = queue:in(NewAsset, State#state.assets),
|
92
|
+
{reply, ok, State#state{assets = Assets2}};
|
93
|
+
handle_call(reload_assets, _From, State) ->
|
94
|
+
Token = make_ref(),
|
95
|
+
{reply, ok, State#state{token = Token}};
|
96
|
+
handle_call(idle_worker_count, _From, State) ->
|
97
|
+
WorkerCount = queue:len(State#state.assets),
|
98
|
+
{reply, WorkerCount, State};
|
99
|
+
handle_call(_Request, _From, State) ->
|
100
|
+
{reply, ok, State}.
|
101
|
+
|
102
|
+
%%--------------------------------------------------------------------
|
103
|
+
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
104
|
+
%% {noreply, State, Timeout} |
|
105
|
+
%% {stop, Reason, State}
|
106
|
+
%% Description: Handling cast messages
|
107
|
+
%%--------------------------------------------------------------------
|
108
|
+
handle_cast(_Msg, State) -> {noreply, State}.
|
109
|
+
|
110
|
+
handle_info({'EXIT', _Pid, normal}, State) ->
|
111
|
+
{noreply, State};
|
112
|
+
handle_info({'EXIT', Pid, Error}, State) ->
|
113
|
+
error_logger:error_msg("Port ~p closed with ~p, restarting port...~n", [Pid, Error]),
|
114
|
+
ValidAssets = queue:filter(fun(Item) -> {asset, A, _T} = Item, A =/= Pid end, State#state.assets),
|
115
|
+
Handler = State#state.handler,
|
116
|
+
Token = State#state.token,
|
117
|
+
NewAsset = create_asset(Handler, Token),
|
118
|
+
Assets = queue:in(NewAsset, ValidAssets),
|
119
|
+
{noreply, State#state{assets = Assets}};
|
120
|
+
handle_info(Msg, State) ->
|
121
|
+
error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
|
122
|
+
{noreply, State}.
|
123
|
+
|
124
|
+
terminate(_Reason, _State) -> ok.
|
125
|
+
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
126
|
+
|
127
|
+
%%====================================================================
|
128
|
+
%% Internal
|
129
|
+
%%====================================================================
|
130
|
+
|
131
|
+
start_handlers(Count, Handler, Token) ->
|
132
|
+
start_handlers(queue:new(), Count, Handler, Token).
|
133
|
+
|
134
|
+
start_handlers(Assets, 0, _Handler, _Token) ->
|
135
|
+
Assets;
|
136
|
+
start_handlers(Assets, Count, Handler, Token) ->
|
137
|
+
Asset = create_asset(Handler, Token),
|
138
|
+
Assets2 = queue:in(Asset, Assets),
|
139
|
+
start_handlers(Assets2, Count - 1, Handler, Token).
|
140
|
+
|
141
|
+
create_asset(Handler, Token) ->
|
142
|
+
Len = length(Handler),
|
143
|
+
case Len > 150 of
|
144
|
+
true -> Cmd = Handler;
|
145
|
+
false -> Cmd = lists:flatten(Handler ++ " --procline " ++ pad(150 - Len - 12))
|
146
|
+
end,
|
147
|
+
io:format("~p~n", [Cmd]),
|
148
|
+
{asset, port_wrapper:wrap_link(Cmd), Token}.
|
149
|
+
|
150
|
+
pad(Size) ->
|
151
|
+
pad(Size, []).
|
152
|
+
pad(0, Acc) ->
|
153
|
+
Acc;
|
154
|
+
pad(Size, Acc) ->
|
155
|
+
pad(Size - 1, ["x" | Acc]).
|
@@ -0,0 +1,13 @@
|
|
1
|
+
-module(asset_pool_sup).
|
2
|
+
-behaviour(supervisor).
|
3
|
+
-export([start_link/2, init/1]).
|
4
|
+
|
5
|
+
start_link(Handler, Number) ->
|
6
|
+
supervisor:start_link(?MODULE, [Handler, Number]).
|
7
|
+
|
8
|
+
init([Handler, Number]) ->
|
9
|
+
io:format("Using handler ~p~n", [Handler]),
|
10
|
+
io:format("Using ~p handler instances~n", [Number]),
|
11
|
+
{ok, {{one_for_one, 1, 60},
|
12
|
+
[{asset_pool, {asset_pool, start_link, [Handler, Number]},
|
13
|
+
permanent, brutal_kill, worker, [asset_pool]}]}}.
|
data/elib/bert.erl
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
%%% See http://github.com/mojombo/bert.erl for documentation.
|
2
|
+
%%% MIT License - Copyright (c) 2009 Tom Preston-Werner <tom@mojombo.com>
|
3
|
+
|
4
|
+
-module(bert).
|
5
|
+
-version('1.1.0').
|
6
|
+
-author("Tom Preston-Werner").
|
7
|
+
|
8
|
+
-export([encode/1, decode/1]).
|
9
|
+
|
10
|
+
-ifdef(TEST).
|
11
|
+
-include("test/bert_test.erl").
|
12
|
+
-endif.
|
13
|
+
|
14
|
+
%%---------------------------------------------------------------------------
|
15
|
+
%% Public API
|
16
|
+
|
17
|
+
-spec encode(term()) -> binary().
|
18
|
+
|
19
|
+
encode(Term) ->
|
20
|
+
term_to_binary(encode_term(Term)).
|
21
|
+
|
22
|
+
-spec decode(binary()) -> term().
|
23
|
+
|
24
|
+
decode(Bin) ->
|
25
|
+
decode_term(binary_to_term(Bin)).
|
26
|
+
|
27
|
+
%%---------------------------------------------------------------------------
|
28
|
+
%% Encode
|
29
|
+
|
30
|
+
-spec encode_term(term()) -> term().
|
31
|
+
|
32
|
+
encode_term(Term) ->
|
33
|
+
case Term of
|
34
|
+
[] -> {bert, nil};
|
35
|
+
true -> {bert, true};
|
36
|
+
false -> {bert, false};
|
37
|
+
Dict when is_record(Term, dict, 8) ->
|
38
|
+
{bert, dict, dict:to_list(Dict)};
|
39
|
+
List when is_list(Term) ->
|
40
|
+
lists:map((fun encode_term/1), List);
|
41
|
+
Tuple when is_tuple(Term) ->
|
42
|
+
TList = tuple_to_list(Tuple),
|
43
|
+
TList2 = lists:map((fun encode_term/1), TList),
|
44
|
+
list_to_tuple(TList2);
|
45
|
+
_Else -> Term
|
46
|
+
end.
|
47
|
+
|
48
|
+
%%---------------------------------------------------------------------------
|
49
|
+
%% Decode
|
50
|
+
|
51
|
+
-spec decode_term(term()) -> term().
|
52
|
+
|
53
|
+
decode_term(Term) ->
|
54
|
+
case Term of
|
55
|
+
{bert, nil} -> [];
|
56
|
+
{bert, true} -> true;
|
57
|
+
{bert, false} -> false;
|
58
|
+
{bert, dict, Dict} ->
|
59
|
+
dict:from_list(Dict);
|
60
|
+
{bert, Other} ->
|
61
|
+
{bert, Other};
|
62
|
+
List when is_list(Term) ->
|
63
|
+
lists:map((fun decode_term/1), List);
|
64
|
+
Tuple when is_tuple(Term) ->
|
65
|
+
TList = tuple_to_list(Tuple),
|
66
|
+
TList2 = lists:map((fun decode_term/1), TList),
|
67
|
+
list_to_tuple(TList2);
|
68
|
+
_Else -> Term
|
69
|
+
end.
|
data/elib/ernie.hrl
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
-record(state, {lsock = undefined, % the listen socket
|
2
|
+
hq = queue:new(), % high priority queue
|
3
|
+
lq = queue:new(), % low priority queue
|
4
|
+
count = 0, % total request count
|
5
|
+
map = undefined}). % module map. tuples of {Mod, Id}
|
6
|
+
|
7
|
+
-record(request, {sock = undefined, % connection socket
|
8
|
+
log = undefined, % log information
|
9
|
+
infos = [], % list of info binaries
|
10
|
+
action = undefined, % action binary
|
11
|
+
priority = high}). % priority [ high | low ]
|
12
|
+
|
13
|
+
-record(log, {taccept = erlang:now(), % time that connection was accepted
|
14
|
+
tprocess = erlang:now(), % time that processing started
|
15
|
+
tdone = erlang:now(), % time that processing and response is done
|
16
|
+
hq = 0, % size of high queue at acceptance
|
17
|
+
lq = 0, % size of low queue at acceptance
|
18
|
+
type = unk}). % type [ unk | nat | ext ]
|
@@ -0,0 +1,170 @@
|
|
1
|
+
-module(ernie_access_logger).
|
2
|
+
-behaviour(gen_server).
|
3
|
+
|
4
|
+
%% api
|
5
|
+
-export([start_link/1, start/1, acc/1, err/3, reopen/0]).
|
6
|
+
|
7
|
+
%% gen_server callbacks
|
8
|
+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
9
|
+
terminate/2, code_change/3]).
|
10
|
+
|
11
|
+
-include_lib("ernie.hrl").
|
12
|
+
|
13
|
+
-record(lstate, {access_file_name = undefined,
|
14
|
+
access_file = undefined}).
|
15
|
+
|
16
|
+
%%====================================================================
|
17
|
+
%% API
|
18
|
+
%%====================================================================
|
19
|
+
|
20
|
+
start_link(Args) ->
|
21
|
+
gen_server:start_link({global, ?MODULE}, ?MODULE, Args, []).
|
22
|
+
|
23
|
+
start(Args) ->
|
24
|
+
gen_server:start({global, ?MODULE}, ?MODULE, Args, []).
|
25
|
+
|
26
|
+
acc(Request) ->
|
27
|
+
gen_server:cast({global, ?MODULE}, {acc, Request}).
|
28
|
+
|
29
|
+
err(Request, Msg, Args) ->
|
30
|
+
gen_server:cast({global, ?MODULE}, {err, Request, Msg, Args}).
|
31
|
+
|
32
|
+
reopen() ->
|
33
|
+
gen_server:cast({global, ?MODULE}, reopen).
|
34
|
+
|
35
|
+
%%====================================================================
|
36
|
+
%% gen_server callbacks
|
37
|
+
%%====================================================================
|
38
|
+
|
39
|
+
%%--------------------------------------------------------------------
|
40
|
+
%% Function: init(Args) -> {ok, State} |
|
41
|
+
%% {ok, State, Timeout} |
|
42
|
+
%% ignore |
|
43
|
+
%% {stop, Reason}
|
44
|
+
%% Description: Initiates the server
|
45
|
+
%%--------------------------------------------------------------------
|
46
|
+
init([undefined]) ->
|
47
|
+
error_logger:info_msg("~p starting~n", [?MODULE]),
|
48
|
+
{ok, #lstate{}};
|
49
|
+
init([AccessFileName]) ->
|
50
|
+
error_logger:info_msg("~p starting~n", [?MODULE]),
|
51
|
+
case file:open(AccessFileName, [append]) of
|
52
|
+
{ok, AccessFile} ->
|
53
|
+
{ok, _T} = timer:apply_interval(10000, ernie_access_logger, reopen, []),
|
54
|
+
{ok, #lstate{access_file_name = AccessFileName,
|
55
|
+
access_file = AccessFile}};
|
56
|
+
{error, Error} ->
|
57
|
+
error_logger:error_msg("Error opening access log ~p: ~p.~n", [AccessFileName, Error]),
|
58
|
+
{ok, #lstate{}}
|
59
|
+
end.
|
60
|
+
|
61
|
+
%%--------------------------------------------------------------------
|
62
|
+
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
63
|
+
%% {reply, Reply, State, Timeout} |
|
64
|
+
%% {noreply, State} |
|
65
|
+
%% {noreply, State, Timeout} |
|
66
|
+
%% {stop, Reason, Reply, State} |
|
67
|
+
%% {stop, Reason, State}
|
68
|
+
%% Description: Handling call messages
|
69
|
+
%%--------------------------------------------------------------------
|
70
|
+
handle_call(_Request, _From, State) ->
|
71
|
+
{reply, ok, State}.
|
72
|
+
|
73
|
+
%%--------------------------------------------------------------------
|
74
|
+
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
75
|
+
%% {noreply, State, Timeout} |
|
76
|
+
%% {stop, Reason, State}
|
77
|
+
%% Description: Handling cast messages
|
78
|
+
%%--------------------------------------------------------------------
|
79
|
+
handle_cast({acc, Request}, State) ->
|
80
|
+
case State#lstate.access_file_name of
|
81
|
+
undefined -> ok;
|
82
|
+
_AccessFilename -> acc(Request, State)
|
83
|
+
end,
|
84
|
+
{noreply, State};
|
85
|
+
handle_cast({err, Request, Msg, Args}, State) ->
|
86
|
+
case State#lstate.access_file_name of
|
87
|
+
undefined -> ok;
|
88
|
+
_AccessFilename -> err(Request, Msg, Args, State)
|
89
|
+
end,
|
90
|
+
{noreply, State};
|
91
|
+
handle_cast(reopen, State) ->
|
92
|
+
case State#lstate.access_file_name of
|
93
|
+
undefined ->
|
94
|
+
{noreply, State};
|
95
|
+
AccessFileName ->
|
96
|
+
case file:read_file_info(AccessFileName) of
|
97
|
+
{ok, _FileInfo} ->
|
98
|
+
{noreply, State};
|
99
|
+
{error, enoent} ->
|
100
|
+
ok = file:close(State#lstate.access_file),
|
101
|
+
{ok, AccessFile} = file:open(AccessFileName, [append]),
|
102
|
+
{noreply, State#lstate{access_file = AccessFile}};
|
103
|
+
_OtherError ->
|
104
|
+
{noreply, #lstate{}}
|
105
|
+
end
|
106
|
+
end;
|
107
|
+
handle_cast(_Msg, State) ->
|
108
|
+
{noreply, State}.
|
109
|
+
|
110
|
+
handle_info(Msg, State) ->
|
111
|
+
error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
|
112
|
+
{noreply, State}.
|
113
|
+
|
114
|
+
terminate(_Reason, _State) -> ok.
|
115
|
+
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
116
|
+
|
117
|
+
%%====================================================================
|
118
|
+
%% Internal
|
119
|
+
%%====================================================================
|
120
|
+
|
121
|
+
acc(Request, State) ->
|
122
|
+
StatString = stat_string(Request),
|
123
|
+
ActionString = action_string(Request),
|
124
|
+
Line = io_lib:fwrite("ACC ~s - ~s~n", [StatString, ActionString]),
|
125
|
+
file:write(State#lstate.access_file, Line).
|
126
|
+
|
127
|
+
err(Request, Msg, Args, State) ->
|
128
|
+
StatString = stat_string(Request),
|
129
|
+
ActionString = action_string(Request),
|
130
|
+
ErrString = io_lib:fwrite(Msg, Args),
|
131
|
+
Line = io_lib:fwrite("ERR ~s - ~s : ~s~n", [StatString, ErrString, ActionString]),
|
132
|
+
file:write(State#lstate.access_file, Line).
|
133
|
+
|
134
|
+
stat_string(Request) ->
|
135
|
+
Log = Request#request.log,
|
136
|
+
TAccept = time_tuple_to_iso_8601_date(Log#log.taccept),
|
137
|
+
D1 = time_difference_in_seconds(Log#log.taccept, Log#log.tprocess),
|
138
|
+
D2 = time_difference_in_seconds(Log#log.tprocess, Log#log.tdone),
|
139
|
+
Type = Log#log.type,
|
140
|
+
HQ = Log#log.hq,
|
141
|
+
LQ = Log#log.lq,
|
142
|
+
Prio = Request#request.priority,
|
143
|
+
Args = [TAccept, D1, D2, HQ, LQ, Type, Prio],
|
144
|
+
io_lib:fwrite("[~s] ~f ~f - ~B ~B ~3s ~p", Args).
|
145
|
+
|
146
|
+
action_string(Request) ->
|
147
|
+
TermAction = binary_to_term(Request#request.action),
|
148
|
+
RawAction = lists:flatten(io_lib:fwrite("~1000000000.0.0p", [TermAction])),
|
149
|
+
case string:len(RawAction) > 150 of
|
150
|
+
true ->
|
151
|
+
Action = re:replace(RawAction, "\n", "", [global, {return, list}]),
|
152
|
+
[string:sub_string(Action, 1, 150), "..."];
|
153
|
+
false ->
|
154
|
+
RawAction
|
155
|
+
end.
|
156
|
+
|
157
|
+
time_tuple_to_iso_8601_date(TimeTuple) ->
|
158
|
+
{{YY, MM, DD}, {H, M, S}} = calendar:now_to_local_time(TimeTuple),
|
159
|
+
{_MegaSecs, _Secs, MicroSecs} = TimeTuple,
|
160
|
+
Args = [YY, MM, DD, H, M, S, MicroSecs],
|
161
|
+
io_lib:fwrite("~4B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B.~-6.10.0B", Args).
|
162
|
+
|
163
|
+
time_difference_in_seconds(T1, T2) ->
|
164
|
+
{_, _, MS1} = T1,
|
165
|
+
{_, _, MS2} = T2,
|
166
|
+
S1 = calendar:datetime_to_gregorian_seconds(calendar:now_to_local_time(T1)),
|
167
|
+
S2 = calendar:datetime_to_gregorian_seconds(calendar:now_to_local_time(T2)),
|
168
|
+
F1 = S1 + (MS1 / 1000000),
|
169
|
+
F2 = S2 + (MS2 / 1000000),
|
170
|
+
F2 - F1.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
-module(ernie_access_logger_sup).
|
2
|
+
-behaviour(supervisor).
|
3
|
+
-export([start_link/1, init/1]).
|
4
|
+
|
5
|
+
start_link(AccessLog) ->
|
6
|
+
supervisor:start_link({local, ?MODULE}, ?MODULE, [AccessLog]).
|
7
|
+
|
8
|
+
init([AccessLog]) ->
|
9
|
+
case AccessLog of
|
10
|
+
undefined -> io:format("No access log~n", []);
|
11
|
+
Any -> io:format("Using access log ~p~n", [Any])
|
12
|
+
end,
|
13
|
+
{ok, {{one_for_one, 1, 60},
|
14
|
+
[{ernie_access_logger, {ernie_access_logger, start_link, [[AccessLog]]},
|
15
|
+
permanent, brutal_kill, worker, [ernie_access_logger]}]}}.
|
@@ -0,0 +1,60 @@
|
|
1
|
+
-module(ernie_admin).
|
2
|
+
-export([process/4]).
|
3
|
+
-include_lib("ernie.hrl").
|
4
|
+
|
5
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
6
|
+
% Process entry point
|
7
|
+
|
8
|
+
process(Sock, reload_handlers, _Args, State) ->
|
9
|
+
spawn(fun() -> process_reload_assets(Sock, State) end),
|
10
|
+
State;
|
11
|
+
process(Sock, stats, _Args, State) ->
|
12
|
+
spawn(fun() -> process_stats(Sock, State) end),
|
13
|
+
State;
|
14
|
+
process(Sock, _Fun, _Args, State) ->
|
15
|
+
gen_tcp:send(Sock, term_to_binary({reply, <<"Admin function not supported.">>})),
|
16
|
+
ok = gen_tcp:close(Sock),
|
17
|
+
State.
|
18
|
+
|
19
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
20
|
+
% Reload handlers
|
21
|
+
|
22
|
+
process_reload_assets(Sock, State) ->
|
23
|
+
lists:map((fun reload/1), State#state.map),
|
24
|
+
gen_tcp:send(Sock, term_to_binary({reply, <<"Handlers reloaded.">>})),
|
25
|
+
ok = gen_tcp:close(Sock).
|
26
|
+
|
27
|
+
reload({_Mod, native}) ->
|
28
|
+
ok;
|
29
|
+
reload({_Mod, Pid}) ->
|
30
|
+
asset_pool:reload_assets(Pid).
|
31
|
+
|
32
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
33
|
+
% Stats
|
34
|
+
|
35
|
+
process_stats(Sock, State) ->
|
36
|
+
CountString = stat(count, State),
|
37
|
+
IdleWorkersString = stat(idle, State),
|
38
|
+
QueueLengthString = stat(queue, State),
|
39
|
+
StatString = list_to_binary([CountString, IdleWorkersString, QueueLengthString]),
|
40
|
+
Data = term_to_binary({reply, StatString}),
|
41
|
+
gen_tcp:send(Sock, Data),
|
42
|
+
ok = gen_tcp:close(Sock).
|
43
|
+
|
44
|
+
stat(count, State) ->
|
45
|
+
Count = State#state.count,
|
46
|
+
list_to_binary([<<"connections.total=">>, integer_to_list(Count), <<"\n">>]);
|
47
|
+
stat(idle, State) ->
|
48
|
+
IdleMap = lists:map((fun idle/1), State#state.map),
|
49
|
+
list_to_binary(IdleMap);
|
50
|
+
stat(queue, State) ->
|
51
|
+
HighQueueLength = queue:len(State#state.hq),
|
52
|
+
LowQueueLength = queue:len(State#state.lq),
|
53
|
+
list_to_binary([<<"queue.high=">>, integer_to_list(HighQueueLength), <<"\n">>,
|
54
|
+
<<"queue.low=">>, integer_to_list(LowQueueLength), <<"\n">>]).
|
55
|
+
|
56
|
+
idle({Mod, native}) ->
|
57
|
+
list_to_binary([<<"workers.idle.">>, atom_to_list(Mod), <<"=native\n">>]);
|
58
|
+
idle({Mod, Pid}) ->
|
59
|
+
IdleCount = integer_to_list(asset_pool:idle_worker_count(Pid)),
|
60
|
+
list_to_binary([<<"workers.idle.">>, atom_to_list(Mod), <<"=">>, IdleCount, <<"\n">>]).
|
@@ -0,0 +1,30 @@
|
|
1
|
+
-module(ernie_config).
|
2
|
+
-export([load/1]).
|
3
|
+
|
4
|
+
load(ConfigFile) ->
|
5
|
+
{ok, Configs} = file:consult(ConfigFile),
|
6
|
+
Configs2 = lists:map((fun load_single/1), Configs),
|
7
|
+
{ok, Configs2}.
|
8
|
+
|
9
|
+
load_single(Config) ->
|
10
|
+
case proplists:get_value(type, Config) of
|
11
|
+
native ->
|
12
|
+
verify(native, Config),
|
13
|
+
CodePaths = proplists:get_value(codepaths, Config),
|
14
|
+
lists:map((fun code:add_patha/1), CodePaths),
|
15
|
+
Mod = proplists:get_value(module, Config),
|
16
|
+
code:load_file(Mod),
|
17
|
+
[{id, native} | Config];
|
18
|
+
external ->
|
19
|
+
verify(external, Config),
|
20
|
+
Handler = proplists:get_value(command, Config),
|
21
|
+
Number = proplists:get_value(count, Config),
|
22
|
+
{ok, SupPid} = asset_pool_sup:start_link(Handler, Number),
|
23
|
+
[{_Id, ChildPid, _Type, _Modules}] = supervisor:which_children(SupPid),
|
24
|
+
[{id, ChildPid} | Config]
|
25
|
+
end.
|
26
|
+
|
27
|
+
verify(native, _Config) ->
|
28
|
+
ok;
|
29
|
+
verify(external, _Config) ->
|
30
|
+
ok.
|
@@ -0,0 +1,26 @@
|
|
1
|
+
-module(ernie_native).
|
2
|
+
-export([process/2]).
|
3
|
+
-include_lib("ernie.hrl").
|
4
|
+
|
5
|
+
process(ActionTerm, Request) ->
|
6
|
+
{_Type, Mod, Fun, Args} = ActionTerm,
|
7
|
+
Sock = Request#request.sock,
|
8
|
+
logger:debug("Calling ~p:~p(~p)~n", [Mod, Fun, Args]),
|
9
|
+
try apply(Mod, Fun, Args) of
|
10
|
+
Result ->
|
11
|
+
logger:debug("Result was ~p~n", [Result]),
|
12
|
+
Data = bert:encode({reply, Result}),
|
13
|
+
gen_tcp:send(Sock, Data)
|
14
|
+
catch
|
15
|
+
error:Error ->
|
16
|
+
BError = list_to_binary(io_lib:format("~p", [Error])),
|
17
|
+
Trace = erlang:get_stacktrace(),
|
18
|
+
BTrace = lists:map(fun(X) -> list_to_binary(io_lib:format("~p", [X])) end, Trace),
|
19
|
+
Data = term_to_binary({error, [user, 0, <<"RuntimeError">>, BError, BTrace]}),
|
20
|
+
gen_tcp:send(Sock, Data)
|
21
|
+
end,
|
22
|
+
ok = gen_tcp:close(Sock),
|
23
|
+
Log = Request#request.log,
|
24
|
+
Log2 = Log#log{tdone = erlang:now()},
|
25
|
+
Request2 = Request#request{log = Log2},
|
26
|
+
ernie_access_logger:acc(Request2).
|