Introduction
This article aims at introducing commonly used implementation of Guava Service
interface in version 13.0.1 with concrete examples from CDAP. This article contains material adapted or quoted from https://github.com/google/guava/wiki/ServiceExplained and Java docs in Guava source code, but citation is omitted for readability and convenience.
...
Guava Service
interface represents an object with an operational state, with start()
and stop()
methods that return a ListenableFuture
that represents the result of an asynchronous transition to a desired state. Synchronous startAndWait()
and stopAndWait()
methods waits for the transition to a desired state to complete. For example, webservers, RPC servers, and timers, can implement the Service
interface.Managing the state of services like these, which require proper startup and shutdown management, can be nontrivial, especially if multiple threads or scheduling is involved. Guava provides some skeletons to manage the state logic and synchronization details for you.
...
doStart()
:doStart()
is called directly by the first call tostart()
. The service is inService.State.STARTING
state whendoStart()
method is called.doStart()
should perform all initialization and then eventually callnotifyStarted()
to transition the service intoService.State.RUNNING
state if start up succeeded ornotifyFailed()
to transition the service intoService.State.FAILED
state if start up failed.doStop()
:doStop()
is called directly by the first call tostop()
only if the service inService.State.RUNNING
orService.State.STARTING
state, which meansdoStart()
must have completed successfully. YourdoStop()
method should shut down your service and then eventually callnotifyStopped()
if shutdown succeeded ornotifyFailed()
if shutdown failed.
...
When AbstractIdleService#start()
or AbstractIdleService#stop()
is called, a new thread will be created to call startUp().
When
AbstractIdleService#stop()
is called, another new thread will be created to call
or shutDown() respectively.
Call AbstractIdleService#startAbstractIdleService#startAndWait().get
or AbstractIdleService#stopAndWait()
to make sure the actions in startUp()
all completesure startUp()
or shutDown()
completes. shutDown()
will only be called if startUp()
completed successfully.
stopAndWaitATTENTION:
the default implementation of AbstractIdleService#executor(final State state)
returns an executor with a new thread to run startUp()
. Therefore, when AbstractIdleService#startAndWait()
returns, there is no guarantee that startUp()
has finished, unless AbstractIdleService#executor(final State state)
is overridden with an executor runs the runnable in the same thread. When AbstractIdleService#stopAndWait()
returns, there is no guarantee that shutDown()
has finished either for the same reason.If AbstractIdleService
is in Service.State.RUNNING
state, it will never transition into Service.State.TERMINATED
until stop()
or Even though AbstractIdleService
runs startUp()
or shutDown()
in a new thread, notifyStarted()
or notifyStopped()
is called after these methods respectively. AbstractIdleService#startAndWait()
or AbstractIdleService#stopAndWait()
will not return until notifyStarted()
or notifyStopped()
is called.
startUp()
has completed, but all other methods in AbstractNotificationService can only run after startUp()
completed. This is guaranteed by AbstractNotificationService#startAndWait()
being called before other methods in AbstractNotificationService are called. ...
executor()
returns the Executor
that will be used to run this service. The default implementation returns a new Executor
that sets the name of its threads to the string returned by getServiceName()
method. Subclass may override this method to use a custom Executor
.with a specific name, thread group or priority. startUp()
and shutDown()
methods will be run in the Executor returned by executor()
Unlike in AbstractExecutionThreadService
, it's not guaranteed that
startUp()
has been called when shutDown()
is called.
To describe the execution schedule, you must implement the scheduler()
method. Typically, you will use one of the provided schedules from AbstractScheduledService.Scheduler
, either newFixedRateSchedule(initialDelay, delay, TimeUnit)
or newFixedDelaySchedule(initialDelay, delay, TimeUnit)
, corresponding to the familiar methods in ScheduledExecutorService
. Custom schedules can be implemented using CustomScheduler
; see the Javadoc for details.
An example implementation of AbstractExecutionThreadService in CDAP is AbstractResourceReporter. It writes out resource metrics every reportInterval
seconds which is defined in scheduler()
.
...