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 MUST callnotifyStarted()
to transition the service intoService.State.RUNNING
state if start up succeeded or .startAndWait()
will only return afternotifyStarted()
returns successfully. Any Throwable thrown bydoStart()
will incurnotifyFailed()
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. Your YourdoStop()
method method should shut down your service and then eventually call eventually MUST callnotifyStopped()
if shutdown succeeded or .startAndWait()
will only return afternotifyStarted()
returns successfully. Any Throwable thrown bydoStop()
will incurnotifyFailed()
if shutdown failed.Service.State.FAILED
state.
start()
and stop()
methods of AbstractService run in the same thread in which they are called.
Your doStart()
and doStop()
methods should be fast, because the service object is locked when doStart()
or doStop()
is running, and no other state transition can happen when the service object is locked. If you need to do expensive initialization, such as reading files, opening network connections, or any operation that might block, you should consider moving that work to another thread.
...
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()
.
...