golang入门笔记—etcd

etcd介绍:

etcd是使用Go语言开发的一个开源的、高可用的分布式key—value存储系统,可以用于配置共享和服务的注册和发现,基于raft算法实现具有强一致性

etcd中涉及了数据一致性、多版本并发控制、watch监控、磁盘IO读写等知识点

etcd的特性:

1.简单:etcd的安装简单,为用户提供了HTTP的接口,使用也很简单
2.存储:etcd的基本功能,基本信息存储在文件中
3.Watch机制:watch指定的键的更改会通知
4.安全通信:SSL证书验证
5.高性能:2K/s读操作,提供了基准测试的工具
6.一致可靠:raft一致性算法

类似的项目有zookeeper和consul。
etcd具有以下的特点:

1.完全复制:集群中的每个节点都可以使用完整的存档
2.高可用性:Etcd可用于避免硬件的单点故障或网络问题
3.一致性:每次读取都会返回跨多主机的最新写入
4.简单:包括一个定义良好、面向用户的API(gRPC)
5.安全:实现了带有可选的客户端证书身份验证的自动化TLS
6.快速:每秒10000次写入的基准速度
7.可靠:使用Raft算法实现了强一致、高可用的服务存储目录

etcd的常见分布式场景:

1.键值对存储
2.服务注册与发现
3.消息订阅与发布
4.分布式锁

CAP原理:

Consistency(一致性):该项强调数据的正确性。具体而言,每次读操作,要么读到最新,要么读失败。这要求整个
分布式系统像是一个不可拆分的整体,写操作作用于集群像作用于单机一样,具有广义的“原子性”。
Availability(可用性):该项站在使用者的角度,强调使用服务的体验。客户端的请求能够得到响应,不发生错误,
也不能出现过长的等待时间.
Partition tolerance(区间容错性):分区容错性强调的是,在网络环境不可靠的背景下,整个系统仍然是正常运作
的,不至于出现系统崩溃或者秩序混乱的局面

CP:强调系统数据的正确性,但由于保证不同节点间严格一致性的机制,可能会牺牲系统的可用性
AP:强调系统的可用性,那就必须在数据一致性上做出妥协退让

分布式系统中,C 和 A 之间的矛盾正是在于网络环境的不稳定

etcd的核心架构:

在这里插入图片描述
在这里插入图片描述

etcd Server:用于对外接收和处理客户端的请求
gRPC Server:etcd与其他etcd节点之间的通信和信息同步
MVCC:多版本控制,etcd的存储模块。键值对的每一次操作行为都会被记录存储。这些数据底层存储在BoltDB数据库中。
在这里插入图片描述
Client层:Client层包括client v2和v3两大版本API客户端库,提供了简洁易用的API,同时支持负载均衡、节点间故障自动转移,可极大降低业务使用etcd复杂度,提升开发效率、服务可用性

API网络层:API网络层主要包括client访问server和server节点之间的通信协议。一方面,client访问etcd server的API分为v2和v3两个大版本。v2 API使用HTTP/1.x协议,v3 API使用gRPC协议。同时v3通过etcd grpc-gateway组件也支持HTTP/1.x协议,便于各种语言的服务调用。另一方面,server之间通信协议,是指节点间通过Raft算法实际数据复制和Leader选举等功能时使用的HTTP协议。

Raft算法层:Raft算法层实现了Leader选举、日志复制、ReadIndex等核心算法特性,用于保障多个etcd多个节点间的数据一致性、提升服务可用性等,是etcd的基石和亮点。

KVServer:鉴权和限流

Quota:写入时进行空间验证

WAL:存放预写式日志、最大的作用是记录整个数据变化的全部历程、在etcd中,所有数据的修改在提交前都要先写入到WAL中

SNAP:存放快照数据,存储etcd的数据状态。etcd防止WAL文件过多而设置的快照。

功能逻辑层:etcd核心特性实现层,如典型的KVServer模块、MVCC模块、Auth鉴权模块、Lease租约模块、Compactor压缩模块等,其中MVCC模块主要由treeIndex模块和boltdb模块组成。

存储层:存储层包含预写日志(WAL)模块、快照(Snapshot)模块、boltdb模块。其中WAL可保障etcd crash后数据不丢失,boltdb则保存了集群元数据和用户写入的数据。

服务注册发现:
在这里插入图片描述

