高可用架构etcd选主故障主备秒级切换实现

目录
  • 什么是Etcd?
  • 主备服务场景描述
  • jetcd具体实现
    • 首先引入jetcd依赖
    • 初始化客户端
    • 关键api介绍
  • 完整的测试用例

什么是Etcd?

etcd是一个强大的一致性的分布式键值存储,它提供了一种可靠的方式来存储需要由分布式系统或机器群访问的数据。它优雅地处理网络分区期间的领导者选举,并且可以容忍机器故障,即使在领导者节点中也是如此。从简单的Web应用程序到Kubernetes,任何复杂的应用程序都可以读取数据并将数据写入etcd。这是官方对Etcd的描述,基于这些特性,Etcd常用于分布式配置、分布式锁、分布式服务协调者以及分布式注册。从功能上来说和zookeeper是一类项目,但是相比而言etcd更现代,etcd使用go语言开发,编译后生成了系统可执行的二进制产物,跨平台性更好,更易维护。etcd直接提供http的接口,非常方便各大语言封装自己的client sdk,在易用性方面也更好一点。下面也主要使用java的客户端jetcd,解决主备服务的协调问题。

etcd官网:https://etcd.io

主备服务场景描述

很多时候为了服务的高可用,除了有个在工作的主服务外,还需要多启用几个备用服务,这样,在主服务出现故障时,备用服务能够马上顶上。这个场景有个很明显的特征就是同一时间只能有一个主服务。常见的如mysql主从切换等,同一时间只能有一个msyql负责写数据。在我们这边的场景是,有一个binlog解析服务,实时解析mysql 的binlog,将解析到的数据传递到kafka中,kafka消费端有一个Flink job去消费解析的数据。最终这些数据会下层到数据中台中,提供给中台系统做基础的业务数据。很多在线的服务查询的数据就是来源binlog解析的数据,所以binlog解析的服务不能存在单点故障,在架构上只能是一主多备的模式,主服务故障时,备用服务实时顶上。同时binlog服务也不能同时多个解析。所以,这个场景使用etcd来做主备架构再好不过了。

jetcd具体实现

首先引入jetcd依赖

<dependency>
            <groupId>io.etcd</groupId>
            <artifactId>jetcd-core</artifactId>
            <version>0.3.0</version>
</dependency>

初始化客户端

Client client = Client.builder().endpoints(
                "http://127.0.0.1:2379",
                "http://127.0.0.1:3379",
                "http://127.0.0.1:4379"
        ).build();

关键api介绍

Lock lock = client.getLockClient();
        Lease lease = client.getLeaseClient();
  • Lease提供授予,撤销和保持租约的方法,其中有两个关键方法grant(long ttl)和keepAlive()。grant用于授予租约,入参为租约的时间,即如果创建带租约的key值,ttl秒后即自动删除,返回租约的id。keepAlive()方法用于保持租约有效,即如果租约即将到期时,keepAlive能够自动续租ttl时间。
  • Lock有两个方法,lock(ByteSequence name, long leaseId)和unlock(ByteSequence lockKey)。来实现分布式锁的功能,其中加锁时,入参leaseid为续约对象的id,即定义了持有锁的时间

通过这Lease和Lock的功能,很容易实现主备服务的切换。关键代码如下:

ByteSequence lockKey = ByteSequence.from("/root/lock", StandardCharsets.UTF_8);
        Lock lock = client.getLockClient();
        Lease lease = client.getLeaseClient();
        long leaseId = lease.grant(lockTTl).get().getID();
        lease.keepAlive(leaseId, new StreamObserver<LeaseKeepAliveResponse>() {
            @Override
            public void onNext(LeaseKeepAliveResponse value) {
                System.err.println("LeaseKeepAliveResponse value:" + value.getTTL());
            }
            @Override
            public void onError(Throwable t) { t.printStackTrace(); }
            @Override
            public void onCompleted() { }
        });
        lock.lock(lockKey, leaseId).get().getKey();
  • 首先申请授予续约获取到leaseId,其中lockttl为1,单位秒,etcd的租约是秒级的。在这里ttl的设置是有讲究的,取决于当主服务故障时,你想多快让从服务感知并顶上。当然,受限于etcd本身租约秒级限制,最快也只能是1秒。
  • 然后调用keepAlive方法,使授予到的leaseid保活,这样,只要应用还存活就会自动续约
  • 接着调用lock方法,传入leaseid。只有首次启动的服务会获取到锁,而且在运行期间,会不断的续约。当从服务运行到此处时,会阻塞住。这样就能保证多个服务同时运行,只有一个服务真正工作的目的。当获取到锁的主服务出现问题时,原先的只有锁的续约在1秒内就会到期,从服务会马上获取到锁执行工作代码

