YAMI - Yet Another Messaging Infrastructure


YAMI Home

Concept

Specification

Implementation

What next?

Questions
& Answers


Comments

Implementation

[Downloads] [Internal model] [Connection modes] [Performance]

This page contains an explanation of the YAMI Agent internal structure.
All the information here is valid for the YAMI C (and C++) library and (by inclusion) for libraries available for other languages (with appropriate limitations).
Note, that YAMI does not mandate any internal structure, only communication protocol is defined (see Specification). If any other YAMI implementation will be developed and published, this page will be restructured.

[Static structure] [Dynamic model]

Static structure

The figure below presents the Agent's static structure:

static structure

The Agent manages internally four kinds of control objects (each control object is active):

  • Sender. There is one Sender in the Agent. Its responsibility is to manage all the outgoing network traffic, which covers message sending, replying to incoming messages and sending out all controlling packets and notifications. The Sender uses a connection pool for best performance where the number of remote domains (to which the Sender sends packets) is small. Also, the Sender uses the Agent's internal address book to resolve domain names to network addresses and IP port numbers.
  • Receiver. There is one Receiver in the Agent. It is supposed to manage all the incoming network traffic. In essence, the Receiver has one listening socket (the IP port for the listening socket is given when the Agent is constructed), but each accepted connection is added to the connection pool (up to some limited number of connections). The Receiver can accept two kinds of packets: the incoming messages - they are queued for appropriate registered object, and the replies, rejections and other control packets that are received concerning the messages sent out earlier - these packets are appropriately converted into the status of the message.
  • Waker. There is one Waker in the Agent. Its responsibility is to wake (set their status) the messages sent out previously, if the user wishes so. The purpose for this functionality is to help the client code recover from the situation when there is no reply for the outgoing message and the client decided to wait for some change in the message's status. The Waker works with resolution of approximately 1s.
  • Dispatcher. There can be many Dispatchers in the Agent. For those objects that were registered as passive, the Dispatcher takes each incoming message and calls the external (external to the Agent) servant, with the incoming message as the parameter. The presence of Dispatcher allows to develop servers that are driven by messages coming from clients.

The figure above shows also entity objects managed internally by the Agent, which are (the address book already described):

  • Registered Object. The Receiver accepts incoming messages only for properly registered objects. If there is none, then the Agent does not accept any incoming messages. It is possible to register the object as passive, in which case the Agent delivers its incoming message to the client code.
  • IncomingMsg. Each incoming message is stored in the queue for one of the registered objects.
  • Message. This represents all the resources managed for the needs of message sent to some remote domain. Note, that the Message is not linked to any registered objects, which means that there is no need to register any object in order to sent out some message.

The IncomingMsg and Message are internal resources that are accesible to the client code via handles. The client code is responsible for their lifetime management, which means that the resources should be released by the client code, otherwise the resource leak may occur. Note, that even while the resource management is automatic for C++ or Python client code, it is still external to the Agent.

Dynamic model

The following sections describe the Agent's behavior in different Use Cases.

[Send New Message] [Receive Message] [Reply] [Receive Response] [Time Out] [Dispatch]

Send New Message

send new message

When the client code asks the Agent to send a new message, the Agent creates the new message entity object (allocates all the resources and so on), posts the request to the Sender and returns the message handle to the client. Note, that the Sender performs its actions asynchronously and the client does not have to wait (although it can) for the message to be actually sent or replied to.
The SendManager object is not depicted on the static model, since its only role is to provide ordering for all the actions required to send a message.

[up]

Receive Message

receive message

When the network module accepts new connection or discovers new packet on any connection in the pool, it reads the packet and hands it to the Receiver. If the packet is of type message, the Receiver tries to locate a registered object with appropriate name. If it finds one, it stores the message in the queue associated with the registered object. Otherwise (there is no such object) the unknown object notification is sent back to the remote Agent.
Similar notification mechanism is used when the Receiver discovers that the new message would overflow the queue set up for the registered object or when the parameter set has Level 2 and the local Agent is of Level 1 (but this indicates that the local Agent is badly registered at the remote site).

[up]

Reply

reply

The Reply collaboration shows that it is asynchronous action, too. The client code gives to the Sender all the necessary information (including parameter set, if any) and immediately returns. The Sender is responsible for delivering the response.

[up]

Receive Response

receive response

When the network packet arrives that was sent as a response (or rejection) to the message sent previously, the Receiver just updates the status of the Message object. In case of response, the parameter set (if any) is also stored with the Message object.

[up]

Time Out

timeout

If the client code wishes to wait until the response arrives for some message, it can subscribe the Message object to the Waker module. When the specified time out expires, the Waker module notifies the Message object so that the client code waiting for any change in the message's status can proceed. Usually client code decides to perform some recovery action if it discovers that after waiting the status is timed out.

[up]

Dispatch

dispatch

The Dispatcher module (there can be many of them) repeatedly asks objects registered as passive for any new incoming message and upcalls the appropriate servant (the one that was provided when the passive object was registered) provided by the client code (the upcall is performed through the PassiveObject interface, which is implemented by the servant).
The diagram above shows only one Dispatcher module collaborating with one registered object, but this collaboration is in fact much wider: one Dispatcher is not assigned to any of the passive objects and it can dispatch messages for any object registered. Also, when more than one Dispatcher is involved, a couple of messages (even for the same passive object, if there are many waiting in the same queue) can be upcalled at the same time.

[up]