应用在启动的时候主动从etcd获取一次配置信息,同时,在etcd节点上注册一个Watcher并等待,以后每次配置有更新的时候,etcd都会实时通知订阅者,以此达到获取最新配置信息的目的。

分布式锁:
因为etcd使用Raft算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。锁服务有两种使用方式,一是保持独占,二是控制时序。
1.保持独占即所有获取锁的用户最终只有一个可以得到。etcd为此提供了一套实现分布式锁原子操作CAS的API。通过设置prevExist值,可以保证在多个节点同时去创建某个目录时,只有一个是成功。而创建成功的用户就可以认为是获得了锁。
2.控制时序,即所有想要得到锁的用户会被安排执行,但是获得锁的顺序也是全局唯一的,同时决定了执行顺序。etcd为此也提供了一套API(自动创建有序键),对一个目录键值时指定为POST动作,这样etcd会自动在目录下生成一个当前最大的值为键,存储这个新的值(客户端编号)。同时还可以使用API顺序列出所有当前目录下的键值。此时这些键值的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号。

etcd架构:
在这里插入图片描述

etcd的简单使用:

etcd常用命令

put

etcdctl--endpoints=http://127.0.0.1:2379 put zyj"dsb"
//endpoints指定连接哪一台etcd,put要设置值

get

etcdctl--endpoints=http://127.0.0.1:2379 get zyj
#PUT设置或者更新某个键的值
etcdctl put /test/name fox

#GET获取指定键的值
etcdctl get /test/name
#以16进制格式返回
etcdctl get /test/name --hex
#获取key范围内的值半开区间: 左闭右开
etcdctl get /test/name /test/name3
#获取指定前缀的值 可以使用选项 --limit 限制返回的key数量
etcdctl get --prefix /test
etcdctl get --prefix /test --limit 3
# 按key的字典顺序读取
#读取字典顺序大于或等于 name2 的key:
etcdctl get --from-key name2
#etcd可通过读取一个key来获取当前etcd服务端的版本号,不管key是否存在
etcdctl get /name -w=json
#访问以前版本的key
#cluster_id: 请求的etcd集群ID。
#member_id: 请求的etcd节点ID。
#revision: etcd服务端当前全局数据版本号。对任一key的put或delete操作都会使revision自增1。revision=1是etcd的保留版本号,因此用户的key版本号将从2开始raft term: etcd当前raft主节点任期号。
#create_revision: 当前key创建时全局数据版本号revision的值。
#mod_revision: 当前key最后一次修改时全局数据版本号revision的值。
#version: 当前key的数据版本号。key创建时version为1,对当前key进行put操作会使version自增1,将key删除后,重新创建,version又会从1开始计数
etcdctl get --rev=8 /test/name #访问第8个版本的key

#删除 --prev-kv返回删除的键值
etcdctl del /name3 --prev-kv 

#watch检测一个键值的变化,一旦键值发生更新,就会输出最新的值并退出
etcdctl watch /test/name

#租约,类似于redis的TTL,etcd中的键值对可以绑定到租约上,实现存活周期控制。
#应用客户端可以为etcd集群里面的键授予租约,一旦租约TTL到期,租约就会过期并且所有附带的键都将被删除
#授权租约TTL为30s,返回一个租约的时间信息705bafafgasg 
etcdctl lease grant 30
#附加键到租约
etcdctl put --lease=705bafafgasg name fox
#撤销租约,将删除所有附带的key
etcdctl lease revoke 705bafafgasg 
#查看租约是否存活
etcdctl lease keep-alive 705bafafgasg 
#查询租约
etcdctl lease timetolive 705b84afaa8asfs0

权限管理:


#创建用户
etcdctl user add fox
#删除用户
etcdctl user del fox
#修改密码
etcdctl user passwd fox
#查看所有用户
etcdctl user list

#创建角色
etcdctl role add test
#给角色赋予权限[read|write|readwrite]
etcdctl role grant-permission test2 readwrite /name
#回收校色权限
etcdcctl role revoke-permission role_name /name
#给用户关联角色
etcdctl user grant-role fox test2

#查看指定用户及绑定角色
etcdctl user get fox

#开启权限认证
etcdctl auth enable

#以某个用户进行操作
etcdctl put /name fox --user=root

etcd启动命令的其它参数:

