Skip to content
This repository has been archived by the owner on Jan 19, 2020. It is now read-only.

Latest commit

 

History

History
393 lines (301 loc) · 13.5 KB

configuration.md

File metadata and controls

393 lines (301 loc) · 13.5 KB

Configuration

##ecomponent (general params)

The basic configuration for the ecomponent project is as follow:

  • syslog_name::string() default value is ecomponent. Optional.
  • jid::string() external component domain.

##processors

The processors handle requests depend on if the stanza is an IQ, a presence or a messsage:

  • processors configure what module or app will be process the iq's. You can set a default value for all namespaces or configure by pair {namespace::atom() | default, mod_processor | app_processor} where mod_processor:: {mod, Module::atom()} and app_processor:: {app, App::atom()}.
  • message_processor set one processor for messages. Optional.
  • presence_processor set one processor for presence stanzas. Optional.

A processor could be an application {app, App::atom()} or a module {mod, Module::atom()}. By example, if you want to handle only messages and pass the processing task to a module, you can set this in the config file:

{message_processor, {mod, my_message_processor}}

In this example the module my_message_processor must exists and must have the following function:

-module(my_message_processor).
-export([process_message/1]).

-include_lib("ecomponent/include/ecomponent.hrl").

process_message(#message{}=Message) ->
    ok.

The record #message{} can be found here.

Now, if we want to add processors for IQ and presence, the configuration should be:

{presence_processor, {mod, my_processor}},
{message_processor, {mod, my_processor}},
{processors, [
    {default, {mod, my_processor}}
]}

Note that you can change default for one namespace and filter the requests by namespace in diferent modules or applications.

The code to use this configuration should be:

-module(my_processor).
-export([
    process_presence/1,
    process_message/1,
    process_iq/1
]).

-include_lib("ecomponent/include/ecomponent.hrl").

process_presence(#presence{}=Presence) ->
    ok.

process_message(#message{}=Message) ->
    ok.

process_iq(#params{}=Params) ->
    ok.

The use of app instead of mod is only recommended when a state should be kept between requests. Be careful with this, because the use of app could generate a bottleneck.

##resend IQs

When ecomponent send an IQ to the server tracks for a reply (else you use false in reply param for send function). If the reply is not received in a configured time, ecomponent resend the reply again.

This resend is made by the timem module and the configuration param is request_timeout, in seconds. The default value for retries is 10 (seconds).

The configuration params available:

  • resend (boolean) - enable/disable IQ resend.
  • request_timeout (integer, seconds) - time between resends for each IQ stanza.
  • max_tries (integer) - the number of tries for send the IQ stanza.

##server configuration

You can configure only one server to connect through XMPP, several connections (each one with its own id) or shared connections (connections through another node in the cluster).

If you have three servers and you want to connect each one locally, but share the connection to avoid when a connection is lost in one server, you can configure as follow:

%% ecomponent one:
{servers, [
    {ejabberd_one, [
        {server, "localhost"},
        {port, 5252},
        {secret, "mypass"}
    ]},
    {ejabberd_two, [
        {node, 'ecomponent_two@server_two'}
    ]},
    {ejabberd_three, [
        {node, 'ecomponent_three@server_three'}
    ]}
]}

%% ecomponent two:
{servers, [
    {ejabberd_one, [
        {node, 'ecomponent_one@server_one'}
    ]},
    {ejabberd_two, [
        {server, "localhost"},
        {port, 5252},
        {secret, "mypass"}
    ]},
    {ejabberd_three, [
        {node, 'ecomponent_three@server_three'}
    ]}
]}

%% ecomponent three:
{servers, [
    {ejabberd_one, [
        {node, 'ecomponent_one@server_one'}
    ]},
    {ejabberd_two, [
        {node, 'ecomponent_two@server_two'}
    ]},
    {ejabberd_three, [
        {server, "localhost"},
        {port, 5252},
        {secret, "mypass"}
    ]}
]}

When the server wants to send a message always try to send to the local connection but, if this is down, the message will be tried by the connections in another nodes in the cluster.

As an option you can use different JID for each server configuration. Be careful with this, if you configure your XMPP server with check_from option and you send an incorrect from tag the connection will be dropped.

If you want to use a connection only for receive stanzas (and reply them) you can configure as follow:

{servers, [
    {master, [
        {server, "localhost"},
        {port, 5252},
        {secret, "mypass"}
    ]},
    {presences, [
        {server, "localhost"},
        {port, 5252},
        {secret, "mypass"},
        {jid, "presence.domain.com"},
        {type, passive}
    ]},
]}

The valid values for type option can be active (by default) and passive for only receive stanzas (and reply them if needed).

When ecomponent:send, ecomponent:send_message or ecomponent:send_presence is used, the message goes to the connection indicated, but if the connection is down or is not indicated, the system tries with another one in a round-robin way. First with active connections and if there aren't active connections available, then goes to passive connections.

To avoid bottlenecks, you can connect to XMPP server with a pool of connections as follow:

{servers, [
    {master, [
        {server, "localhost"},
        {port, 5252},
        {secret, "mypass"},
        {poolsize, 10}
    ]}
]}

This configuration makes 10 connections to localhost:5252 and adds to the round-robin params to send stanzas.

##throttle configuration

If you want to avoid a collapse for your component, you can configure a throttle with the follow params inside the ecomponent main tag:

  • throttle::boolean() enable/disable the throttle. true by default. Optional.
  • whitelist::list(domain::Binary) a list of domain for their request are not dropped, other way the request will be dropped if send more than 10 request in minus 6 seconds by default. Can be empty.
  • max_per_period::integer() number of reques per period for drop packets. Optional.
  • period_seconds::integer() amount of time for period. Optional.

All the request arriving to the component will be anoted in mod_monitor. If the requests for one user achieve more than max_per_period in the period_seconds, the requests from this JID will be dropped until the conditions will be false again.

All the JIDs inside whitelist will be ignored by the monitor.

##granting access to IQs

The requests to IQs can be filtered by users (JIDs) using ACLs. This ACLs are set by namespace, type and JID. A user (JID) can be granted for a namespace in a specific type. The params for configuration inside ecomponent tag are the following:

  • access_list_get::list(tuple(Atom, list(Binary))) a list of {Namespace, list(domain::Binary)}. You can configure what domain can send iq get by namespace. Can be empty.
  • access_list_set::list(tuple(Atom, list(Binary))) a list of {Namespace, list(domain::Binary)}. You can configure what domain can send iq set by namespace. Can be empty.

It's possible specify that user1@domain.com can access to jabber:iq:time for the component, but only in get type:

{access_list_get, [
    <<"user1@domain.com">>
]}

Note that if the configuration for these parameters is omitted all the users can access to all the IQs.

##disco info

There are a couple of params to configure the behaviour when the server requires a disco#info from the component. In this case we can use set a reply like this:

  <query xmlns='http://jabber.org/protocol/disco#info'>
    <identity
        category='conference'
        type='text'
        name='Play-Specific Chatrooms'/>
    <feature var='http://jabber.org/protocol/disco#info'/>
    <feature var='http://jabber.org/protocol/disco#items'/>
    <feature var='http://jabber.org/protocol/muc'/>
    <feature var='jabber:iq:register'/>
    <feature var='jabber:iq:search'/>
    <feature var='jabber:iq:time'/>
    <feature var='jabber:iq:version'/>
  </query>

Only we need to add this configuration to the config file:

{features, [
    <<"http://jabber.org/protocol/disco#info">>,
    <<"http://jabber.org/protocol/disco#items">>,
    <<"http://jabber.org/protocol/muc">>,
    <<"jabber:iq:register">>,
    <<"jabber:iq:search">>,
    <<"jabber:iq:time">>,
    <<"jabber:iq:version">>
]},
{info, [
    {type, <<"text">>},
    {name, <<"Play-Specific Chatrooms">>},
    {category, <<"conference">>}
]},
{disco_info, true}

##clustering with mnesia

Scale ecomponent is pretty easy with these params:

  • mnesia_nodes::[node()] set name of mnesia node if you want set your external component like cluster. Optional.
  • mnesia_callback identify a function that should returns a list of tables to create and distribute in the mnesia cluster. Optional.

With mnesia_nodes it's possible to communicate the nodes in a easy way, and auto-configure Mnesia to work in cluster.

As a plus, it's possible use Mnesia for create new tables easily inside the cluster. By example:

-module(mycode).
-compile([export_all]).

-record(mytable, {id, name, description}).

create_tables() -> [
    {mytable, disc_copies, record_info(fields, mytable)}
].

And in the config file:

{mnesia_callback, [
    {mycode, create_tables, []}
]}

##folsom

Dependency: folsom

Automatically ecomponent provide you metrics using folsom. In old versions we use folsom_cowboy but it's better you can select the better way for you to collect this data. The options available are:

The default values are:

  • iq throughput by namespace. spiral.
  • iq response time by namespace. slide 60 sec.
  • iq dropped by namespace. spiral.
  • presence throughput by type (available, unavailable, ...). spiral.
  • presence dropped by type. spiral.
  • message throughput by type (normal, chat, groupchat, ...). spiral.
  • message dropped by type. spiral.

You can extend that with the ecomponent param metrics_mods:

{ecomponent, [
    ...
    {metrics_mods, [mycode_metrics]},
    ...
]},

In the module mycode_metrics you should provide a list of metrics to create:

-module(mycode_metrics).
-export([init/0]).

init() ->
    [mycode_ok, mycode_errors].

##lager

Dependency: lager configuration

##Config file example

The example file app.config have the following sections:

    [
        {ecomponent, [
            {syslog_name, ComponentName :: string()},
            {jid, ComponentDomain :: string()},

            {servers, [
                {server_one, [
                    {server, ServerHost :: string()},
                    {port, ServerPort :: non_neg_integer()},
                    {pass, ServerPass :: string()}
                ]},
                {server_shared, [
                    {node, Node :: atom()}
                ]}
            ]},

            {throttle, boolean()},
            {whitelist, [domain::Binary] }, %% throttle whitelist
            {max_per_period, MaxPerPeriod :: non_neg_integer()},
            {period_seconds, PeriodSeconds :: non_neg_integer()},

            {access_list_get, [{namespace::Atom, [domain::Binary]}]},
            {access_list_set, [{namespace::Atom, [domain::Binary]}]},

            {processors, [
                {default, mod_processor | app_processor}
            ]},
            {message_processor, message_processor},
            {presence_processor, presence_processor},

            {mnesia_nodes, [Node::atom()]},
            {mnesia_callback, [Callback::{M::atom(),F::atom(),A::[term()]}]},

            {features, [
                <<"jabber:iq:last">> | [binary()]
            ]},
            {info, [
                {type, <<"jabber:protocol:boot">>},
                {name, <<"Boot">>},
                {category, <<"text">>}
            ]},
            {disco_info, boolean()}
        ]},
    
        {lager, [
            {handlers, [
                {lager_console_backend, info},
                {lager_file_backend, [
                    {"log/error.log", error, 10485760, "$D0", 5},
                    {"log/info.log", info, 10485760, "$D0", 5}
                ]}
            ]},
            {crash_log, "log/crash.log"},
            {crash_log_msg_size, 65536},
            {crash_log_size, 10485760},
            {crash_log_date, "$D0"},
            {crash_log_count, 5},
            {error_logger_redirect, true}
        ]}
    ].

On processors you can indicate a list of processor by namespace or choose one by default, the processor can be a module or an app.