In addition to limiter endpoints for platform rate limiting, the SIP RA also provides a Java API allowing the implementation of custom overload control plugins. Overload control plugins are supplied by an application SBB, and are invoked by the SIP RA for every request received from the network prior to the application of regular endpoint rate limiting.

Overload control plugin registration

An overload control plugin is registered with a SIP RA instance using the registerOverloadControlPlugin operation. At most one plugin may be registered with each SIP RA instance in each JVM at any one time. Attempting to register a plugin while one is already registered will replace the existing registration with the new one.

Overload control plugins are expected to be registered with the SIP RA by an SBB. For example, the root SBB of a service may respond to a ServiceNodeStartedEvent by instantiating a plugin implementation object and registering that with the SIP RA when the service is activated. Later, when the service is deactivated and the ServiceNodeActivity ends, the SBB would deregister the plugin from the SIP RA.

Overload control plugin API

The Java API for the overload control plugin is given by SipOverloadControlPlugin The interface contains only a single operation - invoke() - which the SIP RA invokes when it needs an overload control instruction for an inbound network request. An overload control plugin may return one of four limiter instructions for each request it considers:

 accept

The request is accepted by the overload control plugin and should be passed up the software stack as a normal request. If the request is an initial request, this means it is subject to regular SIP RA rate limiting.

 exempt

The request is accepted by the overload control plugin is considered exempt from regular SIP RA rate limiting. This means it must be passed up the software stack into the Session and Application layers.

 reject

The request has been rejected by the overload control plugin and a SIP error response should be returned to the network. The overload control plugin may provide the response to use for the request, otherwise the SIP RA will use its default rate limiting error response instead - 503 (Service Unavailable).

Caution must be used when rejecting requests, as doing so may break various call flows and application state machines. As a rule of thumb, mid-dialog requests should not be rejected, and careful consideration should be given to which initial requests should be exempt from overload control.

 discard

The request and any associated server transaction state should be discarded with no SIP response sent. Discarded requests may be observable through side effects such as statistics, TCP sequence numbers, etc.

Extreme caution must be used when discarding requests. The specific algorithm must be very well thought through. If in doubt, never discard a request.

Warning

Automatic 100 Trying support and discards

If your overload control plugin can return a discard result, you should disable Automatic100TryingSupport on the SIP RA, and make your SIP service send 100 Trying responses for INVITEs itself.

If you do not do this, the SIP RA will send a 100 Trying response for the INVITE before your overload control plugin is invoked. If the plugin then returns discard, the request is dropped, but there is no standard timer in the INVITE client transaction state machine to ensure that the client’s state is cleaned up (implementations may vary).

With Automatic100TryingSupport disabled, if a plugin discards the request the client transaction will time out correctly (Timer B) because a 100 Trying response has not been seen yet. When your service sends 100 Trying this means the request is going to be processed, and the client transaction state machine can safely advance knowing that a final response will arrive eventually.

Example

A simple plugin

Below is the source of a simple plugin that just accepts all requests. A more realistic plugin should use some rate limiting logic to decide whether to accept or reject the request.

package com.opencloud.slee.services.example.sipoverload;

import javax.slee.facilities.Tracer;
import javax.sip.message.Request;
import com.opencloud.javax.sip.slee.overload.LimiterInstruction;
import com.opencloud.javax.sip.slee.overload.SipOverloadControlContext;
import com.opencloud.javax.sip.slee.overload.SipOverloadControlPlugin;


public class SimpleOverloadControlPlugin implements SipOverloadControlPlugin {
    public SimpleOverloadControlPluginImpl(Tracer tracer) {
        this.tracer = tracer;
    }

    @Override
    public LimiterInstruction invoke(SipOverloadControlContext context, Request request) {
        tracer.info("Handling request for overload control: " + request);
        // just accept it
        return LimiterInstruction.accept();
    }

    private final Tracer tracer;
}

Registering the plugin

