快速实现leader选举之Spring Cloud Kubernetes Leader Election

Posted 水中加点糖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速实现leader选举之Spring Cloud Kubernetes Leader Election相关的知识,希望对你有一定的参考价值。

说起节点选举,一般最先想到的就是使用zookeeper或redis来进行实现。

但有时因为项目的原因,如果不方便引入其它的中间件,又正好使用了spring cloud kubernetes框架,那么就可以直接使用spring cloud kubernetes leader election来实现节点选举了。

The Spring Cloud Kubernetes leader election mechanism implements the leader election API of Spring Integration using a Kubernetes ConfigMap.

接入方法

接入方法可直接看官方描述,其地址为:https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/#leader-election

从官方说明中可以看出,实现节点选举功能只需要引入如下的依赖即可:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-kubernetes-fabric8-leader</artifactId>
</dependency>

但在引入时需要注意,目前只有是采用的fabric8作为服务发现的项目才可以引入它,也就是一般需要这样引入依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-fabric8</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-kubernetes-fabric8-leader</artifactId>
        </dependency>

对于服务注册和发现的依赖不能使用spring-cloud-starter-kubernetes-client,也就是不能使用这个依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-client</artifactId>
        </dependency>

如果引用错了,项目可能会报错启动失败。

另外从官方的说明文档中我们可以看出,节点选举是使用ConfigMap实现的。

The Spring Cloud Kubernetes leader election mechanism implements the leader election API of Spring Integration using a Kubernetes ConfigMap.

默认用的名为leader的configmap存储的主节点数据,如果想换个configmap的名字,修改如下配置即可:

spring.cloud.kubernetes.leader.config-map-name=leader

当前面依赖引入之后,接下来的就是实现监听的方法就可以了。

通过官方文档可以看出,当某个节点被成功选为主节点时,会收到一个OnGrantedEvent的事件

When granted leadership, a leader application receives an OnGrantedEvent application event with leadership Context

当主节点释放leader权限时,会手收到一个OnRevokedEvent事件

When leadership removal occurs, the previous leader receives OnRevokedEvent application event.

所以代码里添加添加对应事件的代码即可,如测试时这样添加一下监听类:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.integration.leader.event.OnGrantedEvent;
import org.springframework.integration.leader.event.OnRevokedEvent;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.UnknownHostException;

@Component
public class LeaderElectionEventListener implements ApplicationListener 
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) 
        String localIp = getLocalIp();
        if (applicationEvent instanceof OnGrantedEvent) 
            OnGrantedEvent onGrantedEvent = (OnGrantedEvent) applicationEvent;
            logger.error("[onApplicationEvent] onGrantedEvent: source: role: ip:", onGrantedEvent.toString(), onGrantedEvent.getSource(), onGrantedEvent.getRole(), localIp);
         else if (applicationEvent instanceof OnRevokedEvent) 
            OnRevokedEvent onRevokedEvent = (OnRevokedEvent) applicationEvent;
            logger.error("[onApplicationEvent] onRevokedEvent: source: role: ip:", onRevokedEvent.toString(), onRevokedEvent.getSource(), onRevokedEvent.getRole(), localIp);
         else 
            logger.error("[onApplicationEvent] otherEvent: source: timestamp: ip:", applicationEvent.toString(), applicationEvent.getSource(), applicationEvent.getTimestamp(), localIp);
        
    

    private String getLocalIp() 
        try 
            InetAddress addr = InetAddress.getLocalHost();
            return addr.getHostAddress();
         catch (UnknownHostException e) 
            e.printStackTrace();
            return "unkown ip";
        
    


示例效果

仅一个节点

只有一个活动节点时,会触发一个OnGrantedEvent事件。

再看下configmap中的内容为:

从上面我们可以看出,当k8s集群中leader选举成功后,会创建一个存储leader的configmap。默认用的是leader名称。

多个节点

之后再将节点数设为3个副本数,再观察configmap,

kubectl describe configmap/leader

Name:         leader
Namespace:    default
Labels:       kind=leaders
              provider=spring-cloud-kubernetes
Annotations:  <none>

Data
====
leader.id.leader:
----
svca-service-8fbb78cb7-gdx2j
Events:  <none>

从观察节点可以看出,只要原来的leader是存活的,即便启了新的节点,还会是以原来的节点为准。如上面的,leader仍然为:svca-service-8fbb78cb7-gdx2j

原leader故障转移

再测试一下将原来的leader进行手动删掉,观察下情况。

由前面的configmap数据可知,当前的leader节点为:svca-service-8fbb78cb7-gdx2j

所以直接删除主节点

kubectl delete pod/svca-service-8fbb78cb7-gdx2j

再查看configmap情况

kubectl describe configmap/leader

其值如下:

Name:         leader
Namespace:    default
Labels:       kind=leaders
              provider=spring-cloud-kubernetes
Annotations:  <none>

Data
====
leader.id.leader:
----
svca-service-8fbb78cb7-m9ncl
Events:  <none>

leader变成了节点:svca-service-8fbb78cb7-m9ncl

看下它的日志:

kubectl logs pod/svca-service-8fbb78cb7-m9ncl

其中有如下日志:

2021-10-23 17:20:21.631 ERROR 1 --- [//10.43.0.1/...] c.z.s.event.LeaderElectionEventListener  : [onApplicationEvent] onGrantedEvent:OnGrantedEvent [role=leader, context=org.springframework.cloud.kubernetes.commons.leader.LeaderContext@2734b465, source=org.springframework.cloud.kubernetes.fabric8.leader.Fabric8LeadershipController@663d90b7] source:org.springframework.cloud.kubernetes.fabric8.leader.Fabric8LeadershipController@663d90b7 role:leader ip:10.42.1.203
2021-10-23 17:20:21.636  INFO 1 --- [//10.43.0.1/...] o.s.integration.leader.DefaultCandidate  : DefaultCandidaterole=leader, id=svca-service-8fbb78cb7-m9ncl has been granted leadership; context: org.springframework.cloud.kubernetes.commons.leader.LeaderContext@2734b465

说明这个节点选举成功后,也触发了OnGrantedEvent事件

以上是关于快速实现leader选举之Spring Cloud Kubernetes Leader Election的主要内容,如果未能解决你的问题,请参考以下文章

快速实现leader选举之Spring Cloud Kubernetes Leader Election

快速实现leader选举之Spring Cloud Kubernetes Leader Election

Consul 核心原理解析之-Raft leader选举

spring cloud 01

Raft之Leader选举

Raft协议实战之Redis Sentinel的选举Leader源码解析