Introduction
For many years, EMG users have been able to extend the functionality with their own Perl code. Most importantly, this is used for plugins doing HLR lookups, message filtering, billing, routing, etc. In EMG version 7 and earlier, this is implemented by running several instances of a Perl engine within the emgd process. While being relatively fast, it turned out to have some drawbacks.
- As all plugins run within the same process, it is not possible to see if a particular one uses lots of memory.
- There are some confirmed memory leaks when using multiple Perl engines within the same process. For more short lived programs this is not an issue, but the rest of EMG is designed to run for years without restarts.
- Support for other languages must be added within EMG itself. Even though there is an HTTP/JSON api for plugins in EMG version 7.2, this requires lifecycle management for those microservices.
Overall, this architecture has worked rather well for many years. However, it did not really meet the high quality requirements we have on EMG.
Overview of the new architecture
For EMG version 8, we have created an entirely new solution. The basic idea is similar to microservices, but with lifetime management handled by EMG. The main points are described below.
- Each plugin instance gets its own Perl engine, running in its own process. If you somehow manage to get the Perl engine to crash (maybe due to a bug in some CPAN module), this would previously bring down the entire EMG server. Now only that particular plugin instance stops.
- The Perl processes are handled as a resource pool, so a new process is only started when needed. The number of instances for a
PLUGIN
section and the number of connector instances forURLHANDLER
drivers are therefore just the maximum size of each resource pool. For DLL connectors the drivers have a state, so there we start one process per connector instance, just as before. - EMG and the new processes communicate over abstract Unix sockets. These sockets are only for use within the same machine, just as any other Unix socket. However, they are not connected to the file system, and disappear automatically when they are closed.
- The parameters sent on the command line to the Perl processes include the plugin name and the Perl file. This makes it easy to see which plugins use the most amount of CPU and/or memory, using ps, top, etc.
- If a Perl process stops, it is simply restarted automatically before the next function call to it.
- When the main emgd process stops, the Perl processes are informed, so they can stop as well. If emgd would crash, the Perl processes notice that automatically, and stop. You will therefore never have any stray Perl processes.
- For the transport layer we use the NNG library.
- For serialization of the data being sent back and forth, we use the well-defined CBOR format, an RFC standard.
Due to the serialization, network communication, and context switches, this architecture is a little bit slower than the previous solution. However, it also uses less memory thanks to the resource pools, and provides a much more stable environment for the main emgd process. Furthermore, even a very moderately sized server can provide 1000+ roundtrips per second, so it’s absolutely not slow per se.
The new architecture also opens the door for alternative plugin backends, written in the language of your preference. For the languages where NNG is available, this backend can be written entirely in the target language. Otherwise you can write a backend in C, interfacing with the target language the same way as we do with Perl.
Configuration
In order for emgd to know which process to start, you need to add a new configuration parameter in the top section of the server.cfg
file, called PERL_SERVER
. Its value is the full path to the new emgperl
binary, normally something like /home/emg/bin/emgperl
. There is no default, so for Perl plugins to work you need to explicitly set this parameter.
A particular plugin can use its own server, for example for plugins in other languages. In this case you add the parameter SERVER
in the PLUGIN
section. The value is again the full path to the binary that should be started.
Source code
If you want to write your own plugin server, just send an email to support@braxo.se
and we’ll get you started. The Perl specific part is less than 1000 lines of C, and the general communication part is another 1000 lines plus various functions for arrays, string buffers, etc. How much of this you can reuse depends on your target and implementation languages.