RPC 框架使用 Etcd 作为注册中心

本文将介绍的是我们自研的RPC框架Dapeng-soa(https://github.com/dapeng-soa/dapeng-soa),使用 etcd 作为新的注册中心的一种方案

Dapeng注册中心节点

基本根节点

/soa/runtime/services
/soa/config/services
/soa/config/routes

节点目录图


├── soa
│   ├── runtime
│   │   │   ├── services
│   │   │   │   └── com.today.service.goods.GoodsService 
│   │   │   │                                       └── 192.168.10.121:9071
                                                   └── 192.168.10.121:9072
                                                   └── 192.168.10.121:9073
                                                   └── 192.168.10.121:9074
│   ├── config
│   │   │   ├── services
│   │   │   │   └── com.today.service.goods.GoodsService 
│   │   │   │                                       └──

│   │   │   ├── routes
│   │   │   │   └── com.today.service.goods.GoodsService 
  • 1.除了带有IP,端口信息的节点为临时节点(相对于zk),其他均为永久性节点
  • 2.zk类似与文件系统,父节点,子节点的模式。192.168.10.121:9071在zk中为一个临时子节点
  • 3.etcd是以key-value形式进行存储的,/soa/runtime/services//soa/runtime/services/com.UserService对应etcd中的两个key,他们之间没有什么联系,只有在查询key时,指定--prefix时,会根据前缀进行搜寻key

etcd租约节点 实现类似zookeeper的临时节点方案

  • 1.etcd使用租约的方式,对创建的key设置超时时间,当超时时,该节点就会被删除。
  • 2.我们可以为此节点续租,当节点即将超时时,就进行续租。这样就可以达到类似于zookeeper临时节点的作用。
  • 3.当客户端由于什么原因挂掉以后,etcd上的节点由于没有被继续续租,很快就会到期被删除。

Java客户端实现租约与续租的代码逻辑

public Main(String registryAddress, String host) {
        Client client = Client.builder().endpoints(registryAddress).build();
        this.lease = client.getLeaseClient();
        this.kv = client.getKVClient();
        try {
            this.leaseId = lease.grant(5).get().getID();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }

        keepAlive();

        try {
            int port = 1000;
            register("com.DemoService", host, port + 50);
            logger.info("provider-agent provider register to etcd at port {}", port + 50);
        } catch (Exception e) {
           logger.error(e.getMessage,e);
        }
    }

    /**
     * 向 ETCD 中注册服务
     */
    public void register(String serviceName, String host, int port) throws Exception {
        String strKey = MessageFormat.format("/{0}/{1}/{2}:{3}", rootPath, serviceName, host, String.valueOf(port));
        ByteSequence key = ByteSequence.fromString(strKey);
        String weight = "50";
        ByteSequence val = ByteSequence.fromString(weight);


        kv.put(key, val, PutOption.newBuilder().withLeaseId(leaseId).withPrevKV().build()).get();
        kv.txn();
        logger.info("Register a new service at:{},weight:{}", strKey, weight);
    }

    /**
     * 发送心跳到ETCD,表明该host是活着的
     */
    public void keepAlive() {
        Executors.newSingleThreadExecutor().submit(
                () -> {
                    try {
                        Lease.KeepAliveListener listener = lease.keepAlive(leaseId);
                        listener.listen();
                        logger.info("KeepAlive lease:" + leaseId + "; Hex format:" + Long.toHexString(leaseId));
                    } catch (Exception e) {
                        logger.error(e.getMessage(), e);
                    }
                }
        );
    }
  • 1.通过this.leaseId = lease.grant(5).get().getID();注册租约,然后再注册服务到etcd上时,使用该租约.
  • 2.keepAlive()方法保持客户端与etcd的联系,并对租约进行续租,一旦将要超时时,马上就行续租。
  • 3.当客户端掉线或者关闭后,keepAlive将不会继续续租,5s后,租约到期,节点就会被删除.

etcd有序节点与zookeepr有序节点

  • 1.zookeeper临时节点会在节点最后生成一个序列串,在相同父节点下每次创建子节点时,节点最后的序列串会有序递增
  • 2.etcd创建的每一个节点都会带有如下几个信息:
message KeyValue {
  bytes key = 1;
  int64 create_revision = 2;
  int64 mod_revision = 3;
  int64 version = 4;
  bytes value = 5;
  int64 lease = 6;
}
  • 1.etcd服务段有一个机制,他会对每一次请求创建的任何key提供create_revision和mod_revision的递增,递增时全局性的,任何key的操作都会在全局的举出上面进行递增。

  • 2.那么在服务节点下,我们每一次创建一个key时,Create_Revision都会在之前最后创建的节点的基础上增加1。

  • 3.每一次修改一个key的内容时,mod_Revision就会在全局计数器上增加一次。

结论,etcd的key支持天然的有序性。可以根据这两个属性来判断key创建的先后顺序。

etcd watch机制

  • 1.zookeeperwatch机制时一次性的,当watch的节点发生变更后,会通知客户端,同时watch失效
  • 2.etcdwatch机制时一直生效的,watch一次,一直可以得到watch的节点的变更信息。但是,etcd的watch时阻塞模式的,watch某个节点后,就会阻塞等待回应。
public static void etcdWatch(Watch watch, String key, Boolean usePrefix, WatchCallback callback) {
        executorService.execute(() -> {
            try {
                Watch.Watcher watcher;
                if (usePrefix) {
                    watcher = watch.watch(ByteSequence.fromString(key), WatchOption.newBuilder().withPrefix(ByteSequence.fromString(key)).build());
                } else {
                    watcher = watch.watch(ByteSequence.fromString(key));
                }
                List<WatchEvent> events = watcher.listen().getEvents();
                callback.callback(events);
            } catch (InterruptedException e) {
                logger.error(e.getMessage(), e);
            }
        });
}
  • 1.使用异步线程对key进行watch,注意,我们如果要watch,一个服务节点下的多个实例的变更,需要在watch时指定前缀,即watch当前key为一个前缀,这样后面的所有实例key的变化都能够watch到。
  • 2.当watch事件触发后,回调watchCallBack方法进行相应的处理,这个内容需要我们自己实现。

etcd 获取节点和节点内容

  • 1.etcd为key-value形式存储数据,所以获取key的数据非常简单
private String getEtcdValue(String path, Boolean usePrefix) {
    try {
        KV kv = client.getKVClient();
        ByteSequence seqKey = ByteSequence.fromString(path);
        GetResponse response = kv.get(seqKey).get();
        String key = response.getKvs().get(0).getKey().toStringUtf8();
        String value = response.getKvs().get(0).getValue().toStringUtf8();
        logger.info("Get data from etcdServer, key:{}, value:{}", key, value);
        return value;
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    return null;
}

Etcd Key-Value Api

键值API操作存储在etcd中的键值对

etcd来说,键值对(key-value pair)是最小可操作单元,每个键值对都有许多字段,以protobuf格式定义。

message KeyValue {
  bytes key = 1;
  int64 create_revision = 2;
  int64 mod_revision = 3;
  int64 version = 4;
  bytes value = 5;
  int64 lease = 6;
}
  • key: 以字节为单位的键。不允许使用空密钥。
  • value: 以字节为单位的值。
  • version: 版本是密钥的版本。删除将版本重置为零,任何对密钥的修改都会增加版本号
  • Create_Revision: 修改键上的最后一个创建.
  • Mod_Revision: 修改最后修改的密钥.
  • Lease(租约): 附在钥匙上的租约的ID。如果租约是0,则没有租约附在钥匙上。

除了键和值之外,etcd还将附加的修订元数据作为键消息的一部分。该修订信息按创建和修改的时间对键进行排序,这对于管理分布式同步的并发性非常有用。etcd客户端的分布式共享锁使用创建修改来等待锁定所有权。类似地,修改修订用于检测软件事务内存读集冲突并等待领导人选举更新。


Etcd vs Zookeeper

相较之下,Zookeeper有如下缺点。

  • 1.复杂。Zookeeper的部署维护复杂,管理员需要掌握一系列的知识和技能;而Paxos强一致性算法也是素来以复杂难懂而闻名于世;另外,Zookeeper的使用也比较复杂,需要安装客户端,官方只提供了java和C两种语言的接口。
  • 2.Java编写。这里不是对Java有偏见,而是Java本身就偏向于重型应用,它会引入大量的依赖。而运维人员则普遍希望机器集群尽可能简单,维护起来也不易出错。
  • 3.发展缓慢。Apache基金会项目特有的“Apache Way”在开源界饱受争议,其中一大原因就是由于基金会庞大的结构以及松散的管理导致项目发展缓慢。

而etcd作为一个后起之秀,其优点也很明显。

  • 1.简单。使用Go语言编写部署简单;使用HTTP作为接口使用简单;使用Raft算法保证强一致性让用户易于理解。
  • 2.数据持久化。etcd默认数据一更新就进行持久化。
  • 3.安全。etcd支持SSL客户端安全认证。
    最后,etcd作为一个年轻的项目,正在高速迭代和开发中,这既是一个优点,也是一个缺点。优点在于它的未来具有无限的可能性,缺点是版本的迭代导致其使用的可靠性无法保证,无法得到大项目长时间使用的检验。然而,目前CoreOS、Kubernetes和Cloudfoundry等知名项目均在生产环境中使用了etcd,所以总的来说,etcd值得你去尝试。

etcd 基本操作

docker-compose形式启动etcd单节点服务

etcd:
    container_name: etcd
    # image: quay.io/coreos/etcd:v3.1
    image: registry.cn-hangzhou.aliyuncs.com/coreos_etcd/etcd:v3
    ports:
      - "2379:2379"
      - "4001:4001"
      - "2380:2380"
    environment:
      - ETCDCTL_API=3
      - TZ=CST-8
      - LANG=zh_CN.UTF-8
    command: 
      /usr/local/bin/etcd
      -name node1 
      -data-dir /etcd-data
      -advertise-client-urls http://${host_ip}:2379,http://${host_ip}:4001
      -listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001
      -initial-advertise-peer-urls http://${host_ip}:2380
      -listen-peer-urls http://0.0.0.0:2380
      -initial-cluster-token docker-etcd 
      -initial-cluster node1=http://${host_ip}:2380
      -initial-cluster-state new 
    volumes:
      - "/data/config/etcd/ca-certificates/:/etc/ssl/certs"
      - "/data/conf/etcd/data:/etcd-data"

etcd v3 基本api

  • 1.进入容器
docker exec -it etcd sh
  • 2.设置键、修改键
etcdctl put /maple value
  • 3.删除键
etcdctl del /maple
  • 4.删除所有/test前缀的节点
etcdctl del  /test --prefix
  • 5.查询Key
etcdctl get /test/ok
  • 6.前缀查询
etcdctl get /test/ok --prefix
  • 7.watch key
etcdctl watch /maple/services
  • 8.watch子节点
etcdctl watch /maple/services --prefix
  • 9.申请租约、授权租约

申请租约

etcdctl lease grant 40
    result: lease 4e5e5b853f528859 granted with TTL(40s)

授权租约

etcdctl put --lease=4e5e5b853f528859 /maple/s 123

撤销

etcdctl lease revoke 4e5e5b853f5286cc

租约续约

etcdctl lease keep-alive 4e5e5b853f52892b
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容

  • 因为工作需求,公司需要使用ETCD来做gRPC服务的负载均衡,以及集群管理,所以对etcd做了一些研究,希望能给大...
    Jay_Guo阅读 46,555评论 8 47
  • from http://www.infoq.com/cn/articles/etcd-interpretation...
    小树苗苗阅读 13,935评论 3 38
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,555评论 18 139
  • 小时候历史就是一个故事。 孩子在父母讲述的历史故事中听见英雄的事迹,正义的化身,让孩子觉得历史很有趣。 长大了历史...
    小泥孩黑山站阅读 182评论 0 0
  • 帝都的雨季开始了,一大早出门在小区里七拐八拐还是鞋湿透了。据说很多地方都淹了,不禁感恩自己的生存环境还是很棒的。 ...
    小王子的前世今生阅读 186评论 0 0