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 15: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