1.--name:给etcd取的名字
2.--listen-client-urls:客户端通信的url
3.--advertise-client-urls:建议使用的客户端通信的url
4.--listen-peer-urls:监听用于节点通信的url
5.--inital-advertise-peer-urls:建议用于节点通信的url
6.--inital-cluster-token:节点的token的值,集群生成唯一id
7.--inital-cluster:集群当中所有inital-advertise-peer合集
8.--inital-cluster-state:集群的状态信息

在这里插入图片描述

etcd读写请求执行流程

读过程:
在这里插入图片描述

客户端:
1.首先,etcdctl会对命令中的参数进行解析。

“get”是请求的方法,它是 KVServer 模块的 API;
“hello”是我们查询的 key 名;
“endpoints”是我们后端的 etcd 地址。

2.在解析完请求中的参数后,etcdctl会创建一个clientv3库对象,按照负载均衡策略选取一个合适的etcd节点,接着调用 KVServer 模块对应的 RPC 接口,发起 Range 请求的 gRPC 远程调用;

etcd clientv3库采用的负载均衡算法为Round-robin。针对每一个请求,Round-robin算法通过轮询的方式依次从
endpoint列表中选择一个endpoint访问,使etcd server负责尽量均衡

3.gRPC Server上注册的拦截器拦截到Range请求后,实现 Metrics 统计、日志记录等功能;

etcd 通过拦截器以非侵入式的方式实现了许多特性,例如: 丰富的 metrics、日志、请求行为检查所有请求的执行耗时及错误码、来源 IP、限流、验证等。拦截器提供了在执行一个请求前后的 hook 能力,除了debug 日志、metrics 统计、对 etcd Learner 节点请求接口和参数限制等能力,etcd 还基于它实现了以下特性:

1.要求执行一个操作前集群必须有 Leader;
2.请求延时超过指定阈值的,打印包含来源IP 的慢查询日志(3.5 版本)。

server 收到 client 的 Range RPC 请求后,根据 ServiceName 和 RPC Method 将请求转发到对应的handler 实现,handler 首先会将上面描述的一系列拦截器串联成一个拦截器再执行,在拦截器逻辑中,通过调用 KVServer 模块的 Range 接口获取数据和对数据进行校验和监控。

4.串行读与线性读:etcd为了保证服务高可用,生产环境一般部署多个节点,多节点之间的数据由于延迟等关系可能会存在不一致的情况。

在多节点etcd集群中,各个节点的状态机数据一致性存在差异。而我们不同业务场景的读请求对数据是否最新的容忍度是不一样的,有的场景它可以容忍数据落后几秒甚至几分钟,有的场景要求必须读到反映集群共识的最新数据。根据业务场景对数据一致性差异的接受程度,etcd 中有两种读模式。

1)串行(Serializable)读: 直接读状态机数据返回、无需通过 Raft 协议与集群进行交互,它具有低延时、高吞吐量的特
点,适合对数据一致性要求不高的场景。
2)线性读ReadIndex: etcd默认读模式是线性读,需要经过 Raft 协议模块,反应的是集群共识,因此在延时和吞吐量
上相比串行读略差一点,适用于对数据一致性要求高的场景(读一个地址可能会重定向到另外一个地址)
	当收到一个线性读请求时,它首先会从Leader获取集群最新的已提交的日志索引
	Leader收到ReadIndex请求时,为防止脑裂等异常场景,会向Follower节点发送心跳确认,一半以上节点确认
	Leader身份后才能将已提交的索引(committed index)返回给节点被请求的节点
	节点则会等待,直到状态机已应用索引(applied index)大于等于Leader的已提交索引时,然后去通知读请求,数据
	已赶上Leader,你可以去状态机中访问数据了

5.接着就是调用 MVCC 模块,根据 treeIndex 模块 B-tree 快速查找 key 对应的版本号
6.通过获取的版本号作为 key,查询存储在 boltdb 中的键值对;

写流程:
1.首先,etcdctl会对命令中的参数进行解析。

2.在解析完请求中的参数后,etcdctl会创建一个clientv3库对象,选取一个合适的etcd节点,接着调用 KVServer 模块对应的 RPC 接口,发起 Range 请求的 gRPC 远程调用;

3.etcd Server收到客户端请求,经过gRPC拦截、Quota校检、日志写入,Quota 模块用于校验 etcd db 文件大小是否超过了配额;

4.接着 KVServer 模块将请求发送给本模块中的 raft,这里负责与 etcd raft 模块进行通信,发起一个提案,命令为 put foo bar,即使用 put 方法将 foo 更新为 bar;

5.提案经过转发之后,半数节点成功持久化

