11.2. The Port Service

What is the port service?

The port service is designed to address the problem of clashing IP port values, which frequently arises in a production environment. The following kinds of problem commonly arise:
  • Ports clashing with third-party services—a server machine in a production environment often has multiple services deployed on it, with a wide range of IP ports in use. In this environment, there is a relatively large risk that a Fabric container could clash with existing IP ports.
  • Ports clashing with other Fabric containers—when multiple Fabric containers are deployed on the same host, it is necessary to configure their standard services with different IP ports. Setting the IP ports manually would be a considerable nuisance (and error prone).
  • Ports clashing within a container—a port clash can also occur within a single container, if multiple services are competing for the same ports (for example, multiple routes binding to the same ports). Because Fabric containers are highly dynamic, we need to be able to prevent port clashes in this case, and ports must be allocated and de-allocated as services come and go.
The port service addresses this problem by taking over the process of allocating ports. A service that uses the port service can specify a range of ports that it is willing to use, and the port service takes care of allocating a port that does not clash with any of the existing services.

Benefits of the port service

The port service offers the following benefits at run time:
  • Avoiding port clashes for standard container services
  • Avoiding port clashes for custom services

Avoiding port clashes for standard container services

When you start up multiple containers on the same host, the port service ensures that the containers automatically choose different IP ports for their standard services, thus avoiding port clashes between containers. You get this benefit for free: the standard container services are already configured to use the port service.

Avoiding port clashes for custom services

You can also use the port service for your own applications, enabling your custom services to avoid port clashes. This requires you to configure your custom services, as appropriate, to use the port service.

Using the port service in your own applications

To use the port service in your own application, proceed as follows:
  1. Use the OSGi Config Admin service to define a key, whose value is a port range. Use the following syntax to define a key:
    KeyID = ${port:MinValue,MaxValue}
    The preceding syntax defines the key, KeyID, where MinValue specifies the minimum value of the IP port, and MaxValue specifies the maximum value of the IP port. You can create this key using the standard Karaf commands for editing persistent IDs (PIDs) and their keys (using the fabric:profile-edit command with the --pid option in a Fabric container).
    For example, if you are logged into a Fabric container, you can see that the default profile defines the key, org.osgi.service.http.port, which specifies the container's Jetty port, as follows:
    FuseFabric:karaf@root> fabric:profile-display default
    ...
    PID: org.ops4j.pax.web
      org.ops4j.pax.web.config.checksum ${checksum:profile:jetty.xml}
      org.ops4j.pax.web.config.url profile:jetty.xml
      javax.servlet.context.tempdir ${karaf.data}/pax-web-jsp
      org.osgi.service.http.port ${port:8181,8282}
  2. In your application's XML configuration (either Spring XML or Blueprint XML), replace the literal port value in the service's address by a property placeholder—for example, ${org.osgi.service.http.port}—which substitutes the value of the key defined in step 1.
    For a complete example of how to configure the property placeholder, see Section 11.3, “Using the Port Service”.

How the port service allocates a port

Given a service with a port range (for example, ${port:9090,9190}) running on a specific target host, when you start up the service for the first time, the port service allocates a port as follows:
  1. Determines which ports in the range are already in use on the target host (whether local or remote), by actually trying to bind to the ports.
  2. Checks the registered ports in the ZooKeeper registry for all of the containers deployed on the target host (even if the containers are currently not running).
  3. Allocates the first free port, within the specified range, that does not clash with any of the ports discovered in steps 1 and 2.

How allocated ports are stored

Allocated ports are stored permanently in the ZooKeeper registry, under the following registry node:
/fabric/registry/ports/
Each key value, KeyID, is filed under its corresponding persistent ID, PID, and container name, ContainerName, as follows:
/fabric/registry/ports/containers/ContainerName/PID/KeyID
For example, given the child container, Child1, the key for the child container's Jetty port would be stored in the following ZooKeeper node:
/fabric/registry/ports/containers/Child1/org.ops4j.pax.web/org.osgi.service.http.port

Keys used by the standard container services

Some of keys used by standard container services are as follows:
/fabric/registry/ports/containers/ContainerName/org.apache.karaf.shell/sshPort
/fabric/registry/ports/containers/ContainerName/org.ops4j.pax.web/org.osgi.service.http.port
/fabric/registry/ports/containers/ContainerName/org.apache.karaf.management/rmiServerPort
/fabric/registry/ports/containers/ContainerName/org.apache.karaf.management/rmiRegistryPort

Behavior upon stopping and restarting a container

When you stop a container, the ports used by that container are stored in the ZooKeeper registry and continue to be reserved for that container by the port service. Subsequently, when you restart the container, Fabric reads the port values stored in ZooKeeper and restarts the container's services using the stored values. This behavior has the following consequences:
  • The ports used by the container's services remain constant (after the initial allocation has occurred). You can advertise the ports to clients and be confident that the ports will remain valid over the long term.
  • If, while the container is stopped, another service binds to one of the container's ports, there is a port clash when the container restarts, and the affected service fails to start (but at least we can guarantee that Fabric will not cause such a clash, because Fabric deliberately avoids re-using allocated container ports).

Deallocating ports

When you destroy a container (by invoking the fabric:container-delete command), Fabric deallocates all of the ports assigned to that container, so that they become available for use again by services in other containers. In other words, when the ContainerName container is deleted, all of the key entries under /fabric/registry/ports/containers/ContainerName are deleted from the ZooKeeper registry.