完整的测试用例

/**
 * @author: kl @kailing.pub
 * @date: 2019/7/22
 */
public class JEtcdTest {
    private Client client;
    private Lock lock;
    private Lease lease;
    //单位:秒
    private long lockTTl = 1;
    private ByteSequence lockKey = ByteSequence.from("/root/lock", StandardCharsets.UTF_8);
    private ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
    @Before
    public void setUp() {
         client = Client.builder().endpoints(
                "http://127.0.0.1:2379",
                "http://127.0.0.1:3379",
                "http://127.0.0.1:4379"
        ).build();
         lock = client.getLockClient();
         lease = client.getLeaseClient();
    }
    @Test
    public void lockTest1toMaster() throws InterruptedException, ExecutionException {
        long leaseId = lease.grant(lockTTl).get().getID();
         lease.keepAlive(leaseId, new StreamObserver<LeaseKeepAliveResponse>() {
             @Override
             public void onNext(LeaseKeepAliveResponse value) {
                 System.err.println("LeaseKeepAliveResponse value:"+ value.getTTL());
             }
             @Override
             public void onError(Throwable t) {
                 scheduledThreadPool.shutdownNow();
                 t.printStackTrace();
             }
             @Override
             public void onCompleted() {
                 scheduledThreadPool.shutdownNow();
             }
         });
        lock.lock(lockKey, leaseId).get().getKey();
        scheduledThreadPool.submit(() -> {
            while (true) {
                System.err.println("我是主服务开始工作了");
                TimeUnit.SECONDS.sleep(1);
            }
        });
        TimeUnit.DAYS.sleep(1);
    }
    @Test
    public void lockTest2toStandby() throws InterruptedException, ExecutionException {
        long leaseId = lease.grant(lockTTl).get().getID();
        lease.keepAlive(leaseId, new StreamObserver<LeaseKeepAliveResponse>() {
            @Override
            public void onNext(LeaseKeepAliveResponse value) {
                System.err.println("LeaseKeepAliveResponse value:"+ value.getTTL());
            }
            @Override
            public void onError(Throwable t) {
                scheduledThreadPool.shutdownNow();
                t.printStackTrace();
            }
            @Override
            public void onCompleted() {
                 scheduledThreadPool.shutdownNow();
            }
        });
        lock.lock(lockKey, leaseId).get().getKey();
        scheduledThreadPool.submit(() -> {
            while (true) {
                System.err.println("我是备用服务,我开始工作了,估计主服务已经挂了");
                TimeUnit.SECONDS.sleep(1);
            }
        });
        TimeUnit.DAYS.sleep(1);
    }
    @Test
    public void lockTest3toStandby() throws InterruptedException, ExecutionException {
        long leaseId = lease.grant(lockTTl).get().getID();
        lease.keepAlive(leaseId, new StreamObserver<LeaseKeepAliveResponse>() {
            @Override
            public void onNext(LeaseKeepAliveResponse value) {
                System.err.println("LeaseKeepAliveResponse value:"+ value.getTTL());
            }
            @Override
            public void onError(Throwable t) {
                scheduledThreadPool.shutdownNow();
                t.printStackTrace();
            }
            @Override
            public void onCompleted() {
                scheduledThreadPool.shutdownNow();
            }
        });
        lock.lock(lockKey, leaseId).get().getKey();
        scheduledThreadPool.submit(() -> {
            while (true) {
                System.err.println("我是备用服务,我开始工作了,估计主服务已经挂了");
                TimeUnit.SECONDS.sleep(1);
            }
        });
        TimeUnit.DAYS.sleep(1);
    }
}

上面测试用例模拟了一主两备的高可用架构。分别执行lockTest1toMaster()、lockTest2toStandby()、lockTest3toStandby()服务,会发现只有一个服务会打印。然后手动关闭这个服务,从服务马上会接着打印。在关闭这个从服务,另外一个从服务就会接着打印。很好的模拟了主备故障切换的效果。

以上就是高可用架构etcd选主故障主备秒级切换实现的详细内容,更多关于etcd主备故障秒级切换的资料请关注我们其它相关文章!