当client发起一个写请求后分为以下几个步骤:

1.Leader收到写请求,它会将此请求持久化到WAL日志,并广播给各个节点
2.若一半以上的节点持久化成功,则该请求对应的日志条目被标记为已提交
3.etcdserver模块异步从Raft模块获取已提交的日志条目,应用到状态机(boltdb等)

6.MVCC 模块更新状态机

MVCC

MVCC是为了解决etcd v2不支持保存key的历史版本、不支持保存key的历史版本、不支持多key事务等问题而产生的。它核心是由内存树型索引模块和嵌入式的KV持久化存储数据库boltdb组成。boltdb是个基于B+tree实现的key-value键值库,支持事务,提供Get/Put等简易API给etcd操作。

etcd MVCC具体方案如下:

1.每次修改操作,生成一个新的版本号,以版本号为key,value为用户key-value等信息组成的结构体存储到blotdb。
2.读取时先从treeIndex中获取key的版本号,再以版本号作为boltdb的key,从boltdb中获取其value信息

在这里插入图片描述
6.boltdb
若 buffer 未命中,此时就真正需要向 boltdb 模块查询数据了。boltdb 使用 B+ tree 来组织用户的 key-value 数据,获取 bucket key 对象后,通过 boltdb 的游标 Cursor 可快速在 B+ tree 找到 key hello 对应的 value 数据,返回给 client。

etcd的持久化

etcd目前支持V2和V3两个大版本,这两个版本在实现上有比较大的不同,一方面是对外提供接口的方式,另一方面就是底层的存储引擎,V2版本的实例是一个纯内存的实现,所有的数据都没有存储在磁盘上,而V3版本的实例就支持了数据的持久化。

数据所在的目录,会被分为两个文件夹中,分别为snap和wal目录:

snap:

1.存放快照数据,存储etcd的数据状态
2.etcd防止WAL文件过多而设置的快照

wal:

1.存放预写式日志
2.最大的作用是记录了整个数据变化的全部历程
3.在etcd中,所有数据的修改在提交前都要先写入到WAL中

使用 WAL 进行数据的存储使得 etcd 拥有两个重要功能,那就是故障快速恢复和数据回滚/重做。

故障快速恢复就是当你的数据遭到破坏时,就可以通过执行所有 WAL 中记录的修改操作,快速从最原始的数据恢复
到数据损坏前的状态。
数据回滚重做就是因为所有的修改操作都被记录在 WAL 中,需要回滚或重做,只需要方向或正向执行日志中的操作
即可。

随着使用量的增加,WAL 存储的数据会暴增。为了防止磁盘很快就爆满,etcd 默认每 10000 条记录做一次 snapshot 操作,经过 snapshot 以后的 WAL 文件就可以删除。而通过 API 可以查询的历史 etcd 操作默认为 1000 条。

golang操作etcd

etcd库包的安装

go get go.etcd.io/etcd/clientv3

etcd的简单实例:

put和get操作

package main

import (
	"context"
	"fmt"
	"time"

	"go.etcd.io/etcd/clientv3"
)

func main() {
	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   []string{"127.0.0.1:2379"}, //节点
		DialTimeout: 5 * time.Second,            //超过5秒钟连不上超时
	})
	if err != nil {
		fmt.Println("connect to etcd failed:", err)
		return
	}
	fmt.Println("connect to etcd success")
	defer cli.Close()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	_, err = cli.Put(ctx, "zyj", "dsb")
	cancel()
	if err != nil {
		fmt.Println("put to etcd failed,err:", err)
		return
	}
	ctx, cancel = context.WithTimeout(context.Background(), time.Second)
	resp, err := cli.Get(ctx, "zyj") //resp是相应对象
	cancel()
	if err != nil {
		fmt.Println("get from etcd failed:", err)
		return
	}
	for _, ev := range resp.Kvs { //Kvs是响应对象的多个键值对
		fmt.Printf("%s:%s\n", ev.Key, ev.Value)
	}
}

watch操作

package main

import (
	"context"
	"fmt"
	"time"

	"go.etcd.io/etcd/clientv3"
)

