
uk.co.real_logic.aeron.Aeron Maven / Gradle / Ivy
Show all versions of aeron-client Show documentation
/*
* Copyright 2014 - 2016 Real Logic Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.co.real_logic.aeron;
import uk.co.real_logic.aeron.exceptions.DriverTimeoutException;
import uk.co.real_logic.agrona.*;
import uk.co.real_logic.agrona.concurrent.*;
import uk.co.real_logic.agrona.concurrent.broadcast.BroadcastReceiver;
import uk.co.real_logic.agrona.concurrent.broadcast.CopyBroadcastReceiver;
import uk.co.real_logic.agrona.concurrent.ringbuffer.ManyToOneRingBuffer;
import uk.co.real_logic.agrona.concurrent.ringbuffer.RingBuffer;
import java.nio.MappedByteBuffer;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static uk.co.real_logic.agrona.IoUtil.mapExistingFile;
/**
* Aeron entry point for communicating to the Media Driver for creating {@link Publication}s and {@link Subscription}s.
* Use an {@link Aeron.Context} to configure the Aeron object.
*
* A client application requires only one Aeron object per Media Driver.
*/
public final class Aeron implements AutoCloseable
{
/**
* The Default handler for Aeron runtime exceptions.
* When a {@link uk.co.real_logic.aeron.exceptions.DriverTimeoutException} is encountered, this handler will
* exit the program.
*
* The error handler can be overridden by supplying an {@link Aeron.Context} with a custom handler.
*
* @see Aeron.Context#errorHandler(ErrorHandler)
*/
public static final ErrorHandler DEFAULT_ERROR_HANDLER =
(throwable) ->
{
throwable.printStackTrace();
if (throwable instanceof DriverTimeoutException)
{
System.err.printf("\n***\n*** Timeout from the Media Driver - is it currently running? Exiting.\n***\n");
System.exit(-1);
}
};
private static final long IDLE_SLEEP_NS = TimeUnit.MILLISECONDS.toNanos(4);
private static final long KEEPALIVE_INTERVAL_NS = TimeUnit.MILLISECONDS.toNanos(500);
private static final long INTER_SERVICE_TIMEOUT_NS = TimeUnit.SECONDS.toNanos(10);
private static final long PUBLICATION_CONNECTION_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
private final ClientConductor conductor;
private final AgentRunner conductorRunner;
private final Context ctx;
Aeron(final Context ctx)
{
ctx.conclude();
this.ctx = ctx;
conductor = new ClientConductor(
ctx.epochClock,
ctx.nanoClock,
ctx.toClientBuffer,
ctx.logBuffersFactory,
ctx.countersValuesBuffer(),
new DriverProxy(ctx.toDriverBuffer),
ctx.errorHandler,
ctx.availableImageHandler,
ctx.unavailableImageHandler,
ctx.keepAliveInterval(),
ctx.driverTimeoutMs(),
ctx.interServiceTimeout(),
ctx.publicationConnectionTimeout());
conductorRunner = new AgentRunner(ctx.idleStrategy, ctx.errorHandler, null, conductor);
}
/**
* Create an Aeron instance and connect to the media driver with a default {@link Context}.
*
* Threads required for interacting with the media driver are created and managed within the Aeron instance.
*
* @return the new {@link Aeron} instance connected to the Media Driver.
*/
public static Aeron connect()
{
return new Aeron(new Context()).start();
}
/**
* Create an Aeron instance and connect to the media driver.
*
* Threads required for interacting with the media driver are created and managed within the Aeron instance.
*
* @param ctx for configuration of the client.
* @return the new {@link Aeron} instance connected to the Media Driver.
*/
public static Aeron connect(final Context ctx)
{
return new Aeron(ctx).start();
}
/**
* Clean up and release all Aeron internal resources and shutdown threads.
*/
public void close()
{
conductorRunner.close();
ctx.close();
}
/**
* Add a {@link Publication} for publishing messages to subscribers.
*
* @param channel for receiving the messages known to the media layer.
* @param streamId within the channel scope.
* @return the new Publication.
*/
public Publication addPublication(final String channel, final int streamId)
{
return conductor.addPublication(channel, streamId);
}
/**
* Add a new {@link Subscription} for subscribing to messages from publishers.
*
* @param channel for receiving the messages known to the media layer.
* @param streamId within the channel scope.
* @return the {@link Subscription} for the channel and streamId pair.
*/
public Subscription addSubscription(final String channel, final int streamId)
{
return conductor.addSubscription(channel, streamId);
}
private Aeron start()
{
final Thread thread = new Thread(conductorRunner);
thread.setName("aeron-client-conductor");
thread.start();
return this;
}
/**
* This class provides configuration for the {@link Aeron} class via the {@link Aeron#connect(Aeron.Context)}
* method and its overloads. It gives applications some control over the interactions with the Aeron Media Driver.
* It can also set up error handling as well as application callbacks for image information from the
* Media Driver.
*/
public static class Context extends CommonContext
{
private final AtomicBoolean isClosed = new AtomicBoolean(false);
private EpochClock epochClock;
private NanoClock nanoClock;
private IdleStrategy idleStrategy;
private CopyBroadcastReceiver toClientBuffer;
private RingBuffer toDriverBuffer;
private MappedByteBuffer cncByteBuffer;
private DirectBuffer cncMetaDataBuffer;
private LogBuffersFactory logBuffersFactory;
private ErrorHandler errorHandler;
private AvailableImageHandler availableImageHandler;
private UnavailableImageHandler unavailableImageHandler;
private long keepAliveInterval = KEEPALIVE_INTERVAL_NS;
private long interServiceTimeout = INTER_SERVICE_TIMEOUT_NS;
private long publicationConnectionTimeout = PUBLICATION_CONNECTION_TIMEOUT_MS;
/**
* This is called automatically by {@link Aeron#connect(Aeron.Context)} and its overloads.
* There is no need to call it from a client application. It is responsible for providing default
* values for options that are not individually changed through field setters.
*
* @return this Aeron.Context for method chaining.
*/
public Context conclude()
{
super.conclude();
try
{
if (null == epochClock)
{
epochClock = new SystemEpochClock();
}
if (null == nanoClock)
{
nanoClock = new SystemNanoClock();
}
if (null == idleStrategy)
{
idleStrategy = new SleepingIdleStrategy(IDLE_SLEEP_NS);
}
if (cncFile() != null)
{
cncByteBuffer = mapExistingFile(cncFile(), CncFileDescriptor.CNC_FILE);
cncMetaDataBuffer = CncFileDescriptor.createMetaDataBuffer(cncByteBuffer);
final int cncVersion = cncMetaDataBuffer.getInt(CncFileDescriptor.cncVersionOffset(0));
if (CncFileDescriptor.CNC_VERSION != cncVersion)
{
throw new IllegalStateException("aeron cnc file version not understood: version=" + cncVersion);
}
}
if (null == toClientBuffer)
{
final BroadcastReceiver receiver = new BroadcastReceiver(
CncFileDescriptor.createToClientsBuffer(cncByteBuffer, cncMetaDataBuffer));
toClientBuffer = new CopyBroadcastReceiver(receiver);
}
if (null == toDriverBuffer)
{
toDriverBuffer = new ManyToOneRingBuffer(
CncFileDescriptor.createToDriverBuffer(cncByteBuffer, cncMetaDataBuffer));
}
if (countersMetaDataBuffer() == null)
{
countersMetaDataBuffer(CncFileDescriptor.createCountersMetaDataBuffer(cncByteBuffer, cncMetaDataBuffer));
}
if (countersValuesBuffer() == null)
{
countersValuesBuffer(CncFileDescriptor.createCountersValuesBuffer(cncByteBuffer, cncMetaDataBuffer));
}
interServiceTimeout = CncFileDescriptor.clientLivenessTimeout(cncMetaDataBuffer);
if (null == logBuffersFactory)
{
logBuffersFactory = new MappedLogBuffersFactory();
}
if (null == errorHandler)
{
errorHandler = DEFAULT_ERROR_HANDLER;
}
if (null == availableImageHandler)
{
availableImageHandler = (image) -> { };
}
if (null == unavailableImageHandler)
{
unavailableImageHandler = (image) -> { };
}
}
catch (final Exception ex)
{
System.err.printf("\n***\n*** Failed to connect to the Media Driver - is it currently running?\n***\n");
throw new IllegalStateException("Could not initialise communication buffers", ex);
}
return this;
}
/**
* Set the {@link EpochClock} to be used for tracking wall clock time when interacting with the driver.
*
* @param clock {@link EpochClock} to be used for tracking wall clock time when interacting with the driver.
* @return this Aeron.Context for method chaining
*/
public Context epochClock(final EpochClock clock)
{
this.epochClock = clock;
return this;
}
/**
* Set the {@link NanoClock} to be used for tracking high resolution time.
*
* @param clock {@link NanoClock} to be used for tracking high resolution time.
* @return this Aeron.Context for method chaining
*/
public Context nanoClock(final NanoClock clock)
{
this.nanoClock = clock;
return this;
}
/**
* Provides an IdleStrategy for the thread responsible for communicating with the Aeron Media Driver.
*
* @param idleStrategy Thread idle strategy for communication with the Media Driver.
* @return this Aeron.Context for method chaining.
*/
public Context idleStrategy(final IdleStrategy idleStrategy)
{
this.idleStrategy = idleStrategy;
return this;
}
/**
* This method is used for testing and debugging.
*
* @param toClientBuffer Injected CopyBroadcastReceiver
* @return this Aeron.Context for method chaining.
*/
public Context toClientBuffer(final CopyBroadcastReceiver toClientBuffer)
{
this.toClientBuffer = toClientBuffer;
return this;
}
/**
* This method is used for testing and debugging.
*
* @param toDriverBuffer Injected RingBuffer.
* @return this Aeron.Context for method chaining.
*/
public Context toDriverBuffer(final RingBuffer toDriverBuffer)
{
this.toDriverBuffer = toDriverBuffer;
return this;
}
/**
* This method is used for testing and debugging.
*
* @param logBuffersFactory Injected LogBuffersFactory
* @return this Aeron.Context for method chaining.
*/
public Context bufferManager(final LogBuffersFactory logBuffersFactory)
{
this.logBuffersFactory = logBuffersFactory;
return this;
}
/**
* Handle Aeron exceptions in a callback method. The default behavior is defined by
* {@link Aeron#DEFAULT_ERROR_HANDLER}.
*
* @param errorHandler Method to handle objects of type Throwable.
* @return this Aeron.Context for method chaining.
* @see uk.co.real_logic.aeron.exceptions.DriverTimeoutException
* @see uk.co.real_logic.aeron.exceptions.RegistrationException
*/
public Context errorHandler(final ErrorHandler errorHandler)
{
this.errorHandler = errorHandler;
return this;
}
/**
* Set up a callback for when an {@link Image} is available.
*
* @param handler Callback method for handling available image notifications.
* @return this Aeron.Context for method chaining.
*/
public Context availableImageHandler(final AvailableImageHandler handler)
{
this.availableImageHandler = handler;
return this;
}
/**
* Set up a callback for when an {@link Image} is unavailable.
*
* @param handler Callback method for handling unavailable image notifications.
* @return this Aeron.Context for method chaining.
*/
public Context unavailableImageHandler(final UnavailableImageHandler handler)
{
this.unavailableImageHandler = handler;
return this;
}
/**
* Set the interval in nanoseconds for which the client will perform keep-alive operations.
*
* @param value the interval in nanoseconds for which the client will perform keep-alive operations.
* @return this Aeron.Context for method chaining.
*/
public Context keepAliveInterval(final long value)
{
keepAliveInterval = value;
return this;
}
/**
* Get the interval in nanoseconds for which the client will perform keep-alive operations.
*
* @return the interval in nanoseconds for which the client will perform keep-alive operations.
*/
public long keepAliveInterval()
{
return keepAliveInterval;
}
/**
* Set the amount of time, in milliseconds, that this client will wait until it determines the
* Media Driver is unavailable. When this happens a
* {@link uk.co.real_logic.aeron.exceptions.DriverTimeoutException} will be generated for the error handler.
*
* @param value Number of milliseconds.
* @return this Aeron.Context for method chaining.
* @see #errorHandler(ErrorHandler)
*/
public Context driverTimeoutMs(final long value)
{
super.driverTimeoutMs(value);
return this;
}
/**
* Return the timeout between service calls for the client.
*
* When exceeded, {@link #errorHandler} will be called and the active {@link Publication}s and {@link Image}s
* closed.
*
* This value is controlled by the driver and included in the CnC file.
*
* @return the timeout between service calls in nanoseconds.
*/
public long interServiceTimeout()
{
return interServiceTimeout;
}
/**
* @see CommonContext#aeronDirectoryName(String)
*/
public Context aeronDirectoryName(String dirName)
{
super.aeronDirectoryName(dirName);
return this;
}
/**
* Set the amount of time, in milliseconds, that this client will use to determine if a {@link Publication}
* has active subscribers or not.
*
* @param value number of milliseconds.
* @return this Aeron.Context for method chaining.
*/
public Context publicationConnectionTimeout(final long value)
{
publicationConnectionTimeout = value;
return this;
}
/**
* Return the timeout, in milliseconds, that this client will use to determine if a {@link Publication}
* has active subscribers or not.
*
* @return timeout in milliseconds.
*/
public long publicationConnectionTimeout()
{
return publicationConnectionTimeout;
}
/**
* Clean up all resources that the client uses to communicate with the Media Driver.
*/
public void close()
{
if (isClosed.compareAndSet(false, true))
{
IoUtil.unmap(cncByteBuffer);
super.close();
}
}
}
}