Singleton (separate) message handler context in Javascript
From Yate Documentation
(Difference between revisions)
(Created page with " Singleton message handlers should be used when application is expected to process a high number of messages and want to process it in a 'multithreaded' way. A regular messa...") |
|||
| Line 1: | Line 1: | ||
| − | + | A regular message handler callback is called by javascript when script context is unlocked.<br/> | |
| + | Script context is locked during message processing.<br/> | ||
| + | Script global data is available in message handler callback function. | ||
| + | A singleton message handler may be used when application is expected to process a high number of messages and want to avoid contention due to javascript serialization. | ||
| + | When a singleton message handler is installed the javascript module will build and initialize a separate context.<br/> | ||
| + | Handle callback function is called in this context.<br/> | ||
| + | The context is deleted after message is handled.<br/> | ||
| + | |||
| + | Message singleton related functions (e.g. Message.installsingleton()) are not available in: | ||
| + | * When running in a singleton context. | ||
| + | * When running a script configured with multiple instances | ||
| − | |||
| − | |||
| + | === Sample === | ||
| + | Engine.debugName("handler_singleton_sample"); | ||
| + | Message.trackName("handler_singleton_sample"); | ||
| + | |||
| + | // Callback for call.route. 'context' value is the second parameter passed to Message.installSingleton() | ||
| + | function onCallRoute(msg,context) | ||
| + | { | ||
| + | if (debug) | ||
| + | Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context); | ||
| + | // Increment some shared variable to be reported in status | ||
| + | var perf = new Engine.SharedVars("handler_singleton_sample_perf"); | ||
| + | var perf.inc("handled_" + context); | ||
| + | if (!msg.called) | ||
| + | return false; | ||
| + | var shared_objects = new Engine.SharedObjects; | ||
| + | var data = shared_objects.get("handler_singleton_sample_route_data"); | ||
| + | if (!data) | ||
| + | return false; | ||
| + | if (data.local_route_called) { | ||
| + | // Sample: route local using called party match | ||
| + | var match = msg.called.match(data.local_route_called); | ||
| + | if (match[1]) { | ||
| + | perf.inc("routed_local_route_called"); | ||
| + | msg.retValue("sip/sip:" + match[1] + "@127.0.0.1"); | ||
| + | return true; | ||
| + | } | ||
| + | } | ||
| + | if ("object" == typeof data.database) { | ||
| + | // Sample: route local using database query | ||
| + | var m = new Message("database",false,data.database); | ||
| + | m.module = Engine.debugName(); | ||
| + | if (m.dispatch(true) && !m.error) { | ||
| + | var row = m.getRow(0); | ||
| + | if (row.target) { | ||
| + | perf.inc("routed_database"); | ||
| + | msg.retValue(row.target); | ||
| + | return true; | ||
| + | } | ||
| + | } | ||
| + | else | ||
| + | perf.inc("database_failure"); | ||
| + | } | ||
| + | return false; | ||
| + | } | ||
| + | |||
| + | function onTest(msg,context) | ||
| + | { | ||
| + | if (debug) { | ||
| + | var shared_objects = new Engine.SharedObjects; | ||
| + | var data = shared_objects.get("handler_singleton_sample_route_data"); | ||
| + | var s = "\r\n-----\r\n" + Engine.dump_var_r(data) + "\r\n-----"; | ||
| + | Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context,"route_data:" + s); | ||
| + | } | ||
| + | // Increment some shared variable to be reported in status | ||
| + | perf = new Engine.SharedVars("handler_singleton_sample_perf"); | ||
| + | perf.inc("handled_" + context); | ||
| + | return true; | ||
| + | } | ||
| + | |||
| + | if (!Engine.scriptType) | ||
| + | initialize(true); | ||
| + | else if (Engine.ScriptTypeMsgHandler !== Engine.scriptType()) | ||
| + | initialize(true); | ||
| + | else { | ||
| + | // We are in singleton message handler context | ||
| + | // NOTE: debug (including level) is propagated from main script to singleton | ||
| + | // context by javascript module | ||
| + | debug = Engine.debugEnabled(); | ||
| + | } | ||
| + | |||
| + | // | ||
| + | // From this point the code is used only if script is loaded as static script | ||
| + | // | ||
| + | |||
| + | function onStatus(msg) | ||
| + | { | ||
| + | if (msg.module && ("handler_singleton_sample" != msg.module)) | ||
| + | return false; | ||
| + | var str = ""; | ||
| + | var perf = new Engine.SharedVars("handler_singleton_sample_perf"); | ||
| + | var perf = perf.getVars(); | ||
| + | for (var name in perf) | ||
| + | str += "," + name + "=" + perf[name]; | ||
| + | msg.retValue(msg.retValue() + "name=handler_singleton_sample,type=misc;" + str.substr(1) + "\r\n"); | ||
| + | return !!msg.module; | ||
| + | } | ||
| + | |||
| + | function onDebug(msg) | ||
| + | { | ||
| + | if (var dbg = msg.line) { | ||
| + | Engine.setDebug(dbg); | ||
| + | debug = Engine.debugEnabled(); | ||
| + | } | ||
| + | msg.retValue("Sample Singleton Message Handler debug " + Engine.debugEnabled() + " level " + | ||
| + | Engine.debugLevel() + "\r\n"); | ||
| + | return true; | ||
| + | } | ||
| + | |||
| + | function onReload(msg) | ||
| + | { | ||
| + | if (msg.plugin && ("handler_singleton_sample" != msg.plugin)) | ||
| + | return false; | ||
| + | initialize(); | ||
| + | return !!msg.plugin; | ||
| + | } | ||
| + | |||
| + | function initialize(first) | ||
| + | { | ||
| + | Engine.output("Initializing module Sample Singleton Message Handler"); | ||
| + | if (first) { | ||
| + | // If script is configured to use multiple instances it can only install singleton for the first one | ||
| + | // It makes no sense to configure multiple instances for it! | ||
| + | if (!Message.installSingleton) { | ||
| + | Engine.debug(Engine.DebugConf,"Refusing to start Sample Singleton Message Handler", | ||
| + | "singleton handler install not available"); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // Initial debug | ||
| + | Engine.setDebug("level 10"); | ||
| + | debug = Engine.debugEnabled(); | ||
| + | |||
| + | // Message.installSingleton(func,context,name[,priority[,filterName[,filterValue[,params]]]) | ||
| + | // Do not load extensions (default: javascript.conf global setup) | ||
| + | // Extensions are loaded using a script.init message. C++ modules may push javascript objects in script context | ||
| + | var params = {load_extensions:false}; | ||
| + | // Sample call.route filter. Handle if not explicitly disabled. Use Engine.MatchingItem | ||
| + | var mi = {name:"handler_singleton_sample",value:/^$(true|yes|)$/}; | ||
| + | var filter = new Engine.MatchingItem(mi); | ||
| + | Message.installSingleton(onCallRoute,"call_route","call.route",30,filter,undefined,params); | ||
| + | // Sample test filter. Handle if not explicitly disabled. Use basic | ||
| + | Message.installSingleton(onTest,"test","test",70,"handler_singleton_sample",/^$(true|yes|)$/,params); | ||
| + | |||
| + | // Module messages | ||
| + | Message.install(onStatus,"engine.status"); | ||
| + | Message.install(onDebug,"engine.debug",150,"module","handler_singleton_sample"); | ||
| + | Message.install(onReload,"engine.init"); | ||
| + | } | ||
| + | |||
| + | // Init and set shared object holding route data | ||
| + | var routeData = { | ||
| + | local_route_called: /^(test|other)$/, | ||
| + | database: {account:"some_db_account",query:"select target from route"}, | ||
| + | }; | ||
| + | var shared_objects = new Engine.SharedObjects; | ||
| + | shared_objects.set("handler_singleton_sample_route_data",routeData); | ||
| + | } | ||
'''See also''' | '''See also''' | ||
* [[Javascript]] | * [[Javascript]] | ||
Revision as of 16:47, 29 August 2024
A regular message handler callback is called by javascript when script context is unlocked.
Script context is locked during message processing.
Script global data is available in message handler callback function.
A singleton message handler may be used when application is expected to process a high number of messages and want to avoid contention due to javascript serialization.
When a singleton message handler is installed the javascript module will build and initialize a separate context.
Handle callback function is called in this context.
The context is deleted after message is handled.
Message singleton related functions (e.g. Message.installsingleton()) are not available in:
- When running in a singleton context.
- When running a script configured with multiple instances
Sample
Engine.debugName("handler_singleton_sample");
Message.trackName("handler_singleton_sample");
// Callback for call.route. 'context' value is the second parameter passed to Message.installSingleton()
function onCallRoute(msg,context)
{
if (debug)
Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context);
// Increment some shared variable to be reported in status
var perf = new Engine.SharedVars("handler_singleton_sample_perf");
var perf.inc("handled_" + context);
if (!msg.called)
return false;
var shared_objects = new Engine.SharedObjects;
var data = shared_objects.get("handler_singleton_sample_route_data");
if (!data)
return false;
if (data.local_route_called) {
// Sample: route local using called party match
var match = msg.called.match(data.local_route_called);
if (match[1]) {
perf.inc("routed_local_route_called");
msg.retValue("sip/sip:" + match[1] + "@127.0.0.1");
return true;
}
}
if ("object" == typeof data.database) {
// Sample: route local using database query
var m = new Message("database",false,data.database);
m.module = Engine.debugName();
if (m.dispatch(true) && !m.error) {
var row = m.getRow(0);
if (row.target) {
perf.inc("routed_database");
msg.retValue(row.target);
return true;
}
}
else
perf.inc("database_failure");
}
return false;
}
function onTest(msg,context)
{
if (debug) {
var shared_objects = new Engine.SharedObjects;
var data = shared_objects.get("handler_singleton_sample_route_data");
var s = "\r\n-----\r\n" + Engine.dump_var_r(data) + "\r\n-----";
Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context,"route_data:" + s);
}
// Increment some shared variable to be reported in status
perf = new Engine.SharedVars("handler_singleton_sample_perf");
perf.inc("handled_" + context);
return true;
}
if (!Engine.scriptType)
initialize(true);
else if (Engine.ScriptTypeMsgHandler !== Engine.scriptType())
initialize(true);
else {
// We are in singleton message handler context
// NOTE: debug (including level) is propagated from main script to singleton
// context by javascript module
debug = Engine.debugEnabled();
}
//
// From this point the code is used only if script is loaded as static script
//
function onStatus(msg)
{
if (msg.module && ("handler_singleton_sample" != msg.module))
return false;
var str = "";
var perf = new Engine.SharedVars("handler_singleton_sample_perf");
var perf = perf.getVars();
for (var name in perf)
str += "," + name + "=" + perf[name];
msg.retValue(msg.retValue() + "name=handler_singleton_sample,type=misc;" + str.substr(1) + "\r\n");
return !!msg.module;
}
function onDebug(msg)
{
if (var dbg = msg.line) {
Engine.setDebug(dbg);
debug = Engine.debugEnabled();
}
msg.retValue("Sample Singleton Message Handler debug " + Engine.debugEnabled() + " level " +
Engine.debugLevel() + "\r\n");
return true;
}
function onReload(msg)
{
if (msg.plugin && ("handler_singleton_sample" != msg.plugin))
return false;
initialize();
return !!msg.plugin;
}
function initialize(first)
{
Engine.output("Initializing module Sample Singleton Message Handler");
if (first) {
// If script is configured to use multiple instances it can only install singleton for the first one
// It makes no sense to configure multiple instances for it!
if (!Message.installSingleton) {
Engine.debug(Engine.DebugConf,"Refusing to start Sample Singleton Message Handler",
"singleton handler install not available");
return;
}
// Initial debug
Engine.setDebug("level 10");
debug = Engine.debugEnabled();
// Message.installSingleton(func,context,name[,priority[,filterName[,filterValue[,params]]])
// Do not load extensions (default: javascript.conf global setup)
// Extensions are loaded using a script.init message. C++ modules may push javascript objects in script context
var params = {load_extensions:false};
// Sample call.route filter. Handle if not explicitly disabled. Use Engine.MatchingItem
var mi = {name:"handler_singleton_sample",value:/^$(true|yes|)$/};
var filter = new Engine.MatchingItem(mi);
Message.installSingleton(onCallRoute,"call_route","call.route",30,filter,undefined,params);
// Sample test filter. Handle if not explicitly disabled. Use basic
Message.installSingleton(onTest,"test","test",70,"handler_singleton_sample",/^$(true|yes|)$/,params);
// Module messages
Message.install(onStatus,"engine.status");
Message.install(onDebug,"engine.debug",150,"module","handler_singleton_sample");
Message.install(onReload,"engine.init");
}
// Init and set shared object holding route data
var routeData = {
local_route_called: /^(test|other)$/,
database: {account:"some_db_account",query:"select target from route"},
};
var shared_objects = new Engine.SharedObjects;
shared_objects.set("handler_singleton_sample_route_data",routeData);
}
See also