func main() {
	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   []string{"127.0.0.1:2379"},
		DialTimeout: 5 * time.Second,
	})
	if err != nil {
		fmt.Println("connect to etcd success")
		return
	}
	fmt.Println("connect to etcd success")
	defer cli.Close()
	//派一个哨兵 一直监视着zyj的变化(新增,修改,删除)
	ch := cli.Watch(context.Background(), "zyj")
	//从通道中尝试取值(监视的信息)
	for wresp := range ch {
		for _, evt := range wresp.Events {
			fmt.Printf("Type:%v key:%v value:%v\n", evt.Type, string(evt.Kv.Key), evt.Kv.Value)
		}
	}
}

以静态方式启动etcd集群:

使用的工具为goreman

1.goreman的下载:

go install github.com/mattn/goreman
goreman help

2.编写local-cluster-profile文件

etcd1: etcd --name infra1 --listen-client-urls http://127.0.0.1:12379 --advertise-client-urls=http://127.0.0.1:12379 ……
etcd2: etcd --name infra2 --listen-client-urls http://127.0.0.1:22379 --advertise-client-urls=http://127.0.0.1:22379 ……
etcd3: etcd --name infra3 --listen-client-urls http://127.0.0.1:32379 --advertise-client-urls=http://127.0.0.1:32379 ……

3.使用goreman命令启动etcd集群

goreman -f local-cluster-profile start

4.访问任意一个etcd server获取集群member信息

etcdctl --endpoints=localhost:12379 member list

关闭某个节点:

goreman run stop etcd2

重启某个节点:

goreman run restart etcd2

5.向etcd集群写入数据

# 通过--endpoint参数来选择etcd2
etcdctl put user fox --endpoints http://localhost:22379

利用docker部署etcd集群命令:

docker network create --driver bridge --subnet=10.20.36.0/16 --gateway=10.20.1.1 mynet2
# node1
docker run -p 2379:2379 -p 2380:2380 --name etcd-node-1 \
  --volume=/var/lib/etcd:/etcd-data \
  quay.io/coreos/etcd:latest \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data \
  --initial-advertise-peer-urls "http://10.20.30.1:2380" \
  --listen-peer-urls "http://0.0.0.0:2380" \
  --advertise-client-urls "http://10.20.30.1:2379" \
  --listen-client-urls "http://0.0.0.0:2379" \
  --initial-cluster "etcd-node-1=http://10.20.30.1:2380, etcd-node-2=http://10.20.30.2:2380, etcd-node-3=http://10.20.30.3:2380" \
  --initial-cluster-state "new" \
  --initial-cluster-token "my-etcd-token"

# node2
docker run -p 2379:2379 -p 2380:2380 --name etcd-node-2 \
  --volume=/var/lib/etcd:/etcd-data \
  quay.io/coreos/etcd:latest \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data \
  --initial-advertise-peer-urls "http://10.20.30.2:2380" \
  --listen-peer-urls "http://0.0.0.0:2380" \
  --advertise-client-urls "http://10.20.30.2:2379" \
  --listen-client-urls "http://0.0.0.0:2379" \
  --initial-cluster "etcd-node-1=http://10.20.30.1:2380, etcd-node-2=http://10.20.30.2:2380, etcd-node-3=http://10.20.30.3:2380" \
  --initial-cluster-state "new" \
  --initial-cluster-token "my-etcd-token"

# node3
docker run -p 2379:2379 -p 2380:2380 --name etcd-node-3 \
  --volume=/var/lib/etcd:/etcd-data \
  quay.io/coreos/etcd:latest \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data \
  --initial-advertise-peer-urls "http://10.20.30.3:2380" \
  --listen-peer-urls "http://0.0.0.0:2380" \
  --advertise-client-urls "http://10.20.30.3:2379" \
  --listen-client-urls "http://0.0.0.0:2379" \
  --initial-cluster "etcd-node-1=http://10.20.30.1:2380, etcd-node-2=http://10.20.30.2:2380, etcd-node-3=http://10.20.30.3:2380" \
  --initial-cluster-state "new" \
  --initial-cluster-token "my-etcd-token"

编写docker-compose.yml文件

version: "3.6"

