JAX-RS: Failover  

Starting from CXF 2.4.1, CXF JAX-RS clients can be configured for them to become failover-capable.
Core CXF Failover and Load Distribution features are supported.

Failover

Proxies and WebClients can be configured to failover to alternate addresses in case of connection-related failures.
Sequential and Random strategies are supported and implementers can build more sophisticated failover features by retrieving
alternate addresses from locators and other intermediaries.

Spring

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xmlns:clustering="http://cxf.apache.org/clustering"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
    
    <util:list id="addressList">
        <value>http://localhost:${testutil.ports.Server.1}/rest</value>
        <value>http://localhost:${testutil.ports.Server.2}/rest</value>
        <value>http://localhost:${testutil.ports.Server.3}/rest</value>
    </util:list>

    <bean id="SequentialAddresses" class="org.apache.cxf.clustering.SequentialStrategy">
        <property name="alternateAddresses">
            <ref bean="addressList"/>
        </property>
    </bean>

    <bean id="RandomAddresses" class="org.apache.cxf.clustering.RandomStrategy">
        <property name="alternateAddresses">
            <ref bean="addressList"/>
        </property>
    </bean>

    <jaxrs:client id="failoverSequential" address="http://localhost:8080/initialAddress">
       <jaxrs:features>
           <clustering:failover>
                <clustering:strategy>
                    <ref bean="SequentialAddresses"/>
                </clustering:strategy>
            </clustering:failover>
       </jaxrs:features>
    </jaxrs:client>

    <jaxrs:client id="failoverRandom" address="http://localhost:8080/initialAddress">
       <jaxrs:features>
           <clustering:failover>
                <clustering:strategy>
                    <ref bean="RandomAddresses"/>
                </clustering:strategy>
            </clustering:failover>
       </jaxrs:features>
    </jaxrs:client>

    <bean id="myWebClient" class="org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean" 
factory-method="createWebClient"> 
        <property name="address" value="http://some.base.url.that.responds/" /> 
        <property name="features">
            <ref bean="failover1"/> 
        </property>  
    </bean> 
</beans>

Code

org.apache.cxf.jaxrs.features.clustering.FailoverFeature feature = 
    new org.apache.cxf.jaxrs.features.clustering.FailoverFeature();
List<String> alternateAddresses = new ArrayList<String>();
// addresses are alternate addresses provided at start-up
for (String s : address) {
    alternateAddresses.add(s);
}
SequentialStrategy strategy = new SequentialStrategy();
strategy.setAlternateAddresses(alternateAddresses);
feature.setStrategy(strategy);

JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress("http://localhost:8080/inactive-replica");
List<Feature> features = new ArrayList<Feature>();
features.add(feature);
bean.setFeatures(features);

// create proxy:
bean.create(BookStore.class);
// create web client
bean.createWebClient();

Circuit Breakers Failover

The recent addition to CXF failover features is the implementation based on circuit breakers, more precisely Apache Zest (https://zest.apache.org/) library. It is available starting from CXF 3.2.0.

The configuration is very similar to the regular failover strategy, the only difference is usage of clustering:circuit-breaker-failover element.

Spring

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xmlns:clustering="http://cxf.apache.org/clustering"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
    
    <util:list id="addressList">
        <value>http://localhost:${testutil.ports.Server.1}/rest</value>
        <value>http://localhost:${testutil.ports.Server.2}/rest</value>
        <value>http://localhost:${testutil.ports.Server.3}/rest</value>
    </util:list>

    <bean id="SequentialAddresses" class="org.apache.cxf.clustering.SequentialStrategy">
        <property name="alternateAddresses">
            <ref bean="addressList"/>
        </property>
    </bean>

    <bean id="RandomAddresses" class="org.apache.cxf.clustering.RandomStrategy">
        <property name="alternateAddresses">
            <ref bean="addressList"/>
        </property>
    </bean>

    <jaxrs:client id="failoverSequential" address="http://localhost:8080/initialAddress">
       <jaxrs:features>
           <clustering:circuit-breaker-failover threshold="1" timeout="60000">
                <clustering:strategy>
                    <ref bean="SequentialAddresses"/>
                </clustering:strategy>
            </clustering:circuit-breaker-failover>
       </jaxrs:features>
    </jaxrs:client>

    <jaxrs:client id="failoverRandom" address="http://localhost:8080/initialAddress">
       <jaxrs:features>
           <clustering:circuit-breaker-failover threshold="1" timeout="60000">
                <clustering:strategy>
                    <ref bean="RandomAddresses"/>
                </clustering:strategy>
            </clustering:circuit-breaker-failover>
       </jaxrs:features>
    </jaxrs:client>

    <bean id="myWebClient" class="org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean" 
factory-method="createWebClient"> 
        <property name="address" value="http://some.base.url.that.responds/" /> 
        <property name="features">
            <ref bean="failover1"/> 
        </property>  
    </bean> 
</beans>

Circuit breakers have recommended themselves as a proven strategy to handle and monitor the failures related to external service calls, giving the external systems a time to recover by preventing endless retries or time-outing. For that reason, two configuration parameters could be tuned:

  • threshold: the error threshold to open the circuit breaker
  • timeout: the timeout to wait before trying the next invocation

Code

org.apache.cxf.jaxrs.features.clustering.CircuitBreakerFailoverFeature feature = 
    new org.apache.cxf.jaxrs.features.clustering.CircuitBreakerFailoverFeature(1, 60000);
List<String> alternateAddresses = new ArrayList<String>();
// addresses are alternate addresses provided at start-up
for (String s : address) {
    alternateAddresses.add(s);
}
SequentialStrategy strategy = new SequentialStrategy();
strategy.setAlternateAddresses(alternateAddresses);
feature.setStrategy(strategy);

JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress("http://localhost:8080/inactive-replica");
List<Feature> features = new ArrayList<Feature>();
features.add(feature);
bean.setFeatures(features);

// create proxy:
bean.create(BookStore.class);
// create web client
bean.createWebClient();

Load Distribution

CXF Load Distribution feature is a failover feature which can iterate where alternate addresses not only in case of failures but also after a successful invocation has been done.

Example:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xmlns:clustering="http://cxf.apache.org/clustering"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
    
    <util:list id="addressList">
        <value>http://localhost:${testutil.ports.Server.1}/rest</value>
        <value>http://localhost:${testutil.ports.Server.2}/rest</value>
        <value>http://localhost:${testutil.ports.Server.3}/rest</value>
    </util:list>

    <bean id="SequentialAddresses" class="org.apache.cxf.clustering.SequentialStrategy">
        <property name="alternateAddresses">
            <ref bean="addressList"/>
        </property>
    </bean>

    
    <jaxrs:client id="loadDistributorClient" address="http://localhost:8080/initialAddress">
       <jaxrs:features>
           <clustering:loadDistributor>
                <clustering:strategy>
                    <ref bean="RandomAddresses"/>
                </clustering:strategy>
            </clustering:loadDistributor>
       </jaxrs:features>
    </jaxrs:client>

the selector can be set from code like this:

org.apache.cxf.jaxrs.features.clustering.FailoverFeature feature = 
    new org.apache.cxf.jaxrs.features.clustering.FailoverFeature();
feature.setSelector(new org.apache.cxf.clustering.LoadDistributorTargetSelector());