Skip to content

spring integration leader starter

Patrick D edited this page Oct 1, 2018 · 28 revisions

Installation and Configuration

Add the following module as a dependency:

<dependency>
   <groupId>com.mimacom.spring</groupId>
   <artifactId>spring-integration-leader-starter</artifactId>
   <version>xxx</version>
</dependency>

Leader Election Provides

spring-integration-leader-starter` is only going to configure itself if one of the following technologies is available and configured

  • Zookeeper
  • Hazelcast
  • Lock-Registry

Info: If multiple of the mentioned providers are configured then the first one wins (as ordered as in the list above). However you can explicitly define the provider type using the following property: spring-integration.leader.type=<provider>. Whereas <provider> can be 'zookeeper', 'hazelcast' or 'lock-registry'

Zookeeper based Leader-Election

Add the following dependency to your project:

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-zookeeper</artifactId>
</dependency>

Provide a CuratorFramework bean by using a spring-boot auto-configuration aware module such as the following:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-zookeeper-core</artifactId>
</dependency>

... or manually define the bean

@Configuration
public class MyZookeeperConfiguration {

   @Bean
   CuratorFrameworkFactoryBean curatorFramework() {
       return new CuratorFrameworkFactoryBean("localhost:2181");
   }

}

CuratorFrameworkFactoryBean is part of the spring-integration-zookeeper module

Zookeeper leader specific configuration

The zookeeper.path property defined where the zookeeper leaders are going to store their information

spring-integration:
  leader:
    zookeeper:
      path: /custom/zookeeper/path 

If no path has been defined spring-integration-leader-starter is going to build the path like this

  • If spring.application.name has been defined: /spring/leader/<spring.application.name>/
  • If no spring.application.name has been defined: /spring/leader/default/

Hazelcast based Leader-Election

Add the following dependency to your project:

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-hazelcast</artifactId>
</dependency>

Provide a HazelcastInstance by defining it manually or let the HazelcastAutoConfiguration from the spring-boot-autoconfigure module define it for you.

Lock-Registry based Leader-Election

Provide a LockRegistry

Example based on a JdbcLockRegistry

@Configuration
class JdbcLockRegistryConfiguration {

	private final DataSource dataSource;

	JdbcLockRegistryConfiguration(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	@Bean
	public DefaultLockRepository lockRepository() {
		return new DefaultLockRepository(this.dataSource);
	}

	@Bean
	public LockRegistry lockRegistry() {
		return new JdbcLockRegistry(this.lockRepository());
	}

 }

Usage

Leader Aware Integration Endpoints

You can define that some of your integration endpoints become "leader-aware" by declaring their endpoint role in your spring configuration file. The configured endpoints are then not going to "autostart" on startup instead they wait until the application has become the leader (org.springframework.integration.leader.event.OnGrantedEvent) and if so the Endpoints are started. Visa-verca if the application's leadship has been revoked (org.springframework.integration.leader.event.OnRevokedEvent) the configured Endpoints are then gracefully stopped.

Example:

In your application.[yml|property] define the endpoint-bean-id in the key: spring-integration.leader-aware.endpoints

spring-integration:
  leader:
    roles: my-flow
@Configuration
public class FlowExampleConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(FlowExampleConfig.class);

    @Bean
    IntegrationFlow sampleIntegrationFlow() {
        return IntegrationFlows
                .from(() -> UUID.randomUUID().toString(), c -> c
                        .role("my-flow")
                        .poller(p -> p.fixedDelay(1000))
                        .autoStartup(false))
                .handle((GenericHandler<String>) (payload, headers) -> {
                    LOGGER.info("Handle Message: {}", payload);
                    return null;
                })
                .get();
    }

}

Hint: spring-integration-leader-start will automatically disable the endpoint's auto-start behaviour if that endpoint's role is configured as a leader-aware role

In the above example if we'd start multiple instances of the application only one - the leader - executes the sampleIntegrationFlow once it received the Leader OnGrantedEvent (see org.springframework.integration.support.SmartLifecycleRoleController)

Using LeaderAware annotation

Decorate an method with the LeaderAware annotation to make sure it is only called if the associated role is currently the leader.

Example:

spring-integration:
  leader:
    roles: my-role
@Configuration
public class SchedulerConfig {

    @Scheduled(....)
    @LeaderAware("my-role")
    void sampleScheduler() {
       // do something
    }

}

Programmatically check for Leadership

spring-integration-leader-starter creates a LeaderProvider for every defined spring-integration.leader.role. In order to check manually whether a certain role has the leader-ship inject the LeaderProvider in your bean by using a qualifier

Bean-Name: <role>_leaderProvider

Example:

spring-integration:
  leader:
    roles: my-role
@Configuration
public class SchedulerConfig {

    @Autowired
    @Qualifier("my-role_LeaderProvider")
    private LeaderProvider myRoleLeaderProvider;

    @Scheduled(....)
    void sampleScheduler() {
       if (!this.myRoleLeaderProvider.getContext().isLeader()){
           return;
       } 
       // do something
    }

}

Health Endpoint

A simple Health Indicator is created that show whether the current application instance is the leader. You can disable the health indicator like any actuator based health indicator in your application.[yml|property] using the key: management.health.leader.enabled

management:
  health:
    leader:
      enabled: false