services:
  node1:
    image: quay.io/coreos/etcd
    volumes:
      - node1-data:/etcd-data
    expose:
      - 2379
      - 2380
    networks:

    environment:
      - ETCDCTL_API=3
    command:
      - /usr/local/bin/etcd
      - --data-dir=/etcd-data
      - --name
      - node1
      - --initial-advertise-peer-urls
      - http://172.16.238.100:2380
      - --listen-peer-urls
      - http://0.0.0.0:2380
      - --advertise-client-urls
      - http://172.16.238.100:2379
      - --listen-client-urls
      - http://0.0.0.0:2379
      - --initial-cluster
      - node1=http://172.16.238.100:2380,node2=http://172.16.238.101:2380,node3=http://172.16.238.102:2380
      - --initial-cluster-state
      - new
      - --initial-cluster-token
      - docker-etcd

  node2:
    image: quay.io/coreos/etcd
    volumes:
      - node2-data:/etcd-data
    networks:
      cluster_net:
        ipv4_address: 172.16.238.101
    environment:
      - ETCDCTL_API=3
    expose:
      - 2379
      - 2380
    command:
      - /usr/local/bin/etcd
      - --data-dir=/etcd-data
      - --name
      - node2
      - --initial-advertise-peer-urls
      - http://172.16.238.101:2380
      - --listen-peer-urls
      - http://0.0.0.0:2380
      - --advertise-client-urls
      - http://172.16.238.101:2379
      - --listen-client-urls
      - http://0.0.0.0:2379
      - --initial-cluster
      - node1=http://172.16.238.100:2380,node2=http://172.16.238.101:2380,node3=http://172.16.238.102:2380
      - --initial-cluster-state
      - new
      - --initial-cluster-token
      - docker-etcd

  node3:
    image: quay.io/coreos/etcd
    volumes:
      - node3-data:/etcd-data
    networks:
      cluster_net:
        ipv4_address: 172.16.238.102
    environment:
      - ETCDCTL_API=3
    expose:
      - 2379
      - 2380
    command:
      - /usr/local/bin/etcd
      - --data-dir=/etcd-data
      - --name
      - node3
      - --initial-advertise-peer-urls
      - http://172.16.238.102:2380
      - --listen-peer-urls
      - http://0.0.0.0:2380
      - --advertise-client-urls
      - http://172.16.238.102:2379
      - --listen-client-urls
      - http://0.0.0.0:2379
      - --initial-cluster
      - node1=http://172.16.238.100:2380,node2=http://172.16.238.101:2380,node3=http://172.16.238.102:2380
      - --initial-cluster-state
      - new
      - --initial-cluster-token
      - docker-etcd

volumes:
  node1-data:
  node2-data:
  node3-data:

networks:
  cluster_net:
    driver: bridge
    ipam:
      driver: default
      config:
      -
        subnet: 172.16.238.0/24

以动态发现启动etcd集群:

discovery service:即服务发现,帮助新的etcd成员使用共享URL在集群引导阶段发现所有其他成员

所有新成员都与发现服务交互并帮助生成预期的成员列表

每个新成员使用此列表引导其服务器

该列表执行与–inital-cluster标志相同的功能
即设置所有集群的成员信息

实现机制:Discovery Service Protocol 可以帮助新的Etcd成员使用共享URL在集群引导阶段发现所有其他成员。该协议使用新的发现令牌来引导一个唯一的etcd集群,一个发现令牌只能代表一个etcd集群,只要此令牌上的发现协议启动,即使它中途失败,也不能用于引导另一个etcd集群。

操作步骤:

1.环境准备:所有节点均需要安装etcd

#创建etcd日志保存目录
mkdir -p /var/log/etcd
#创建单独的etcd数据目录
mkdir -p /data/etcd

2.创建集群发现:

#使用公共etcd发现服务,返回一个url地址
curl https://discovery.etcd.io/new?size=3
#生成的url
https://discovery.etcd.io/qr2yr2jhr23iu5r23refsf

size为集群节点数量,若未指定数量,则默认为3

3.启动集群
每个成员必须指定不同的名称标志,否则发现将因重复的名称而失败

etcd --name etcd1 --data-dir /data/etcd --initial-advertise-peer-urls http://192.168.65.206:2380 --listen-peer-urls http://192.168.65.206:2380 --listen-client-urls http://192.168.65.206:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.65.206:2379 --discovery https://discovery.etcd.io/qr2yr2jhr23iu5r23refsf

在启动新的etcd时,加一个参数–advertise-client-urls就能发现加入集群中

4.集群检测

etcdctl member list

面试题

etcd和redis比较

etcd是一种分布式存储,强调的是各个节点之间的通信,同步,确保各个节点上数据和事务的一致性,使得服务发现
工作更稳定,本身单节点的写入能力并不强。etcd采用的是强一致性raft算法。

