ejabberd的启动从ejabberd:start() 开始。
application:start(ejabberd).
application用来表示一个可以单独启动和停止、并被复用的程序单元。通常一个application都有一个同名的app文件来定义。
对于系统工具(systool)直接调用的app文件必须包含以下几项:
description , vsn , modules , registered , applications
我们查看ejabberd.app可以看到以下内容:
{application, ejabberd, [{description, "ejabberd"}, {vsn, "2.1.7"}, {modules, [acl, ...... {registered, [ejabberd, ...... {applications, [kernel, stdlib]}, {env, []}, {mod, {ejabberd_app, []}}]}.
最后一项:mod 定义了应用程序的回调模块和启动参数。 在应用程序启动时会由主导进程来调用这个模块的start/2方法。
ejabberd_app.erl 中的start/2 :
start(normal, _Args) -> ejabberd_loglevel:set(4), write_pid_file(), application:start(sasl), randoms:start(), db_init(), sha:start(), stringprep_sup:start_link(), xml:start(), start(), translate:start(), acl:start(), ejabberd_ctl:init(), ejabberd_commands:init(), ejabberd_admin:start(), gen_mod:start(), ejabberd_config:start(), ejabberd_check:config(), connect_nodes(), %% Loading ASN.1 driver explicitly to avoid races in LDAP catch asn1rt:load_driver(), Sup = ejabberd_sup:start_link(), ejabberd_rdbms:start(), ejabberd_auth:start(), cyrsasl:start(), % Profiling %ejabberd_debug:eprof_start(), %ejabberd_debug:fprof_start(), maybe_add_nameservers(), start_modules(), ejabberd_listener:start_listeners(), ?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]), Sup; 我们可以看到它调用了一系列的方法和函数来完成整个应用的启动。
1. ejabberd_loglevel:set(4) //设置日志级别
2. write_pid_file() //将当前进程标识写到环境变量EJABBERD_PID_PATH定义的文件中
3. application:start(sasl) //启动sasl应用 ,具体sasl应用所提供的服务请查看<a href="http://www.erlang.org/doc/man/sasl_app.html" mce_href="http://www.erlang.org/doc/man/sasl_app.html">sasl</a>
4. randoms:start() //注册一个名为random_generator,用当前时间做种子生成随机字符串的进程
5. db_init() //初始化并启动本地的mnesia数据库,并等待直到所有表可用或超时
6. sha:start() //加载一些驱动
7. stringprep_sup:start_link() //启动一个监控进程
8. xml:start() //提供xml处理功能
9. start() //注册进程自身为ejabberd,设置日志目录,加载驱动
10. translate:start()//从环境变量EJABBERD_MSGS_PATH定义的位置找*.msg 并加载到ets:translations 表中(国际化)
11. acl:start() //访问控制
12. ejabberd_ctl:init() //创建两张表ejabberd_ctl_cmds,ejabberd_ctl_host_cmd
13. ejabberd_commands:init() //创建表ejabberd_commands
14. ejabberd_admin:start() //注册常用的ejabberd_command到表ejabberd_commands 这里的ejabberd_command类似于java中的虚方法,这个ejabberd_command 具有指定的名称、指定类型数量参数、指定类型返回值。
例如:
#ejabberd_commands{name = register, tags = [accounts],
desc = "Register a user",
module = ?MODULE, function = register,
args = [{user, string}, {host, string}, {password, string}],
result = {res, restuple}},定义了一个名为register,接受[{user, string}, {host, string}, {password, string}] 参数,返回{res, restuple}的虚方法。
15. gen_mod:start() //创建表ejabberd_modules
16. ejabberd_config:start() //创建config,local_config两张表,并加载配置文件中的配置到表中
17. ejabberd_check:config() //检查配置文件中指定要加载的模块是否存在
18. connect_nodes() //建立到集群中其他节点的链接,其他节点在配置中定义
19. Sup = ejabberd_sup:start_link() //启动一个supervisor,并启动和监控定义的子进程
详细看下这个supervisior 的init()定义:
{ok, {{one_for_one, 10, 1}, [Hooks, NodeGroups, SystemMonitor, Router, SM, S2S, Local, Captcha, ReceiverSupervisor, C2SSupervisor, S2SInSupervisor, S2SOutSupervisor, ServiceSupervisor, HTTPSupervisor, HTTPPollSupervisor, IQSupervisor, STUNSupervisor, FrontendSocketSupervisor, CacheTabSupervisor, Listener]}}
重启策略为one_for_one(只重启终止的子进程自身),如果1s内重启次数超过10则终止所有子进程和自身。列表中为子进程的定义。以Hooks为例子:
Hooks = {ejabberd_hooks, {ejabberd_hooks, start_link, []}, permanent, brutal_kill, worker, [ejabberd_hooks]} 子进程定义的格式为:{Id, StartFunc, Restart, Shutdown, Type, Modules}
id: 分配的ID
StartFunc: 启动子进程时调用的方法
Restart: 重启子进程时调用的方法
Shutdown: 关闭子进程时调用的方法
Type: 子进程的类型,(supervisior or worker)
Modules: 回调模块
20. ejabberd_rdbms:start() //启动数据库相关模块21. ejabberd_auth:start() //启动所有鉴权模块22. cyrsasl:start() //启动SASL安全认证模块
可以看到这里启动了不少的子进程,从名字上大体上可以看出各个子进程的功能。
23. maybe_add_nameservers() //如果运行在windows系统,则添加域名服务器(DNS)地址到erlang系统中
24. start_modules() //启动所有节点上的定义在local_config中的模块
25. ejabberd_listener:start_listeners() //启动配置文件中的监听器模块