This starter provides leader election based on solace exclusive queues.
Consult the table below to determine which version you need to use:
Spring Cloud | solace-spring-integration-leader | Spring Boot | sol-jcsmp |
---|---|---|---|
2023.0.3 | 1.3.6 | 3.3.3 | 10.24.1 |
2023.0.3 | 1.3.5 | 3.3.3 | 10.24.1 |
2023.0.3 | 1.3.4 | 3.3.3 | 10.24.1 |
2023.0.2 | 1.3.3 | 3.3.0 | 10.24.0 |
2023.0.2 | 1.3.2 | 3.3.0 | 10.23.0 |
2023.0.1 | 1.3.1 | 3.2.5 | 10.23.0 |
In order to be able to use the leader election functionality, add the following section to your Maven pom:
<dependency>
<groupId>community.solace.spring.integration</groupId>
<artifactId>solace-spring-integration-leader</artifactId>
<version>1.3.6</version>
</dependency>
Starting with version 1.3.4 the JCSMP Session will be shared when provided by:
<dependency>
<groupId>ch.sbb</groupId>
<artifactId>spring-cloud-stream-binder-solace</artifactId>
<version>[5.0.0,)</version>
</dependency>
Only one member of your consumer group should run the scheduled task.
You have a service/component with the spring standard @Scheduled
annotation, as well as the @LeaderAware
annotation, that suppresses the method execution if you are not leader of the example group "demo".
@Service
public class MyScheduledService {
@Scheduled(fixedRateString = "PT3S", initialDelayString = "PT1S")
@LeaderAware("demo")
void scheduler() {
log.info("I am the leader and log it schedules all 3s");
}
}
The @LeaderAware
annotation can be used on any component method. Those methods will return null if the code was not executed.
You need to specify in your application.yaml to auto join the group whenever your application is ready.
spring:
leader:
join-groups:
- groupName: demo
joinType: ON_READINESS
if your not want to have the leader group name in your configuration. You could do:
@Service
public class MyScheduledService {
@Scheduled(fixedRateString = "PT3S", initialDelayString = "PT1S")
@LeaderAware(configValue = "application.name")
void scheduler() {
log.info("I am the leader and log it schedules all 3s");
}
}
application:
name: DemoApp
spring:
leader:
join-groups:
- groupName: DemoApp
joinType: ON_READINESS
When listening to leader based events, you will receive OnGrantedEvent
and OnRevokedEvent
events.
You can query the leader role via event.getRole()
or use the condition = "#leaderEvent.role == 'demo'"
option of the @EventListener
to receive events only for a single leader group.
This requires the join-groups
configuration set to "ON_READINESS".
@EventListener(classes = {AbstractLeaderEvent.class}, condition = "#leaderEvent.role == 'demo'")
void leaderEventListener(AbstractLeaderEvent leaderEvent) {
log.warn("LeaderController: " + leaderEvent);
}
You can test whether you are the leader in your business logic and decide what you want to do with this information.
@Autowired
private SolaceLeaderInitiator leaderInitiator;
private void yourLeaderMethod() {
boolean isLeader = leaderInitiator.getContext("theNameOfTheRoleC").isLeader();
if (isLeader) {
// ...
}
return;
}
In case you have a cluster of leader services you may want to hand over the leadership to a local process. The leadership can not be taken but must be handed over to a new process, which joined the cluster. The order of leadership is based on the time a process joined the group. When a process X currently owns the leadership and wants to yield it, the next leader process will be the process, which joined the group right after process X.
@GetMapping(value = "/leader/yield/{role}")
public String yieldLeaderShip(@PathVariable("role") final String role) {
Context context = leaderInitiator.getContext(role);
if (context.isLeader()) {
context.yield();
return context.isLeader() ? "I am leader AGAIN! Am I the only group member?" : "I am not longer the leader";
}
return "I was not the leader";
}
By default, the leadership will be yielded on shutdown. To improve the failover speed. Because on a grace full application shut down. Tear down broker connection, database connection can take up to 1sec.
But if there is a reason that stops you from using this feature you can disable this by:
spring:
leader:
join-groups:
- groupName: DemoApp
joinType: ON_READINESS
yieldOnShutdown: false
or if you join groups programmatically:
@Autowired
private SolaceLeaderInitiator leaderInitiator;
private void yourMethode() {
leaderInitiator.joinGroup("theNameOfTheRoleA", false);
}
Just add this to your pom.xml file:
<dependency>
<groupId>community.solace.spring.integration</groupId>
<artifactId>solace-spring-integration-leader</artifactId>
<version>1.2.0</version>
</dependency>
There are three different options to join a group:
- programmatically: explicitly invoke a method to join a group
- during the first request of the leader context
- as soon as your application is ready
Join methods are appended to the leader group name below the "join-groups" configuration key:
spring:
leader:
join-groups:
- groupName: theNameOfTheRoleA
joinType: PROGRAMMATIC
- groupName: theNameOfTheRoleB
joinType: PROGRAMMATIC
- groupName: theNameOfTheRoleC
joinType: FIRST_USE
- groupName: demo
joinType: ON_READINESS
@Autowired
private SolaceLeaderInitiator leaderInitiator;
private void yourMethode() {
leaderInitiator.joinGroup("theNameOfTheRoleA");
}
By default, also those groups has to be defined in your application configuration.
You can allow anonymous PROGRAMMATIC stated groups by setting:
spring:
leader:
permit-anonymous-groups: true
The leader group will be joined when you request the leader context for the first time.
@Autowired
private SolaceLeaderInitiator leaderInitiator;
@GetMapping(value = "/leader/status/{role}")
public String isLeader(@PathVariable("role") final String role) {
return leaderInitiator.getContext(role).isLeader() ? "leader" : "passive";
}
The leader group will be joined as soon as your application is ready.
This allows to run bootstrap code like cache loading via ApplicationRunner
This is useful in combination with the @LeaderAware
annotation.
A durable Solace queue is provisioned for each leader group. The queue name is "leader." + role. A queue is exclusively used for the flow activation trigger. No other messages are supposed to pass this queue. Any other message apart from the flow activation trigger will lead to an exception.
The fail over timeout is managed by the solace ClientProfile. (Broker version >= 9.7)
broker# show client smf1 detail
Client Keepalive:
Enabled: Yes
Effective Timeout (sec): 10
broker> enable
broker# configure
broker(configure)# client-profile default message-vpn default
broker(configure/client-profile)# service
broker(configure/client-profile/service)# min-keepalive-timeout 10
broker(configure/client-profile/service)# smf
broker(configure/client-profile/service/smf)# min-keepalive-enabled
broker> show client-profile default message-vpn default detail
Client Keepalive
Enabled for SMF clients : Yes
Minimum Timeout : 10 seconds
This starter provides some JMX operations to remote manage the application leadership.
For monitoring purpose we provide a micrometer metrics.
Here an example if your application uses the micrometer-prometheus-registry.
# HELP leader_status Indicates if this project is [-1=not joined, 0=joined but not leader, 1=is leader] for a group.
# TYPE leader_status gauge
leader_status{group="noJoined",} -1.0
leader_status{group="theOther",} 0.0
leader_status{group="demo",} 1.0
For more information try these resources:
- The Solace Developer Portal website at: https://solace.dev
- Ask the Solace Community
Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.
See the list of contributors who participated in this project.
See the LICENSE file for details.