redis更像是内存型缓存,虽然也有cluster做主从同步和读写分离,但节点间的一致性主要强调的是数据,并不在乎事
务,因此读写能力很强,提供数据存储的类型也很多。redis是弱一致性,可能会丢失数据,或者读取不到最新数据。
redis的数据变化监听机制没有etcd完善

Raft中的一个任期是什么意思?

在Raft一致性算法中,Term(任期)是一个用于对事物进行顺序化的概念。每个任期都有一个唯一的正整数标识,用
于区分其他任期。Raft算法中的时间被分为一个个的任期,每个任期由一名领导者主导。

Raft 状态机是怎样切换的?

1.Leader选举:当一个Raft集群启动时,最开始没有Leader。集群中的每个节点都会发送选举请求(请求投票)来参与
	Leader选举。一个节点可以变为Candidate角色,并开始选举过程。
2.Candidate角色:Candidate角色执行以下几个步骤:
	a. 发起选举请求,并向其他节点请求投票。
	b. 如果收到大多数节点的投票赞成,则该Candidate节点成为新的Leader,并进入Leader角色。
	c. 如果在选举超时时间内没有获得足够的投票,会重新发起新一轮的选举。
3.Leader角色:一旦一个节点成为Leader,它会执行以下几个步骤:
	a. 接收来自客户端的日志操作请求,并将其复制到集群中的其他节点。
	b. 向其他节点发送心跳信号以保持其Leader地位。
	c. 在收到大多数节点响应的确认日志条目后,将其应用到自己的状态机,并向客户端返回响应。
4.Follower角色:在Leader选举过程中,未被选为Leader的节点会变为Follower角色。Follower角色执行以下几个主
要步骤:
	a. 接收来自Leader的心跳信号,以保持同步。
	b. 响应Leader的日志复制请求。
	c. 如果在选举超时时间内未收到来自Leader的心跳信号,该节点会变为Candidate角色,并重新参与Leader选举。

如何保证最短时间内竞选出 Leader,防止竞选冲突?

1.初始状态:系统中的每个节点都是候选者(Candidate)状态。
2.发起选举:当一个节点成为候选者后,它会增加当前任期号(term)并发起选举请求。同时,它会将自己的信息
(包括任期号、候选者ID等)广播给其他节点。
3.响应选举请求:其他节点收到选举请求后,首先检查请求中的任期号是否比自己的当前任期号大。如果是,则更新
自己的任期号为更大的值,清空投票给其他候选者的状态,并回复该请求。如果不是,则拒绝该请求。
4.支持候选者:节点在回复选举请求时,会将自己的投票给候选者,并重置选举超时定时器。

如何防止别的 Candidate 在遗漏部分数据的情况下发起投票成为 Leader?

1.预写日志在经过超过半数节点的回复后才会被提交
2.每个节点只会给日志比自己新的candidate投票
3.candidate只有在超过半数节点的投票后才可能成为leader

Raft 某个节点宕机后会如何?

1.首先,其他正常工作的节点会检测到宕机节点的心跳超时,将其标记为不可用。
2.集群中的 Leader 节点将会开始选举新的 Leader
3.新的 Leader 被选举后,它会通知其他节点,并开始复制日志。这确保了在宕机期间被认为是已提交的日志能够被
正确地复制到其他节点。
4.当宕机节点重新加入集群时,它会将自身的日志与 Leader 节点进行同步。Leader 会确定宕机节点的日志和自己的
日志之间的差异,并将缺失的日志发送给宕机节点。

为什么 Raft 算法在确定可用节点数量时不需要考虑拜占庭将军问题?

拜占庭将军问题是指在一个分布式系统中,存在有恶意的节点(拜占庭将军)向其他节点发送错误的或者虚假的信
息,从而干扰分布式系统的正常运行。

raft算法的设计目标之一是能够容忍一定数量的节点失效或变得不可信。具体来说,Raft算法要求集群中的大多数节
点是可用的,只有不能超过半数的节点失效或变得不可信。当超过半数节点失效或变得不可信时,Raft算法会进入暂	
停状态等待恢复。

如何判断写入是否成功?

1.客户端发送写入请求给Leader节点。
2.Leader节点将请求作为一个日志条目追加到自己的日志中,并发送AppendEntries RPC给其他节点,请求它们复制
该日志条目。
3.当Leader节点收到大多数节点的确认后,判定写入成功。
4.Leader节点将该日志条目应用到状态机,并将结果返回给客户端。

