您的位置:首页 > 其它

Ejabberd源码学习——启动流程

2016-04-11 14:35 579 查看
这篇文章是我之前在RYTong内部分享的一篇文章,将简单介绍一下Ejabberd在启动时候的流程,以及启动过程中一些关键的逻辑。

PS:Ejabberd版本是2.1.11

启动入口

Ejabberd的启动函数是ejabberd:start/0方法,并最终调用ejabberd_app:start/2方法。这个方法实现了启动的主体逻辑,顺着该方法的源码逻辑就可以了解Ejabberd启动都做了些什么了。

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;
start(_, _) ->
{error, badarg}.


关键功能解析

日志

Ejabberd在ejabberd.hrl中定义了若干级别的日志宏,这些宏最终调用了ejabberd_logger模块的函数来打印日志,但在源码路径中是搜索不到这个模块的。这是一个神奇的模块,它会在Ejabberd启动的时候自动生成。生成ejabberd_logger模块是由ejabberd_loglevel:set(4)方法完成的,这个set方法传入的参数是loglevel。

%%
%% loglevel: Verbosity of log files generated by ejabberd.
%% 0: No ejabberd log at all (not recommended)
%% 1: Critical
%% 2: Error
%% 3: Warning
%% 4: Info
%% 5: Debug
%%
{loglevel, 5}.


在ejabberd.cfg配置文件中(上图)有loglevel配置项,Ejabberd将日志分成了debug、info、warning等等若干日志级别,每个日志级别对应一个整数。配置一个级别后,Ejabberd在运行时将不会打印比这个级别高的日志信息。因此,我们在开发时配置loglevel为5(最低),便可以查看到所有级别的日志。在投产时配置loglevel为2,便只会看到error及以下级别的信息。

ejabberd_loglevel:set/1方法会根据传入的loglevel动态的生成ejabberd_logger模块,使得高于这个级别的日志方法不会打印内容。有了这个方法,我们就可以在不重启的情况下切换日志级别了。麻麻再也不用担心了。

另外,ejabberd_logger的日志函数并不是最终写日志的函数。ejabberd_app:start/2会调用error_logger:add_report_handler(ejabberd_logger_h, LogPath)方法将error_logger产生的日志事件的handler改成 ejabberd_logger_h,这个模块处理日志事件并完成写日志的功能。ejabberd_logger的日志函数最终都会调用gen_event:notify(error_logger, LoggerMsg)方法产生日志事件。

进程监控树

每个Erlang Node都会有自己的监控树。监控树由若干Supervisor和Worker进程组成,Supervisor进程可以监控其他Supervisor或Worker进程。Worker是实现具体功能的进程。当被监控的进程意外退出时,Supervisor进程将会根据监控策略重启所监控的进程。关于Supervisor的介绍可以参考Erlang的官方文档。

Ejabberd的监控进程树通过ejabberd_sup模块构建。ejabberd_sup进程在启动后会启动若干Supervisor进程并监控它们。这里我们先不去关注这些Supervisor,来看一下ejabberd_tmp_sup模块。ejabberd_sup.erl代码里可以看到很多Supervisor都是用ejabberd_tmp_sup作为启动模块的,为什么用这一个模块来启动不同的Supervisor呢?

start_link(Name, Module) ->
supervisor:start_link({local, Name}, ?MODULE, Module).

init(Module) ->
{ok, {{simple_one_for_one, 10, 1},
[{undefined, {Module, start_link, []},
temporary, brutal_kill, worker, [Module]}]}}.


通过以上源码可以了解到,原来ejabberd_tmp_sup启动的Supervisor所监控的进程都是Worker进程,并且使用simple_one_for_one的重启策略。Supervisor使用simple_one_for_one重启策略时,不会马上启动所监控的Worker进程,而是通过supervisor:start_child/2方法动态添加Worker进程。并且这些Worker进程在退出之后,并不会被重启,也不会影响该Supervisor监控的其他Worker进程。

一个ejabberd_tmp_sup监控的都是做相同事情的临时Worker进程,比如Ejabberd与每个客户端建立连接后都会创建一个Worker进程来接收TCP包,这个进程就是由ejabberd_tmp_sup监控的。在断开连接后,这个Worker进程就会退出,此时不需要重启该进程,也不需要重启其他Worker进程。因此使用ejabberd_tmp_sup来监控这些Worker进程是再合适不过的了。

Modules启动

ejabberd.cfg配置文件中有modules配置项,这个配置用来管理一些类似于插件的服务模块,这些服务模块用来提供部分XEP扩展协议定义的服务,如mod_register提供了注册服务。hosts配置项定义了当前Ejabberd服务的虚拟主机,当Ejabberd收到一个XMPP报文时会检查该报文的目的主机是否为已配置的虚拟主机,如果是则会在本地处理这个报文。如果不是,则会尝试转发给其他XMPP主机或返回错误。

Ejabberd启动时,会为每个虚拟主机启动modules配置的服务,并最终调用配置模块的start/2方法,第一个参数为虚拟主机,第二个参数为配置的启动参数。

端口监听

XMPP基于TCP协议,因此Ejabberd需要监听一些端口来接收TCP连接。Ejabberd启动监听的逻辑在ejabberd_listener:start_listeners/0函数里实现。该函数会监听ejabberd.cfg里listen配置项配置的每个端口。

ejabberd_listener本身是一个Supervisor进程,被ejabberd_sup监控。ejabberd_sup启动时会启动ejabberd_listener。start_listeners/0方法最终会为每个端口启动一个Worker进程(如下代码),Worker进程启动时会调用ejabberd_listener:start/3方法。

start_listener_sup(Port, Module, Opts) ->
ChildSpec = {Port,
{?MODULE, start, [Port, Module, Opts]},
transient,
brutal_kill,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_listeners, ChildSpec).


ejabberd_listener:start/3方法实现了具体的端口监听逻辑。这个监听逻辑到底是怎样的呢?Ejabberd又是如何接收连接并在每个XMPP实体之间转发报文的呢?在下一篇文章找答案吧,童鞋们!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ejabberd 源码