Basic information about the context usage …
MqContextC - the class known as ctx or context is the application-handle of the application-server and also the main data-handle …
The context is the package-item with the required features and created by the implementation-layer-programmer. The context can be a client or a server.
The client-context-creation is triggerd by the software-workflow on demand. The client is calling the MqLinkCreate to create a connection to the server using the connection-arguments to specify the target.
The life-cycle of a client is:
ContextCreate | create and initialize the MqContextC ... |
LinkCreate | make ctx to a parent-context and setup a new client-server-link … |
SendTT | MqContextC - append a native PRIMITIVE TYPE value to the send-data-package … |
ReadTT | read a PRIMITIVE TYPE from the read-data-package … |
LinkDelete | close the client-server-link … |
ContextDelete | Destructor - delete a MqContextC instance … |
Exit | delete the context and exit the current process or thread … |
The server-context-creation is always triggerd by the MqLinkCreate command of the client. The server is usually using a factory-constructor to call the MqContextCreate and finally to call the MqContextDelete.
The server-context is fully under control of the client.
The life-cycle of a server is:
SETUP | define a class and add the setup/cleanup code |
MqServerSetupIF | define the server-setup-interface (callback) used on startup … |
ServiceCreate | create a link between a service-token and a service-callback … |
MqServerCleanupIF | define the server-cleanup-interface (callback) used on cleanup … |
ServiceDelete | delete a service. … |
STARTUP | define the factory and start the listener |
FactoryAdd | add a new MqFactoryC identified by factory-identifier and defined by factory-constructor … |
FactoryNew | create a new MqContextC from a MqFactoryC … |
LinkCreate | make ctx to a parent-context and setup a new client-server-link … |
ProcessEvent | enter the event-loop and wait for an incoming service-request. … |
WORK | process the service-calls and exit on end |
ReadTT | read a PRIMITIVE TYPE from the read-data-package … |
SendTT | MqContextC - append a native PRIMITIVE TYPE value to the send-data-package … |
Exit | delete the context and exit the current process or thread … |
command | alias |
---|---|
(constructor,static,runtime) MQ_CTX MqContextCreate(MQ_CTX const tmpl) | no |
(destructor,runtime) void MqContextDelete(MQ_CTX ctx) | no |
MqContextC - setup and manage a client-server-link …
The client-server-link connect two context, a client-parent-context and a server-parent-context. The link can be local (connect two context on the same host) or can be remote (connect two context on different hosts). On-Top the parent-context multiple child-context are allowed.
!on remote host! !on local host! server1---------x x----------server2 | | | | | child-context-1 child-context-2 | | | | | server parent-context-1-----x x-----parent-context-2 | | (MqConfigS::server) (example: MqConfigS::server --fork --uds … --file …) | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | (--tcp) (--pipe, --uds, --tcp) | | parent-context-1-----x x-----parent-context-2 | | | | client | child-context-1 child-context-2 | | | | | x------------x--------client-------x-------------x !on local host!
Definition of a "client-context"
Definition of a "server-context"
Definition of a "parent-context"
Definition of a "child-context"
MqContextC - create and manage a slave context …
The master-slave-link is used to create a mesh of nodes defined by different parent-context. The master control the slave.
The master-slave-link is used to perform the following tasks:
In difference to the client-server-link the master-slave-link connect two independent parent-context in the same process or thread (e.g. node). This leads to the restriction that only the master-context can be a server-context because only one server-context per node is possible.
node-0 | node-1/2 | node-3/4/5 =================================================================== | <- client/server link -> | <- client/server link -> | | <-- master/slave link --> | |- client1-0 -|- server3 ... |- server1 -| | |- client1-1 -|- server4 ... client0-0 -| |- server2 -|- client1-2 -|- server5 ...
Definition of the "master-context"
Definition of the "slave-context"
0
.Definition of the "worker-context"
0
./etc/services
0
slave-id | value | definition |
---|---|---|
MQ_SLAVE_MAX | 1024 | internal: the maximum slave-id … . |
MQ_SLAVE_USER | 10 | internal: start of user-defined-slave-id . |
MQ_SLAVE_LOOPBACK | 0 | internal: the loopback-slave-id, (call my own services) . |
MQ_SLAVE_FILTER | 1 | internal: the filter-slave-id, (on a master get the filter-slave) . |
MQ_SLAVE_MASTER | 1 | internal: the master-slave-id, (on a slave get the master) . |
MQ_SLAVE_OTHER | 1 | internal: on the master-ctx get the slave-ctx and on the slave-ctx get the master-ctx . |
range | definition |
---|---|
0 <= slave-id < MQ_SLAVE_MAX | range of valid slave-id's |
0 <= slave-id < MQ_SLAVE_USER | internale usage |
MQ_SLAVE_USER <= slave-id < MQ_SLAVE_MAX | external usage |
Definition of the "LOOPBACK" (0) slave
client | server | =========================================== | <--- client/server ---> | <-- loop --> | | <------ master/slave -----> | client -- | -- server -- | -- client -- # == == # server -- | -- client -- #
slave-id = 0
. MyLoopServer.c
→ create a new loop-server #include "common.h" // [MyLoopServerC_Define] typedef struct MyLoopServerS { struct MqContextS mqctx; ///< link to the \libmqmsgque object #define mydata_size 30 char mydata[mydata_size]; ///< define the "mydata" attribute } MyLoopServerC; // [MyLoopServerC_Define] // the MyLoopServerC class-type static MkThreadLocal MK_TYP MyLoopServerCTT = NULL; // service to serve all EXTERNAL requests for token "HLWO" static enum MkErrorE HLWO_srv ( MQ_SERVICE_CALL_ARGS ) { // get the "loopback" context MQ_CTX loop = MqSlaveGet_e(mqctx,MQ_SLAVE_LOOPBACK); // call the LOOP service on the SAME server MqSend_E(loop,"W","LOOP"); // answer HLWO with string-return from LOOP MqSend_E(mqctx, "R", "C", MqReadSTR_e(loop)); return MK_OK; error: return MqSendRETURN(mqctx); } // service to serve all INTERNAL requests for token "LOOP static enum MkErrorE LOOP_srv ( MQ_SERVICE_CALL_ARGS ) { // get the "master" context MyLoopServerC* master = (MyLoopServerC*)MqSlaveGetMaster(mqctx); // answer LOOP with data from MASTER->mydata attribute return MqSend(mqctx, "R", "C", master->mydata); } // define a service as link between the token "HLWO" and the callback "HLWO_srv" static enum MkErrorE ServerSetup ( MQ_SERVICE_CALL_ARGS ) { // EXTERNAL: link the "HLWO" service with "HLWO_srv" MqServiceCreate_E(mqctx, "HLWO", HLWO_srv, NULL, NULL, NULL); // INTERNAL: link the "LOOP" service with "LOOP_srv" MqServiceCreate_E(MqSlaveGet_e(mqctx,MQ_SLAVE_LOOPBACK), "LOOP", LOOP_srv, NULL, NULL, NULL); return MK_OK; error: return MkErrorStack_1X(mqctx); } // [MyLoopServerC_Create] enum MkErrorE MyLoopServerFactory ( MQ_CALLBACK_FACTORY_CTOR_ARGS ) { // create new instance using the MyLoopServerCTT class-type MQ_CTX const mqctx = *contextP = MqContextCreate(MyLoopServerCTT,tmpl); // initialize the new context MqConfigSetServerSetup (mqctx, ServerSetup, NULL, NULL, NULL); // cast the libmqmsgque-context into the MyLoopServerC-context MyLoopServerC* mqctxC = (MyLoopServerC*)mqctx; // initialize the "mydata" attribute strncpy(mqctxC->mydata,"Hello World",mydata_size); mqctxC->mydata[mydata_size-1] = '\0'; return MK_OK; } // [MyLoopServerC_Create] int main (int argc, MK_STRN argv[]) { AllRtSetup_NULL; // [MyLoopServerC_Init] // initialize the MyLoopServer class-type with existing MqContextC_TT class-type MyLoopServerCTT = MkTypeDup2(MqContextC_TT,"MyLoopServerC"); MyLoopServerCTT->objsize = sizeof(struct MyLoopServerS); // [MyLoopServerC_Init] // setup commandline arguments for later use MK_BFL largv = MkBufferListCreateVC(argc, argv); MQ_CTX mqctx = NULL; // create "MyLoopServer" factory… and make it to the default. MqFactoryDefault(MqFactoryAdd_2(MyLoopServerFactory,"MyLoopServer")); // inspect commandline-argument for the "factory" to choose… and create a object MqFactoryNew_E (MqFactoryGetCalledL(largv), NULL, &mqctx); // start listen for incoming call's MqLinkCreate_E (mqctx, largv); MqCheckForLeftOverArguments_E (mqctx, largv); MqProcessEvent_E (mqctx,MQ_WAIT_FOREVER,MK_TIMEOUT_DEFAULT); error: MkBufferListDelete(largv); MqExit_1(mqctx); }
Performance analyse
Nhi1Exec perfclient.c --parent --wrk ? @ perfserver.c
Nhi1Exec -r=uds perfserver.c --spawn|fork|thread
Nhi1Exec -r=uds perfclient.c --parent --wrk ?
perfclient worker perfserver ========== ====== ========== | |- loop --wrk x |- MqSlaveWorker(...) -> worker[1] |- MqSend(worker[1],"E","STR0..") -> PerfWorker_I160(...) |- loop endless |- MqContextCreate(...) |- MqLinkCreate(...) <-> MqContextCreate(...) |- MqContextDelete(...) <-> MqContextDelete(...) |- sleep x sec |- loop --wrk x |- MqSend(worker[1],"C"..,"END0") -> PerfWorker_END0(...) | |- stop loop |- "callback" - add number to all <- |- return #context
setup | –wrk | # worker-context | performance | info |
---|---|---|---|---|
pipe | 1 | 2500 | 1000 | the pipe start a new worker-context with spawn |
spawn | 1 | 2500 | <1000 | same as pipe but use network-protocoll |
fork | 1 | 3800 | 4000 | the fork is faster than spawn |
thread | 1 | 16500 | 9000 | the thread is faster than fork |
pipe | 4 | 8000 | 4500 | the worker scale linear up to number of processors |
spawn | 4 | 7600 | <4500 | - |
fork | 4 | 23200 | 11500 | - |
thread | 4 | 55500 | 27500 | - |
pipe | 8 | 10000 | 5500 | the additional scaling up to the max hyper-threading does not really help |
spawn | 8 | 9100 | <5500 | - |
fork | 8 | 23200 | 11500 | - |
thread | 8 | 55500 | 27500 | - |