--------------------------------debugger-----------------------
werl -setcookie emqsecretcookie -name node_3@CN00209774 -remsh emq@CN00209774
or
werl -sname observer -hidden -setcookie emqsecretcookie -run observer
Ctrl+G
r 'emq@CN00209774'
j
c 2
--load new module into vm---
code:add_pathz("C:/Program Files/erl9.3/lib/debugger-4.2.4/ebin").
l(debugger).
debugger:start().
ii(emqttd_app).
--------------------rebar---------relx.config-----定义了应用的版本及依赖的应用-----------------------------------
要定义一个发布,首先要创建一个 发布资源文件 ,简称 .rel 文件,我们在这里指定发布的名称和版本、它基于哪个ERTS版本,以及它由哪些应用组成:
{release, {
Name,Vsn}, {erts, EVsn},
[{
Application1, AppVsn1},
...
{
ApplicationN, AppVsnN}]}.
生成启动脚本
在SASL模块 systools 里面有很多可以用来构建和检验发布的工具。函数读取 .rel 和 .app 文件并执行语法和依赖性检查。函数systools:make_script/1,2 用于生成启动脚本。
当使用启动脚本启动Erlang/OTP时, .rel 文件中的所有应用都会被自动加载和启动
那么对于一些辅助lib呢,我们希望它被打包在应用发布目录中,但不希望它们被启动(它们可能根本不能启动),一种方法是将mylib指定为{mylib, load}(参见Issue1, Issue2),列表中的依赖项默认被relx解释为{mylib, permanent},即以常驻的方式启动应用。
------------------------------------------emqtt 启动流程------------------------------------------------------------------------
{application,emqttd,
[{description,"ErlangMQTTBroker"},
{vsn,"2.3.9"},
{modules,['emqttd','emqttd_access_control','emqttd_access_rule','emqttd_acl_internal','emqttd_acl_mod','emqttd_alarm','emqttd_app','emqttd_auth_mod','emqttd_base62','emqttd_boot','emqttd_bridge','emqttd_bridge_sup','emqttd_bridge_sup_sup','emqttd_broker','emqttd_cli','emqttd_cli_config','emqttd_client','emqttd_cm','emqttd_cm_sup','emqttd_config','emqttd_ctl','emqttd_gc','emqttd_gen_mod','emqttd_guid','emqttd_hooks','emqttd_http','emqttd_inflight','emqttd_keepalive','emqttd_message','emqttd_metrics','emqttd_mgmt','emqttd_misc','emqttd_mod_sup','emqttd_mqueue','emqttd_net','emqttd_packet','emqttd_parser','emqttd_plugins','emqttd_pmon','emqttd_pool_sup','emqttd_pooler','emqttd_protocol','emqttd_pubsub','emqttd_pubsub_sup','emqttd_rest_api','emqttd_router','emqttd_serializer','emqttd_server','emqttd_session','emqttd_session_sup','emqttd_sm','emqttd_sm_helper','emqttd_sm_sup','emqttd_stats','emqttd_sup','emqttd_sysmon','emqttd_sysmon_sup','emqttd_time','emqttd_topic','emqttd_trace','emqttd_trace_sup','emqttd_trie','emqttd_vm','emqttd_ws','emqttd_ws_client','emqttd_ws_client_sup','gen_server2','lager_emqtt_backend','priority_queue']},
{registered,[emqttd_sup]},
{applications,[kernel,stdlib,gproc,lager,esockd,mochiweb,pbkdf2]},
{env,[]},
{mod,{emqttd_app,[]}},
{maintainers,["FengLee<feng@emqtt.io>"]},
{licenses,["Apache-2.0"]},
{links,[{ "Github","https://github.com/emqtt/emqttd"}]}]}.
----------------------------------------------------------------
Emqttd.app.src application定义源文件生成emqttd.app文件
{mod,{emqttd_app,[]}}
-----------------------------------------------------------------------------
--> emqttd_app.erl
ekka:start(), 启动ekka
%%--------------------------------------------------------------------
%%MnesiaBootstrap
%%--------------------------------------------------------------------
mnesia(boot) 系统启动的时候创建mqtt_route mnesia 表。
mnesia(boot)->
ok=ekka_mnesia:create_table(mqtt_route
%%@docCreatemnesiatables.
create_tables()->
ekka_boot:apply_module_attributes(boot_mnesia). -- 该方法遍历所有已加载的应用和模块,查找模块中的属性,再调用对应方法创建表。
-boot_mnesia({mnesia,[boot]}).
EMQ工程目录下,有关键字-boot_mnesia({mnesia, [boot]}).和-copy_mnesia({mnesia, [copy]}).的模块是:
-module(emqttd_router).
-module(emqttd_sm). -- mqtt_session
-module(emqttd_trie). -- mqtt_trie / mqtt_trie_node
-----------------------------------------------------------------------------
emqttd_sup:start_link()
--> emqttd_sup.erl --emqttd_sup 监督进程
start_servers() 启动所有关联子进程。
--emqttd_ctl 命令行操作相关功能。
--> emqttd_ctl.erl
启动创建mqttd_ctl_cmd表
-----------------------------------------------------------------------------
--emqttd_hooks
EMQ 消息服务器在客户端上下线、主题订阅、消息收发位置设计了扩展钩子(Hook).
emqttd.erl 模块封装了 Hook 接口,
emqttd_hooks.erl 模块实现 Hook 机制,
创建mqtt_hook表,
emq_plugin_template 提供了全部钩子的使用示例,例如端到端的消息处理回调。
-----------------------------------------------------------------------------
--emqttd_router
--> emqttd_router.erl
该模块提供了消息路由的功能。EMQ 按主题树(Topic Trie)发布订阅模式在集群节点间路由 MQTT 消息。
init([])->
ekka:monitor(membership),
ets:new(mqtt_local_route,[set,named_table,protected]),
{ok,TRef}=timer:send_interval(timer:seconds(1),stats),
{ok,#state{stats_timer=TRef}}.
启动的时候会调用ekka的monitor接口打开集群监控,
--> ekka.erl --> ekka_membership.erl
ekka_membership 提供集群监控的功能
创建ets表,membership
保存节点信息
创建ets表,mqtt_local_route
{ok, TRef} = timer:send_interval(timer:seconds(1), stats),
和timer_server模块建立连接,每秒更新一次mqtt_stats 表状态,->
handle_info(stats,State)->
update_stats_(),
{noreply,State,hibernate};
update_stats_() -> emqttd_stats.erl
handle_cast({setstats,Stat,MaxStat,Val},State)
14:37:23:671000 (<10624.222.0>) << stats
14:37:23:671001 (<10624.222.0>) in {erlang,hibernate,3}
14:37:23:671002 (<10624.222.0>) emqttd_stats ! {'$gen_cast',
{setstats,'routes/count','routes/max',0}}
14:37:23:671003 (<10624.222.0>) emqttd_stats ! {'$gen_cast',
{setstats,'topics/count','topics/max',0}}
14:37:23:671004 (<10624.222.0>) out {erlang,hibernate,3}
-----------------------------------------------------------------------------
--emqttd_pubsub_sup
create_table: [mqtt_subproperty, mqtt_subscriber, mqtt_subscription]
emqttd:env(pubsub) 从配置文件中获取到环境变量
mqtt.pubsub.pool_size = 8
mqtt.pubsub.async = true
(emq@CN00209774)15> emqttd_pubsub_sup:init([]).
{ok,{ {one_for_all,10,3600},
[{pubsub_pool,
{emqttd_pool_sup,start_link,
[pubsub,hash,8,
{emqttd_pubsub,start_link,[[{pool_size,8},{async,true}]]}]},
transient,infinity,supervisor,
[emqttd_pool_sup]},
{server_pool,
{emqttd_pool_sup,start_link,
[server,hash,8,
{emqttd_server,start_link,[[{pool_size,8},{async,true}]]}]},
transient,infinity,supervisor,
[emqttd_pool_sup]}]}}
[pool_sup(pubsub, Env), pool_sup(server, Env)] 创建两个进程池pubsub_pool对应emqtt_pubsub模块, server_pool对应emqtt_server模块,每个池包含8个进程,
-> emqttd_pool_sup.erl
start_link(Pool,Type,Size,MFA)
init([Pool,Type,Size,{ M,F,Args}])
gproc_pool:add_worker(Pool,Name,Slot)
-> gproc_pool.erl 该模块实现了负载均衡服务器池,每个池都包含一定数量的worker和一个负载均衡策略,进程可以通过connect_worker/2 方法连接到池中的一个worker。用户可以通过pick/1方法获取到池中一个连接上的进程。
gproc 表存放进程池的相关信息
-----------------------------------------------------------------------------
emqttd_pubsub 提供pub/sub 相关的api
%%PubSubAPI.
-export([subscribe/3,async_subscribe/3,publish/2,unsubscribe/3,
async_unsubscribe/3,subscribers/1]).
-export([dispatch/2]).
emqttd_pubsub:publish(Topic,Msg1); -- 发送MQTT消息到对应topic
-> emqttd_router:match(Topic) --
-> emqttd_trie:match/1 -- 从mqtt_trie表和mqtt_trie_node表中匹配$SYS开头的系统topic,再从mqtt_route表中找出对应的route信息
-> emqttd_router:match_local(Topic) -- 从mqtt_local_route表中匹配本地订阅的topic
-> route() -- 没匹配到任何topic则drop掉消息,匹配到本地节点的topic则调用dispatch()发送消息,非本地节点则调用forward()方法发送消息到其他节点,再通过其他节点的dispatch()方法发送消息
-> dispatch() -- 先根据topic去mqtt_subscriber表查出订阅列表,没有则drop掉消息,有则向订阅者的session进程发送dispatch消息。
-----------------------------------------------------------------------------
emqttd_server 提供pub/sub 以及管理相关的api
%%PubSubAPI.
-export([subscribe/1,subscribe/2,subscribe/3,publish/1,
unsubscribe/1,unsubscribe/2]).
%%AsyncPubSubAPI.
-export([async_subscribe/1,async_subscribe/2,async_subscribe/3,
async_unsubscribe/1,async_unsubscribe/2]).
%%ManagementAPI.
-export([setqos/3,subscriptions/1,subscribers/1,subscribed/2]).
%%DebugAPI
-export([dump/0]).
-----------------------------------------------------------------------------
--emqttd_stats emqtt 统计数据收集模块,收集client, topic, session, 等信息
%%ClientandSessionStats
-export([set_client_stats/2,get_client_stats/1,del_client_stats/1,
set_session_stats/2,get_session_stats/1,del_session_stats/1]).
%%StatisticsAPI.
-export([statsfun/1,statsfun/2,getstats/0,getstat/1,setstat/2,setstats/3]).
创建表 mqtt_stats / mqtt_client_stats / mqtt_session_stats
init([])->
%Ticktopublishstats
{ok,#state{tick=emqttd_broker:start_tick(tick)},hibernate}.
%%@docStartaticktimer.
start_tick(Msg)->
start_tick(emqttd:env(broker_sys_interval,60000),Msg).
每分钟发送一次tick消息,触发publish操作,向表中的topic发送统计数据
handle_info(tick,State)->
[publish(Stat,Val)||{ Stat,Val}<-ets:tab2list(?STATS_TAB)],
{noreply,State,hibernate};
-----------------------------------------------------------------------------
--emqttd_metrics emqtt 统计数据收集模块,收集数据包和消息发送接收详细信息
%%APIFunctionExports
-export([start_link/0]).
%%Received/SentMetrics
-export([received/1,sent/1]). -- 统计接收和发送包的数量
-export([all/0,value/1,inc/1,inc/2,inc/3,dec/2,dec/3,set/2]).
all -- 获取所有指标的值
value -- 获取指定指标的值
inc -- 指标值递增
dec -- 指标值递减
set -- 给指标设置一个值
创建表 mqtt_metric
%Ticktopublishmetrics
{ok,#state{tick=emqttd_broker:start_tick(tick)},hibernate}.
每分钟发送一次tick消息,触发publish操作,向表中的topic发送统计数据
-----------------------------------------------------------------------------
--emqttd_pooler 进程池管理模块
根据运行机器的CPU核数,启动相应数量的emqtt_pooler进程
erlang:system_info(schedulers)
{state,{<0.244.0>,emqttd_pool_sup},
one_for_one,
[{child,<0.248.0>,
{emqttd_pooler,4},
{emqttd_pooler,start_link,[pooler,4]},
transient,5000,worker,
[emqttd_pooler]},