(0)

相关推荐

  • 基于Docker的Etcd分布式部署的方法步骤

    一 环境准备 1.1 基础环境 ntp配置:略 #建议配置ntp服务,保证时间一致性 etcd版本:v3.3.9 防火墙及SELinux:关闭防火墙和SELinux 名称 地址 主机名 备注 etcd1 172.24.8.71 etcd1.example.com 用于保存相关IP信息 docker01 172.24.8.72 docker01.example.com   docker02 172.24.8.73 docker02.example.com   # hostnamectl set-h

  • Docker微服务的ETCD集群搭建教程详解

    目录 etcd的特性 Etcd构建自身高可用集群主要有三种形式 本次搭建的基础环境 1.将服务器挨个添加进集群 2.将服务器统一添加进集群 etcd api接口 服务注册与发现 etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现.etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性.Raft是一个来自Stanford的新的一致性算法,适用于分布式系统的日志复制,Raft通过选举的

  • docker-compose部署etcd集群的实现步骤

    目录 编写docker-compose.yml 运行docker-compose 检查搭建状态 测试节点 Golang 与 etcd 简单交互 编写docker-compose.yml version: "3.0" networks: etcd-net: # 网络 driver: bridge # 桥接模式 volumes: etcd1_data: # 挂载到本地的数据卷名 driver: local etcd2_data: driver: local etcd3_data: driv

  • 利用二进制文件安装etcd的教程详解

    etcd组件作为一个高可用强一致性的服务发现存储仓库. etcd作为一个受到ZooKeeper与doozer启发而催生的项目,除了拥有与之类似的功能外,更专注于以下四点. 简单:基于HTTP+JSON的API让你用curl就可以轻松使用. 安全:可选SSL客户认证机制. 快速:每个实例每秒支持一千次写操作. 可信:使用Raft算法充分实现了分布式. 场景一:服务发现(Service Discovery)一个强一致性.高可用的服务存储目录.基于Raft算法的etcd天生就是这样一个强一致性高可用的

  • Go操作etcd的实现示例

    目录 etcdetcd介绍 etcd应用场景 服务发现 配置中心 分布式锁 为什么用 etcd 而不用ZooKeeper? 为什么不选择ZooKeeper? 为什么选择etcd? etcd集群 搭建一个3节点集群示例: Go语言操作etcd 安装 put和get操作 watch操作 基于etcd实现分布式锁 参考链接: etcd是近几年比较火热的一个开源的.分布式的键值对数据存储系统,提供共享配置.服务的注册和发现,本文主要介绍etcd的安装和使用. etcdetcd介绍 etcd是使用Go语言

  • 高可用架构etcd选主故障主备秒级切换实现

    目录 什么是Etcd? 主备服务场景描述 jetcd具体实现 首先引入jetcd依赖 初始化客户端 关键api介绍 完整的测试用例 什么是Etcd? etcd是一个强大的一致性的分布式键值存储,它提供了一种可靠的方式来存储需要由分布式系统或机器群访问的数据.它优雅地处理网络分区期间的领导者选举,并且可以容忍机器故障,即使在领导者节点中也是如此.从简单的Web应用程序到Kubernetes,任何复杂的应用程序都可以读取数据并将数据写入etcd.这是官方对Etcd的描述,基于这些特性,Etcd常用于

  • MySQL高可用架构之MHA架构全解

    目录 一.介绍 二.组成 三.工作过程 四.架构 五.实例展示 MHA(Master HA)是一款开源的 MySQL 的高可用程序,它为 MySQL 主从复制架构提供了 automating master failover 功能.MHA 在监控到 master 节点故障时,会提升其中拥有最新数据的 slave 节点成为新的master 节点,在此期间,MHA 会通过于其它从节点获取额外信息来避免一致性方面的问题.MHA 还提供了 master 节点的在线切换功能,即按需切换 master/sla

  • MySQL数据库实现高可用架构之MHA的实战

    目录 一.MySQLMHA介绍 1.1什么是MHA? 1.2MHA的组成 1.3MHA的特点 二.MySQLMHA搭建 1.MHA架构部分 2.故障模拟部分 3.实验环境 三.实验步骤 1.关闭防火墙和SElinux 2.Master.Slave1.Slave2节点上安装mysql5.7 3.修改Master.Slave1.Slave2节点的主机名 4.修改Master.Slave1.Slave2节点的Mysql主配置文件/etc/my.cnf 5.在Master.Slave1.Slave2节点

  • MySQL之高可用架构详解

    目录 引言 MySQL高可用 一主一备: MySQL主从同步的几种模式: 总结 引言 "高可用"是互联网一个永恒的话题,先避开MySQL不谈,为了保证各种服务的高可用有几种常用的解决方案. 服务冗余:把服务部署多份,当某个节点不可用时,切换到其他节点.服务冗余对于无状态的服务是相对容易的. 服务备份:有些服务是无法同时存在多个运行时的,比如说:Nginx的反向代理,一些集群的leader节点.这时可以存在一个备份服务,处于随时待命状态. 自动切换:服务冗余之后,当某个节点不可用时,要做

  • keeplive+mysql+drbd高可用架构安装步骤

    DRBD(DistributedReplicatedBlockDevice)是一个基于块设备级别在远程服务器直接同步和镜像数据的开源软件,类似于RAID1数据镜像,通常配合keepalived.heartbeat等HA软件来实现高可用性. DRBD是一种块设备,可以被用于高可用(HA)之中.它类似于一个网络RAID-1功能,当你将数据写入本地文件系统时,数据还将会被发送到网络中另一台主机上.以相同的形式记录在一个文件系统中. 本地(master)与远程主机(backup)的保证实时同步,如果本地

  • MySQL高可用解决方案MMM(mysql多主复制管理器)

    一.MMM简介: MMM即Multi-Master Replication Manager for MySQL:mysql多主复制管理器,基于perl实现,关于mysql主主复制配置的监控.故障转移和管理的一套可伸缩的脚本套件(在任何时候只有一个节点可以被写入),MMM也能对从服务器进行读负载均衡,所以可以用它来在一组用于复制的服务器启动虚拟ip,除此之外,它还有实现数据备份.节点之间重新同步功能的脚本.MySQL本身没有提供replication failover的解决方案,通过MMM方案能实

  • MySQL数据库实现MMM高可用群集架构

    概念 MMM(Master-Master replication managerfor Mysql,Mysql主主复制管理器)是一套灵活的脚本程序,基于perl实现,用来对mysql replication进行监控和故障迁移,并能管理mysql Master-Master复制的配置(同一时间只有一个节点是可写的). MMM高可用架构说明 mmm_mond:监控进程,负责所有的监控工作,决定和处理所有节点角色活动.此脚本需要在监管机上运行. mmm_agentd:运行在每个mysql服务器上的代理

  • MySQL数据库的高可用方案总结

    高可用架构对于互联网服务基本是标配,无论是应用服务还是数据库服务都需要做到高可用.虽然互联网服务号称7*24小时不间断服务,但多多少少有一些时候服务不可用,比如某些时候网页打不开,百度不能搜索或者无法发微博,发微信等.一般而言,衡量高可用做到什么程度可以通过一年内服务不可用时间作为参考,要做到3个9的可用性,一年内只能累计有8个小时不可服务,而如果要做到5个9的可用性,则一年内只能累计5分钟服务中断.所以虽说每个公司都说自己的服务是7*24不间断的,但实际上能做到5个9的屈指可数,甚至根本做不到

  • 基于 ZooKeeper 搭建 Hadoop 高可用集群 的教程图解

    一.高可用简介 Hadoop 高可用 (High Availability) 分为 HDFS 高可用和 YARN 高可用,两者的实现基本类似,但 HDFS NameNode 对数据存储及其一致性的要求比 YARN ResourceManger 高得多,所以它的实现也更加复杂,故下面先进行讲解: 1.1 高可用整体架构 HDFS 高可用架构如下: 图片引用自: https://www.edureka.co/blog/how-to-set-up-hadoop-cluster-with-hdfs-hi

  • MySQL 8.0.23中复制架构从节点自动故障转移的问题

    接触MGR有一段时间了,MySQL 8.0.23的到来,基于MySQL Group Replicaion(MGR)的高可用架构又提供了新的架构思路. 灾备机房的slave,如何更好的支持主机房的MGR? MGR 到底可以坏几个节点? 这次我就以上2个问题,和大家简单聊下MGR的一些思想和功能. 一.MySQL Group Relication 成员数量的容错能力 上面的表格相信大家不会陌生了,我经常在面试里会问:"4个节点的MGR,最多坏几个呢?" ,多数人回答:"最多坏1个

随机推荐