This specification defines the programming model to build a resilient microservice. Microservices developed using this feature are guaranteed to be resilient despite of running environments.
This programming model is very flexible. All the annotation parameters are configurable and the annotations can be disabled as well.
This specification defines the annotations: @Asynchronous
, @Bulkhead
, @CircuitBreaker
, @Fallback
, @Retry
and @Timeout
.
Each annotation except @Asynchronous
has parameters.
All of the parameters are configurable.
The value of each parameter can be overridden individually or globally.
-
Override individual parameters The annotation parameters can be overwritten via config properties in the naming convention of
<classname>/<methodname>/<annotation>/<parameter>
.
The <classname>
and <methodname>
must be the class name and method name where the annotation is declared upon.
In the following code snippet, in order to override the maxRetries
for serviceB invocation to 100, set the config property com.acme.test.MyClient/serviceB/Retry/maxRetries=100
Similarly to override the maxDuration
for ServiceA, set the config property
com.acme.test.MyClient/serviceA/Retry/maxDuration=3000
-
Override parameters globally
If the parameters for a particular annotation need to be configured with the same value for a particular class, use the config property <classname>/<annotation>/<parameter>
for configuration.
For an instance, use the following config property to override all maxRetries
for Retry
specified on the class MyClient
to 100.
com.acme.test.MyClient/Retry/maxRetries=100
Sometimes, the parameters need to be configured with the same value for the whole microservice.
For an instance, all Timeout
needs to be set to 100ms
.
It can be cumbersome to override each occurrence of Timeout
.
In this circumstance, the config property <annotation>/<parameter>
overrides the corresponding parameter value for the specified annotation.
For instance, in order to override the maxRetries
for the Retry
to be 30
, specify the config property Retry/maxRetries=30
.
When multiple config properties are present, the property <classname>/<methodname>/<annotation>/<parameter>
takes precedence over <classname>/<annotation>/<parameter>
, which is followed by <annotation>/<parameter>
.
The override just changes the value of the corresponding parameter specified in the microservice and nothing more.
If no annotation matches the specified parameter, the property will be ignored.
For instance, if the annotation Retry
is specified on the class level for the class com.acme.ClassA
, which has method methodB, the config property com.acme.ClassA/methodB/Retry/maxRetries
will be ignored.
In order to override the property, the config property com.acme.ClassA/Retry/maxRetries
or Retry/maxRetries
needs to be specified.
package come.acme.test;
public class MyClient{
/**
* The configured the max retries is 90 but the max duration is 1000ms.
* Once the duration is reached, no more retries should be performed,
* even through it has not reached the max retries.
*/
@Retry(maxRetries = 90, maxDuration= 1000)
public void serviceB() {
writingService();
}
/**
* There should be 0-800ms (jitter is -400ms - 400ms) delays
* between each invocation.
* there should be at least 4 retries but no more than 10 retries.
*/
@Retry(delay = 400, maxDuration= 3200, jitter= 400, maxRetries = 10)
public Connection serviceA() {
return connectionService();
}
/**
* Sets retry condition, which means Retry will be performed on
* IOException.
*/
@Retry(retryOn = {IOException.class})
public void serviceB() {
writingService();
}
}
If an annotation is not present, the configured properties are ignored.
For instance, the property com.acme.ClassA/methodB/Retry/maxRetries
will be ignored if @Retry
annotation is not specified on the methodB
of com.acme.ClassA
.
Similarly, the property com.acme.ClassA/Retry/maxRetries
will be ignored if @Retry
annotation is not specified on the class com.acme.ClassA
as a class-level annotation.
Some service mesh platforms, e.g. Istio, have their own Fault Tolerance policy.
The operation team might want to use the platform Fault Tolerance.
In order to fulfil the requirement, MicroProfile Fault Tolerance provides a capability to have its resilient functionalities disabled except fallback
.
The reason fallback
is special is that the fallback
business logic can only be defined by microservices and not by any other platforms.
Setting the config property of MP_Fault_Tolerance_NonFallback_Enabled
with the value of false
means the Fault Tolerance is disabled, except @Fallback
.
If the property is absent or with the value of true
, it means that MicroProfile Fault Tolerance is enabled if any annotations are specified. For more information about how to set config properties, refer to MicroProfile Config specification.
In order to prevent from any unexpected behaviours, the property MP_Fault_Tolerance_NonFallback_Enabled
will only be read on application starting.
Any dynamic changes afterwards will be ignored until the application restarting.
Fault Tolerance policies can be disabled with configuration at method level, class level or globally for all deployment. If multiple configurations are specified, method-level configuration overrides class-level configuration, which then overrides global configuration. e.g.
-
com.acme.test.MyClient/methodA/CircuitBreaker/enabled=false
-
com.acme.test.MyClient/CircuitBreaker/enabled=true
-
CircuitBreaker/enabled=false
For the above scenario, all occurrences of CircuitBreaker
for the application are disabled except for those on the class com.acme.test.MyClient
. All occurrences of CircuitBreaker
on com.acme.test.MyClient
are enabled, except for the one on methodA
which is disabled.
Each policy can be disabled by using its annotation name.
-
Disabling a policy at Method level
A policy can be disabled at method level with the following config property and value:
<classname>/<methodname>/<annotation>/enabled=false
For instance the following config will disable circuit breaker policy on methodA
of com.acme.test.MyClient
class:
com.acme.test.MyClient/methodA/CircuitBreaker/enabled=false
Policy will be disabled even if the policy is also defined at class level
-
Disabling a policy at class level
A policy can be disabled at class level with the following config property and value:
<classname>/<annotation>/enabled=false
For instance the following config will disable fallback policy on com.acme.test.MyClient
class:
com.acme.test.MyClient/Fallback/enabled=false
Policy will be disabled on all class methods even if a method has the policy.
-
Disabling a policy globally
A policy can be disabled globally with the following config property and value:
<annotation>/enabled=false
For instance the following config will disable bulkhead policy globally:
Bulkhead/enabled=false
Policy will be disabled everywhere ignoring existing policy annotations on methods and classes.
If the above configurations patterns are used with a value other than true
or false
(i.e. <classname>/<methodname>/<annotation>/enabled=whatever
) non-portable behaviour results.
When the above property is used together with the property MP_Fault_Tolerance_NonFallback_Enabled
, the property MP_Fault_Tolerance_NonFallback_Enabled
has the lowest priority. e.g.
-
MP_Fault_Tolerance_NonFallback_Enabled=true
-
Bulkhead/enabled=true
In the above example, only Fallback
and Bulkhead
are enabled while the others are disabled.
The integration with MicroProfile Metrics can be disabled by setting a config property named MP_Fault_Tolerance_Metrics_Enabled
to the value false
.
If this property is absent or set to true
then the integration with MicroProfile Metrics will be enabled and the metrics listed earlier in this specification
will be added automatically for every method annotated with a @Retry
, @Timeout
, @CircuitBreaker
, @Bulkhead
or @Fallback
annotation.
In order to prevent any unexpected behaviour, the property MP_Fault_Tolerance_Metrics_Enabled
will only be read when the application starts.
Any dynamic changes afterwards will be ignored until the application is restarted.