We incorporated XMPP into our product to do multi-user chat.
For this we use eJabberd, an Erlang based XMPP server.
- eJabberd supports clustering (standard erlang and mnesia clustering)
- eJabberd supports multi-user-chat (plugin: mod_muc)
We needed to be able to get information out of Jabber from our JVM based
services:
- see who is currently in a room
if there is no counterparty for a user, we actively try to find one that is
on-call and send them into the room - peek at the messages in a room: so we could write tests for our system to
ensure we were mapping sms <→ xmpp - post a message to the room on behalf of a user
this was for integrating SMS send/recv into the room
We thought there were a few approaches:
- extend the XMPP protocol to support all of these
PRO: fits into the existing clients (bosh, smack)
CON: existing client (smack) is flaky/unreliable
CON: we don’t want normal users to be able to exercise
the new features.
CON: eJabberd prevents the same user from logging into a room multiple times:
this is in conflict with our distributed system
CON: extending XMPP would be complex and standard clients may no longer be
able to participate - create an alternate interface into jabber
We found mod_restful_admin, so we chose to go with an alternate interface:
HTTP. mod_restful_admin already allowed for registering users (which is
something we need to do, but can via smack), and looked like it could be easily
extended to do what we needed.
Understanding eJabberd through the standard OTP Behaviors:
- gen_server
- gen_fsm
- gen_event
- supervisor
mod_muc_room’s record structure:
./software/build/ejabberd-2.1.12/src/mod_muc/mod_muc_room.hrlUnderstanding Erlang: Language, Semantics of code
- expression based language
- single assignment
- pattern matching and destructuring
- single funciton, multiple clauses does not exactly map to polymorphisim,
function overloading or even defmulti but defmulti is closest - syntatic: comma, semi-colon; and period.
- data types: numbers, atoms, tuples, lists and strings
- records, tuples with a type
Understanding Erlang: Runtime Semantics
- processes
- message passing
- recursion and handling state in a single-assginment world
- mnesia, data storage and clustering
- iolists, and Erlang’s sprintf
- The erlang path, and finding compiled code.
Lessons Learned, some learned the hard way
- mnesia is tied to the node name(!)
- erlang cookie
- data types: JSON challenges: strings are arrays of ints
- debugging: ?INFO_MSG(),
- debugging: mod_restful_debug: capture live data (vars) like we do in Clojure
- debugging: query a gen_fsm for it’s state
- iterating: shell, interacting with code
- iterating: remote shell, connect to a running erlang node
- iterating: using ejabberdctl debug
- iterating: make sure the build only builds what you changed
- iterating: using ejabberdctl live
Internals of MUC that we reverse engineered:
- MUC room is managed by a process
- that process stores it’s configuration in mnesia
- list of rooms is persisted in mnesia
- the list of users and the messages are in the room state only
only in memory
To R&D:
- recompile and hot reload code: ‘code replacement’ or ‘release handling’
Authors:
- Kyle Burton
- Paul Santa Clara
The code in this repository assumes that you have Erlang already installed.
Ruby’s rake
utiliity must be installed, is used for automating and building.
The included Rakefile will download and install eJabberd into a self-contained
directory for working through the examples.
eJabberd can be run in the background as a daemon, or in the foreground. If it
is run in the background, you can still interact with it by launching
ejabberdctl debug
. To run eJabberd, run ejabberdctl start
. After
installation, ejabberdctl
will be located in ./software/ejabberd/sbin/ejabberdctl
.
- Interact with the Shell:
erl
- Create a module
- Compile and load
- Connect 2 Nodes
- Mnesia: Starting mnesia
- Mnesia: Creating tables, insertion and querying.
- Mnesia: Clustering 2 Nodes
http://www.flickr.com/photos/42336015@N00/5067418822/
“Donkey”: Donkey-5067418822_6fde6e9e64_z.jpg