文档章节

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

凯京技术团队
 凯京技术团队
发布于 07/23 17:21
字数 1656
阅读 1031
收藏 12

什么是Etcd?

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

etcd项目地址:https://github.com/etcd-io/etcd

etcd官网:https://etcd.io

jetcd地址:https://github.com/etcd-io/jetcd

主备服务场景描述

很多时候为了服务的高可用,除了有个在工作的主服务外,还需要多启用几个备用服务,这样,在主服务出现故障时,备用服务能够马上顶上。这个场景有个很明显的特征就是同一时间只能有一个主服务。常见的如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();
  1. 首先申请授予续约获取到leaseId,其中lockttl为1,单位秒,etcd的租约是秒级的。在这里ttl的设置是有讲究的,取决于当主服务故障时,你想多快让从服务感知并顶上。当然,受限于etcd本身租约秒级限制,最快也只能是1秒。
  2. 然后调用keepAlive方法,使授予到的leaseid保活,这样,只要应用还存活就会自动续约
  3. 接着调用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) {
                 t.printStackTrace();
             }

             @Override
             public void onCompleted() {
             }
         });
        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) {
                t.printStackTrace();
            }

            @Override
            public void onCompleted() {

            }
        });
        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) {
                t.printStackTrace();
            }

            @Override
            public void onCompleted() {

            }
        });
        lock.lock(lockKey, leaseId).get().getKey();

        scheduledThreadPool.submit(() -> {
            while (true) {
                System.err.println("我是备用服务,我开始工作了,估计主服务已经挂了");
                TimeUnit.SECONDS.sleep(1);
            }
        });
        TimeUnit.DAYS.sleep(1);
    }
}

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

作者简介:

陈凯玲,2016年5月加入凯京科技。现任凯京科技研发中心架构组经理,救火队队长。独立博客KL博客(http://www.kailing.pub)博主。

© 著作权归作者所有

凯京技术团队
粉丝 83
博文 42
码字总数 86909
作品 1
浦东
架构师
私信 提问
加载中

评论(2)

空白的纸飞机
空白的纸飞机
Hello~关注了您分享的技术文章,很棒!我是腾讯云云+社区的小编,我们诚挚邀请您加入云+社区,与我们众多开发者一起交流分享技术内容。这个是我们云+社区【腾讯云自媒体分享计划】入驻流程和权益介绍的地址:https://cloud.tencent.com/developer/support-plan。了解更多详情请联系我~微信:18374981632,我们对您的加入充满期待。
袁跃12
canal可替代
Mysql双主互备+keeplived高可用架构

一、Mysql双主互备+keeplived高可用架构介绍 Mysql主从复制架构可以在很大程度保证Mysql的高可用,在一主多从的架构中还可以利用读写分离将读操作分配到从库中,减轻主库压力。但是在这种架构...

cy_lyh
2016/09/01
0
0
UCloud高可用UDB 开启云端金融级数据库服务

数据库作为底层的数据存储和管理工具,是企业IT系统中不可或缺的一环。从Oracle、DB2到目前最流行的开源数据库MySQL,关系型数据库已经存在了近40年,在企业中所发挥的作用不可替代。 但与此...

UCloud
2016/03/07
145
0
云方案丨数据库高可用、高并发运维解决方案在医疗行业的运用

前言 “全面上云的拐点到了!”7月25日,阿里云智能总裁张建锋在2019阿里云上海峰会上做出了明确回答。张建锋表示,今年是云产业一个非常重要的拐点。云上服务与数据库服务成为各类云服务厂商...

袋鼠云
08/12
0
0
阿里云数据库 SQL Server 2016 双机高可用版给您拜年了

在2018年狗年春节来临之际,阿里云数据库推出 SQL Server 2016 双机高可用版 和 2012 双机高可用版,全面涵盖官方企业版和标准版,预祝大家新年事业旺旺旺。 新版本底层基础架构作了颠覆性的...

rds-pd
2018/02/11
0
0
世界领先!详解蚂蚁金服自研数据库OceanBase的高可用及容灾方案

小蚂蚁说: 关于蚂蚁金服自研的金融级分布式关系型数据库OceanBase的故事相信大家已经不再陌生了(新来的同学可以移步《厉害了,蚂蚁金服!创造了中国自己的数据库OceanBase》了解更多~)。其...

兔子酱
2018/07/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Hibernate 5 的模块/包(modules/artifacts)

Hibernate 的功能被拆分成一系列的模块/包(modules/artifacts),其目的是为了对依赖进行独立(模块化)。 模块名称 说明 hibernate-core 这个是 Hibernate 的主要(main (core))模块。定义...

honeymoose
53分钟前
4
0
CSS--属性

一、溢出 当内容多,元素区域小的时候,就会产生溢出效果,默认是纵向溢出 横向溢出:在内容和容器之间再套一层容器,并且内部容器要比外部容器宽 属性:overflow/overflow-x/overflow-y 取值...

wytao1995
今天
4
0
精华帖

第一章 jQuery简介 jQuery是一个JavaScript库 jQuery具备简洁的语法和跨平台的兼容性 简化了JavaScript的操作。 在页面中引入jQuery jQuery是一个JavaScript脚本库,不需要特别的安装,只需要...

流川偑
今天
7
0
语音对话英语翻译在线翻译成中文哪个方法好用

想要进行将中文翻译成英文,或者将英文翻译成中文的操作,其实有一个非常简单的工具就能够帮助完成将语音进行翻译转换的软件。 在应用市场或者百度手机助手等各大应用渠道里面就能够找到一款...

401恶户
今天
3
0
jenkins 插件下载加速最终方案

推荐做法 1、告诉jenkins 我哪些插件需要更新 jenkins插件清华大学镜像地址 https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json 1.进入jenkins系统管理 2.进入插件管...

vasks
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部