前言
etcd本质是一个kv键值数据库,安装完毕后可以使用官方提供的命令行工具etcdctl向etcd服务端执行命令。etcd也开放了http接口给外部,自己可以直接写一个etcd控制台。
这里推荐一个我喜欢的etcd客户端etcd-manager。
etcd-manager
etcd-manager是一款跨平台、开源的etcd管理工具,etcd默认使用etcdctl命令行进行管理。etcd-manager方便运维人员采取UI可视化的方式管理我们的etcd数据库,etcd-manager是一款使用js的Electron技术研发的C/S架构的应用,目前有windows、MacOS、Linux 3个客户端。
etcd-manager下载
etcd-manager官网:https://etcdmanager.io/
因为众所周知的原因可能会访问受限,科学上网者除外。这里示例选择Windows客户端,在官网找到Installation标题,选择Windows链接即可下载。
etcd-manager安装
Windows的安装就非常简单了,选择安装的对象是当前用户还是所有Windows用户,再选择安装路径即可。
etcd-manager使用
使用的话超级简单,前提服务端的etcd.conf配置文件内必须将ETCD_LISTEN_CLIENT_URLS的值设定为http://0.0.0.0:2379,方便外网连接。
etcd-manager客户端布局一目了然,左侧为菜单栏,菜单栏解释如下:
菜单名称 | 说明 |
Settings | 设置ETCD的连接信息,以及其他一些配置,如监视ETCD的错误、认证、密码模式、监视watcher重新连接和断开连接等等... |
Manage Cluster | 集群管理,此菜单将展示连接的ETCD服务端对应的集群信息,可对集群下各节点进行健康检查(发送心跳包检测)。 |
Manager keys | 管理键值对,可以使用列表方式与树状结构查看,树状结构更加直观。 |
Manager watchers | 观察者管理,可以直接在此菜单内对指定的Key执行watch。 |
Manager users | 用户管理, 可以对用户进行增删改查操作。 |
Manager leases | 租约管理, 可以Key的租约详情。 |
Manager roles | 角色管理, 可以对ETCD的角色执行增删改查。 |
About | 自述信息。 |
连接创建成功后会在本地存储一份配置文件,etcd-manager通过加载该配置文件(Profile)从而读取该配置文件对应的ETCD服务端信息。通过保存多个配置文件可以在etcd-manager里对不同服务端执行管理。
下图是Settings菜单展示页
通过使用Manager keys菜单的功能可以加深对ETCD存储结构的理解,我们得知在ETCD里写入一个KEY就好比在操作系统上存储一个文件,Key是目录,value则是文件。
同理我们获取KEY也可以根据部分目录获取,比如存在两个KEY:api/test/aaa、api/test/bbb。我们可以通过ETCD的关键字--prefix指定前缀方式一次获取多个相同前缀的KEY信息。
在服务器端使用etcdctl命令行中输入下面内容:
etcdctl put api/test/aaa "http://127.0.0.1:8001"
etcdctl put api/test/bbb "http://127.0.0.1:8002"
etcdctl get --prefix api/test
执行后会输出api/test/aaa、api/test/bbb两个KEY以及各自的VALUE。我们的服务发现就是通过这种方式,指定同一个服务名设定不同的服务器地址,服务名称即前缀信息,对应上的api/test,服务地址则对应上面的aaa和bbb。通常value会存储服务的一些事件性属性值,比如注册时间、版本、续约时常等等~
ETCD代码实践
这里使用C#控制台方式对ETCD服务端进行KEY读取、写入以及Watch。
新增一个EtcdClientConsoleApp控制台应用,需要依赖dotnet-etcd这个nuget包,
再创建一个AppEtcdManaer类,用于封装ETCD操作。
public EtcdClient ConnetcionEtccServer(string connectionString, int port = 2379)
{
EtcdClient etcdClient = new EtcdClient(connectionString, port);
return etcdClient;
}
写入Key
public async Task SetKey(EtcdClient etcdClient, string key, string value)
{
await etcdClient.PutAsync(key, value);
}
获取Key的Value
public async Task<string> GetValue(EtcdClient etcdClient, string key)
{
return await etcdClient.GetValAsync(key);
}
上面代码非常简单,只是对key的写入和拉取,接下来再添加对Key的Watch。ETCD里面的Watch是采取消消息订阅方式,我们客户端需要有一个回调函数进行接收Key的更新或者删除事件。这里新增一个回调方法,将收到的回调信息打印到控制台。
private void print(RepeatedField<Mvccpb.Event> response)
{
for (int i = 0; i < response.Count; i++)
{
switch (response[i].Type)
{
case Mvccpb.Event.Types.EventType.Put: / 目录下新增或修改Key
Console.WriteLine($"新增的Key={response[i].Kv.Key.ToStringUtf8()},Value={response[i].Kv.Value.ToStringUtf8()},最后修改的版本(ModRevision)={response[i].Kv.ModRevision}");
break;
case Mvccpb.Event.Types.EventType.Delete: //目录下移除Key
Console.WriteLine($"移除的Key={response[i].Kv.Key.ToStringUtf8()},上一次最后修改的版本(ModRevision)={response[i].Kv.ModRevision}");
break;
}
}
}
private void print(WatchResponse response)
{
if (response.Events.Count == 0)
{
Console.WriteLine(response);
}
else
{
print(response.Events);
}
}
// Watch Key
public void WatchKey(EtcdClient etcdClient, string key)
{
etcdClient.Watch(key, print);
}
我们在应用写入一个key为configTest,值为configTest,然后再到etcd-manager应用上查看,如下图:
需要特别注意我画上箭头的地方,上图表格中的Previous value、Version、Create Rev、Mod Rev都会随着每次对Key的变更而变更。Type则只会是Put或Delete。是的,哪怕这个Key被删除了,当你再次写入相同的Key后ETCD依旧会将你的行为记录,可以根据Create Rev追踪操作顺序。嗯,还是用一个表格描述下这些字段代表的含义吧。
字段 | 说明 |
Previous value | Key的上一次操作时记录的值,如首次操作则与Value一致。 |
Version | Key每变更一次则该值递增(针对当前Key),如对Key执行了删除则此值会归零。 |
Type | Type是一个枚举,在etcd对外提供的grpc契约文件kv.proto中可以得知,Type是记录Key的事件标识,分别有PUT、DELETE。分别代表写入与删除。 |
Create Rev | 记录当前Key创建时的版本号(针对当前Key),此版本号与Version字段没关系。 |
Mod Rev | 记录当前Key变更时的版本号(针对当前Key),此版本号与Version字段没关系,与Create Rev相关联。 |
我们可以将ETCD的Watch特性应用到分布式配置中心上,那如果要应用到服务注册与发现上则需要使用到WatchRange这个API。
WatchRange允许我们对Key的前缀进行Watch, 通过这个特性可以对前缀相同的Key,如下面是提供一个相同服务的三个实例,分别代表同一前缀的三个key。通过`WatchRange("/business-system/business-model/v1/")`可以完成对该服务的所有实例进行监控,及时获取最新的服务实例信息,ETCD就是通过该特性得以为成千上万的应用提供了服务发现与注册功能。
/business-system/business-model/v1/192.168.8.11:8001
/business-system/business-model/v1/192.168.8.12:8001
/business-system/business-model/v1/192.168.8.13:8001
附上WatchRange的代码:
public async Task WatchRangeByPath(EtcdClient etcdClient, string path)
{
await Task.Factory.StartNew(() => etcdClient.WatchRange(path, print), TaskCreationOptions.LongRunning);
}