The example SBB shows an example of registering an overload control plugin with the SIP RA.

A plugin may be given references to non-transactional SBB resources such as tracers (for logging) and usage parameter sets (for stats). Consequently, it is important that plugin objects are deregistered from the SIP RA no later than associated service deactivation to avoid potential memory leaks if the service is later uninstalled from the SLEE.

package com.opencloud.slee.services.example.sipoverload;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.slee.ActivityContextInterface;
import javax.slee.ActivityEndEvent;
import javax.slee.CreateException;
import javax.slee.RolledBackContext;
import javax.slee.Sbb;
import javax.slee.SbbContext;
import javax.slee.facilities.Tracer;
import com.opencloud.javax.sip.slee.OCSleeSipProvider;
import com.opencloud.rhino.slee.servicenodeactivity.ServiceNodeActivity;
import com.opencloud.rhino.slee.servicenodeactivity.ServiceNodeStartedEvent;


public abstract class SimpleOverloadControlPluginSbb implements Sbb {
    private OCSleeSipProvider provider;
    private Tracer tracer;

    @Override
    public void setSbbContext(SbbContext context) {
        tracer = context.getTracer("");
        try {
            provider = (OCSleeSipProvider) new InitialContext().lookup("slee/resources/ocsip/provider");
        }
        catch (NamingException e) {
            throw new RuntimeException("JNDI lookup error", e);
        }
    }

    public void onServiceNodeStartedEvent(ServiceNodeStartedEvent e, ActivityContextInterface aci) {
        tracer.info("Registering overload control plugin");
        provider.registerOverloadControlPlugin(new SimpleOverloadControlPluginImpl(tracer));
    }

    public void onActivityEndEvent(ActivityEndEvent e, ActivityContextInterface aci) {
        if (aci.getActivity() instanceof ServiceNodeActivity) {
            tracer.info("Deregistering overload control plugin");
            provider.registerOverloadControlPlugin(null);
        }
    }

    ...
}

Deployment descriptors

The SBB’s deployment descriptor would need the following event definitions to receive the ServiceNodeStartedEvent and ActivityEndEvent events:

<sbb-jar>
  <sbb>

    ...

    <event event-direction="Receive" initial-event="True">
      <event-name>ServiceNodeStartedEvent</event-name>
      <event-type-ref>
        <event-type-name>
            com.opencloud.rhino.slee.servicenodeactivity.ServiceNodeStartedEvent
        </event-type-name>
        <event-type-vendor>com.opencloud</event-type-vendor>
        <event-type-version>1.0</event-type-version>
      </event-type-ref>
      <initial-event-select variable="ActivityContext"/>
    </event>

    <event event-direction="Receive">
      <event-name>ActivityEndEvent</event-name>
      <event-type-ref>
        <event-type-name>javax.slee.ActivityEndEvent</event-type-name>
        <event-type-vendor>javax.slee</event-type-vendor>
        <event-type-version>1.0</event-type-version>
      </event-type-ref>
    </event>

    ...

  </sbb>
</sbb-jar>

Custom reject responses

An overload control plugin that returns a REJECT instruction also has the option to return a custom response message to be sent to the network in place of the default overload control response. The example code below illustrates how an overload control plugin can do this.

@Override
public LimiterInstruction invoke(SipOverloadControlContext context, Request request) {
    // status code should be a 3xx, 4xx, or 5xx response code
    int rejectStatusCode = XXX;

    try {
        // create response object
        Response rejectResponse = context.createResponse(request, rejectStatusCode);

        // set any required headers as needed
        ReasonHeader reasonHeader = context.getHeaderFactory().createReasonHeader(...);
        rejectResponse.setHeader(reasonHeader);
        ...

        // return reject instruction with this response
        return LimiterInstruction.reject(rejectResponse);

    } catch (Exception e) {
        // Any exception thrown by the plugin will trigger the default accept handling
        throw new RuntimeException("Error creating reject response", e);
    }
}
Previous page Next page