add file
This commit is contained in:
parent
11db16725e
commit
cb04c9c7e5
|
@ -0,0 +1 @@
|
|||
ebin/*
|
|
@ -0,0 +1,7 @@
|
|||
all: compile
|
||||
|
||||
run: compile
|
||||
erl -pa ebin -config etc/emqtt.config -s emqtt_app start
|
||||
|
||||
compile:
|
||||
rebar compile
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
Cluster Architecture
|
||||
====================
|
||||
|
||||
|
||||
Topic: Memory Copy
|
||||
|
||||
Topic ----------- Topic
|
||||
|
||||
Subscriber: Local Node
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
Erlang TCP
|
||||
=========
|
||||
|
||||
|
||||
One Million TCP Connections
|
||||
==========================
|
||||
|
||||
http://news.ycombinator.com/item?id=3028547
|
||||
|
||||
http://www.kegel.com/c10k.html
|
||||
|
||||
http://20bits.com/article/erlang-a-generalized-tcp-server
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
Direct Topic
|
||||
|
||||
or
|
||||
|
||||
Wildchar Topic?
|
||||
|
||||
|
||||
a/+/b
|
||||
a/#
|
||||
#
|
||||
|
||||
Trie Data Structure
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
|
||||
%% ex: ft=erlang ts=4 sw=4 et
|
||||
[{kernel,
|
||||
[{start_timer, true},
|
||||
{start_pg2, true}
|
||||
]},
|
||||
{sasl, [
|
||||
{sasl_error_logger, {file, "log/emqtt_sasl.log"}}
|
||||
]},
|
||||
{mnesia, [
|
||||
{dir, "var/mnesia"}
|
||||
]}
|
||||
].
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
|
||||
-record(direct_topic, {name, node}).
|
||||
|
||||
-record(wildcard_topic, {words, node}).
|
||||
|
||||
-record(subscriber, {topic, pid}).
|
||||
|
||||
|
||||
|
Binary file not shown.
|
@ -0,0 +1,12 @@
|
|||
{application, emqtt,
|
||||
[
|
||||
{description, ""},
|
||||
{vsn, "1"},
|
||||
{registered, []},
|
||||
{applications, [
|
||||
kernel,
|
||||
stdlib
|
||||
]},
|
||||
{mod, { emqtt_app, []}},
|
||||
{env, []}
|
||||
]}.
|
|
@ -0,0 +1,32 @@
|
|||
-module(emqtt_app).
|
||||
|
||||
-export([start/0]).
|
||||
|
||||
-behaviour(application).
|
||||
|
||||
%% Application callbacks
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
-define(APPS, [sasl, mnesia, emqtt]).
|
||||
|
||||
start() ->
|
||||
[start_app(App) || App <- ?APPS].
|
||||
|
||||
start_app(mnesia) ->
|
||||
mnesia:create_schema([node()]),
|
||||
mnesia:start();
|
||||
|
||||
start_app(App) ->
|
||||
application:start(App).
|
||||
|
||||
|
||||
%% ===================================================================
|
||||
%% Application callbacks
|
||||
%% ===================================================================
|
||||
|
||||
start(_StartType, _StartArgs) ->
|
||||
emqtt_sup:start_link().
|
||||
|
||||
stop(_State) ->
|
||||
ok.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
%simulate a mqtt connection
|
||||
-module(emqtt_client).
|
||||
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
-module(emqtt_client_sup).
|
|
@ -0,0 +1 @@
|
|||
-module(emqtt_frame).
|
|
@ -0,0 +1,2 @@
|
|||
-module(emqtt_lib).
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
%tcp data reader
|
||||
-module(emqtt_reader).
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
-module(emqtt_router).
|
||||
|
||||
-include("emqtt.hrl").
|
||||
|
||||
-export([start_link/0]).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
terminate/2,
|
||||
code_change/3]).
|
||||
|
||||
-record(state,{}).
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
{stop, {badreq, Req}, State}.
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
{stop, {badmsg, Msg}, State}.
|
||||
|
||||
handle_info(Info, State) ->
|
||||
{stop, {badinfo, Info}, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, _State, _Extra) ->
|
||||
ok.
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
-module(emqtt_subscriber).
|
||||
|
||||
-include("emqtt.hrl").
|
||||
|
||||
-export([start_link/0]).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
terminate/2,
|
||||
code_change/3]).
|
||||
|
||||
-record(state,{}).
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
ets:new(subscriber, [bag, protected, {keypos, 2}]),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
{stop, {badreq, Req}, State}.
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
{stop, {badmsg, Msg}, State}.
|
||||
|
||||
handle_info(Info, State) ->
|
||||
{stop, {badinfo, Info}, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, _State, _Extra) ->
|
||||
ok.
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
-module(emqtt_sup).
|
||||
|
||||
-include("emqtt.hrl").
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
%% Helper macro for declaring children of supervisor
|
||||
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
|
||||
|
||||
%% ===================================================================
|
||||
%% API functions
|
||||
%% ===================================================================
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
%% ===================================================================
|
||||
%% Supervisor callbacks
|
||||
%% ===================================================================
|
||||
|
||||
init([]) ->
|
||||
{ok, { {one_for_all, 5, 10}, [
|
||||
?CHILD(emqtt_topic, worker),
|
||||
?CHILD(emqtt_router, worker)
|
||||
]} }.
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
|
||||
|
||||
-module(emqtt_topic).
|
||||
|
||||
-include("emqtt.hrl").
|
||||
|
||||
-export([start_link/0,
|
||||
match/1,
|
||||
insert/1,
|
||||
delete/1]).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
terminate/2,
|
||||
code_change/3]).
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
match(Topic) when is_binary(Topic) ->
|
||||
DirectMatches = mnesia:dirty_read(direct_topic, Topic),
|
||||
Words = topic_split(Topic),
|
||||
WildcardMatches = lists:append([
|
||||
mnesia:dirty_read(wildcard_topic, Key) ||
|
||||
Key <- mnesia:dirty_all_keys(wildcard_topic), topic_match(Words, Key)
|
||||
]),
|
||||
DirectMatches ++ WildcardMatches.
|
||||
|
||||
|
||||
insert(Topic) when is_binary(Topic) ->
|
||||
gen_server:call(?MODULE, {insert, Topic}).
|
||||
|
||||
delete(Topic) when is_binary(Topic) ->
|
||||
gen_server:cast(?MODULE, {delete, Topic}).
|
||||
|
||||
init([]) ->
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
direct_topic, [
|
||||
{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, direct_topic)}]),
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
wildcard_topic, [
|
||||
{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, wildcard_topic)}]),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call({insert, Topic}, _From, State) ->
|
||||
Words = topic_split(Topic),
|
||||
Reply =
|
||||
case topic_type(Words) of
|
||||
direct ->
|
||||
mnesia:dirty_write(#direct_topic{name=Topic});
|
||||
wildcard ->
|
||||
mnesia:dirty_write(#wildcard_topic{words=Words})
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
{stop, {badreq, Req}, State}.
|
||||
|
||||
handle_cast({delete, Topic}, State) ->
|
||||
Words = topic_split(Topic),
|
||||
case topic_type(Words) of
|
||||
direct ->
|
||||
mnesia:dirty_delete(direct_topic, Topic);
|
||||
wildcard ->
|
||||
mnesia:direct_delete(wildcard_topic, Words)
|
||||
end,
|
||||
{noreply, State};
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
{stop, {badmsg, Msg}, State}.
|
||||
|
||||
handle_info(Info, State) ->
|
||||
{stop, {badinfo, Info}, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, _State, _Extra) ->
|
||||
ok.
|
||||
|
||||
topic_type([]) ->
|
||||
direct;
|
||||
topic_type([<<"#">>]) ->
|
||||
wildcard;
|
||||
topic_type([<<"+">>|_T]) ->
|
||||
wildcard;
|
||||
topic_type([_|T]) ->
|
||||
topic_type(T).
|
||||
|
||||
topic_match([], []) ->
|
||||
true;
|
||||
|
||||
topic_match([H|T1], [H|T2]) ->
|
||||
topic_match(T1, T2);
|
||||
|
||||
topic_match([_H|T1], [<<"+">>|T2]) ->
|
||||
topic_match(T1, T2);
|
||||
|
||||
topic_match(_, [<<"#">>]) ->
|
||||
true;
|
||||
|
||||
topic_match([], [_H|_T2]) ->
|
||||
false.
|
||||
|
||||
topic_split(S) ->
|
||||
binary:split(S, [<<"/">>], [global]).
|
||||
|
Loading…
Reference in New Issue