WAL具体的结构?
etcd的WAL(Write-Ahead Log)是一种持久化的日志记录方式,用于在节点发生故障等情况下保护数据的完整性。WAL是etcd的核心组件之一,用于记录集群中的所有写入操作。

WAL的具体结构如下:

1.日志条目(Entry):WAL基于日志条目进行写入和恢复操作。每个日志条目由索引、任期号和具体数据组成。索
引用于唯一标识日志条目,任期号用于区分不同的任期,数据存储了具体的操作内容。
2.任期(Term):集群中的每个任期会对应一个领导者(Leader),领导者负责接收客户端请求并将其复制到集群
中的其他节点。任期通过任期号进行标识。
3.快照(Snapshot):WAL还可以通过快照来减少存储空间的使用。快照是etcd当前数据的一个副本,它只包含最新
的一次快照和其后的所有日志条目。

WAL的写入流程如下:

1.客户端发起写入请求
2.领导者接收请求,将操作转化为日志条目
3.领导者将日志条目写入WAL,同时将其复制给其他节点
4.其他节点接收并写入WAL,确认写入成功后返回响应给领导者
5.领导者将接收到的响应确认给客户端

WAL的恢复流程如下:

1.节点启动时加载WAL,并验证其中的日志条目
2.如果存在快照,将其应用到当前数据状态
3.根据WAL中的日志条目逐个应用到当前数据状态,保证数据的一致性

etcd进行服务注册与发现:

etcd 基于 Raft 算法,能够有力地保证分布式场景中的一致性。各个服务启动时注册到 etcd 上,同时为这些服务配置
键的 TTL 时间。注册到 etcd 上面的各个服务实例通过心跳的方式定期续租,实现服务实例的状态监控。

etcd作为服务注册发现的优势在于:
	1.简单易用,不需要集成sdk
	2.可配置性强

etcd的可配置性强体现在

1.数据模型的灵活性:etcd允许用户以键值对的形式存储和检索数据。用户可以自由定义键和值的内容,从而灵活地	
适应不同的业务需求。同时,etcd还支持复杂数据结构的存储,例如列表、映射等,使得数据模型更加丰富。

2.监听和触发机制:etcd支持监听特定键的变化,并通过回调函数触发相应的业务逻辑。这使得用户可以轻松地实现	
服务发现功能。当新的服务加入或退出时,etcd可以及时通知相关服务,使其动态更新服务列表。

3.ACL(访问控制列表)的支持:etcd支持针对不同用户和角色设置访问权限。用户可以通过ACL机制,灵活地控制	
对etcd集群中数据的读写权限。这保证了数据的安全性和可靠性。

4.多种API接口的支持:etcd提供了丰富的API接口,包括HTTP接口、gRPC接口、命令行接口等。用户可以根据自	
身的需求选择适合的接口进行操作,提高了使用的灵活性。
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Golang中调用etcd,可以使用etcd的Go客户端库来实现。以下是一个简单的示例代码,展示如何使用Golangetcd进行交互: 首先,您需要导入etcd的Go客户端库,可以使用以下命令安装: ```shell go get go.etcd.io/etcd/client/v3 ``` 然后,在您的代码中导入所需的包: ```go import ( "context" "fmt" "go.etcd.io/etcd/client/v3" ) ``` 接下来,您可以创建一个etcd客户端来连接到etcd服务器: ```go endpoints := []string{"http://localhost:2379"} // etcd服务器的地址 config := client.Config{ Endpoints: endpoints, DialTimeout: 5 * time.Second, // 连接超时时间 } client, err := client.New(config) if err != nil { // 错误处理 } defer client.Close() ``` 现在,您可以使用客户端进行一些基本的操作,例如设置键值对、获取键值对、删除键等: ```go // 设置键值对 ctx := context.TODO() _, err := client.Put(ctx, "mykey", "myvalue") if err != nil { // 错误处理 } // 获取键值对 resp, err := client.Get(ctx, "mykey") if err != nil { // 错误处理 } for _, kv := range resp.Kvs { fmt.Printf("Key: %s, Value: %s\n", kv.Key, kv.Value) } // 删除键 _, err = client.Delete(ctx, "mykey") if err != nil { // 错误处理 } ``` 这只是一个简单的示例,您可以根据自己的需求使用etcd提供的更多功能和操作。请注意,此示例假定您已经在本地运行了etcd服务器,并将地址设置为`http://localhost:2379`。您需要根据实际情况更改这些配置。 希望这个示例能帮助您开始使用Golangetcd进行交互!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值